WebAssembly: Building GUI for C++ libraries with Embind

iprosk

Igor Proskurin

Posted on September 17, 2023

WebAssembly: Building GUI for C++ libraries with Embind

This post will probably not be very long of full of technical details. Instead, I would like to share some thought about using Embind -- a WebAssembly tool for binding high-level C++ code to JavaScript, and an example of an open-source project (that is of personal interest to me) that uses Embind to make GUI for its main product -- a numeric motion planning library. So if you are a C/C++ software/library developer, and you are looking for new tools to visualize, advertise or present you work, WebAssembly is something to thing about...

If you are new to WebAssembly, it is a relatively new web-technology that allows to compile code in languages such as C, C++ and Rust into a low-level assembly language that can be executed in a virtual stack-based machine inside a browser with reasonable productivity (compared to pure JavaSript). There are several resources that provide a good dive into WebAssembly basics:

What should I use it for?

What are benefits of using WebAssebmly with compiled languages like C/C++ or Rust?

  • You can run computationally intensive stuff client-side in browser, and have it on a web page. Think about fractal rendering, 3D graphics, or some physics simulations.
  • If you have library already written in C++ or C, it can be (in most cases) recompiled into WebAssembly bytecode with moderate efforts.

Of course, with benefits come limitations (Dialectics, eh?).

  • There are some limitations with what you can port to WebAssembly. Exceptions? Threads? Fancy memory layout?
  • To use it in a web browser, the code should be compiled with a dedicated compiler (e.g. emcc). It not possible to grab a production library.a compiled for x86-64 and just use it (would be nice, eh?).
  • Some work has to be done to bind data from web application to WebAssembly (read C/C++). Low-level memory management is clumsy, and tools like Embind are real help.

One possible application of WebAssembly is to provide a web access to a (low-level) numeric library API, so that users may turn the knobs in the browser and see what goes on without deploying this library to a hardware of making it a part of larger project. And in this post I will provide and example of a cool project in robotics that uses this approach. Of course, there is always an option to bind such code to Python or Julia which might make more sense in most cases, but sometimes a web app might be an interesting option.

A little bit about Embind

If you would like to jump start with C/C++ in WebAssembly, just install Emscipten SDK. It will install the emcc compiler and some basic environment. If you don't have XTerm/Bash at hand, don't worry -- it works in Windows PowerShell out of the box. Compiling C/C++ with emcc produces *.wasm binary file and optionally JavaScript module to embed *.wasm into a web application. Making it work in React may require a little bit more work. But that's pretty much it.

The problem is how to get something into WebAssembly internal memory space, and how to get stuff out of there. There is always low-level API at our disposal, but this is tedious and error prone, and that's where our today's hero comes in.

Embind. I will not write much about it here, because the official documentation is quite comprehensive to get hands on. Just one remark from the official docs to make this discussion complete. Binding class methods and properties with EMSCRIPTEN_BINDINGS(my_class_example) {class_<MyClass>("MyClass") /* ... */} allows to use it from JavaScript in a "normal" way without worrying about how this works at low level.

var instance = new Module.MyClass(10, "hello");
instance.incrementX();
instance.x; // 11
instance.x = 20; // 20
Module.MyClass.getStringFromInstance(instance); // "hello"
instance.delete();
Enter fullscreen mode Exit fullscreen mode

Let's now look at the example from some real-world project...

Example: GUI for a motion planning library

There is an open-source project which is of personal interest for me -- a robotic motion planning library which is called Ruckig. It is a relatively small C++17 tool which does one thing and does it well -- it plans kinematically-optimal multi-degrees-of-freedom trajectories for robotics controllers. Doesn't matter what it really means. It is a software library that is supposed to be a part of another software tools or libraries. It has Python bindings (of course), and can be used from a Jupyter notebook. Well, as I found recently, it now has WebAssembly bindings too to provide a simple web GUI.

For it is a good chance to peek inside and learn something by example...

Install it for local development and peek inside

Let us quickly look inside Ruckig project. Jump into the GitHub repository of the community version (MIT license) and clone it.

git clone https://github.com/pantor/ruckig
Enter fullscreen mode Exit fullscreen mode

We don't have to build it with CMake, so don't worry about right building tools. It can just live in a Windows folder for now if it is your platform.

If we go to src/ruckig, we will find a file wasm.cpp which contains all the Embind bindings for the web GUI, which allows to seamlessly bind input/output into underlying C++ library.

The web GUI itself is in doc/web-gui. To build it, we need to compile the C++ library to the ruckig.wasm binary first. This can be done by running doc/web-gui/make_wasm.sh in Bash, or if you are in Windows PowerShell, just run the following command in the terminal or put it in a *.bat file (this implies that Emscipten SDK is installed and activated and the environmental variables are already sources into current shell)

emcc src/ruckig/wasm.cpp src/ruckig/brake.cpp src/ruckig/position_first_step1.cpp src/ruckig/position_first_step2.cpp src/ruckig/position_second_step1.cpp src/ruckig/position_second_step2.cpp src/ruckig/position_third_step1.cpp src/ruckig/position_third_step2.cpp src/ruckig/velocity_second_step1.cpp src/ruckig/velocity_second_step2.cpp src/ruckig/velocity_third_step1.cpp src/ruckig/velocity_third_step2.cpp -Iinclude -Ithird_party -std=c++17 -lembind -Os -s MODULARIZE -s EXPORT_ES6 -s EXPORT_NAME='RuckigModule' -s ENVIRONMENT='web' -s EXPORTED_RUNTIME_METHODS=ccall,cwrap -o doc/web-gui/src/ruckig.js
Enter fullscreen mode Exit fullscreen mode

This gives us two files ruckig.wasm binary, and ruckig.js glue code to load this binary into a browser session and interface with a Plotly.js React component, which shows fancy plots on the web page.

If you have resent version of Node.js, you can quickly build you local copy of web GUI by running the following commands in the root directory of the project.

npm install
npm run start
Enter fullscreen mode Exit fullscreen mode

This will serve the page at localhost:1234. Now, make a cup of coffee (if it is still morning) and just look inside the doc/web-gui/ at index.html and gui.js. That's it.

💖 💪 🙅 🚩
iprosk
Igor Proskurin

Posted on September 17, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related