Using C++ in a web app with WebAssembly
By Stefan Verhoeven, Faruk Diblen, Jurriaan H. Spaaks, Adam Belloum, and Christiaan Meijer.
Let’s say you have some C++ code laying around that you would like to make available to a wider audience, by putting it on the web as a ready to use web tool. Until recently, this used to be pretty difficult, and may even have required reimplementation of the software in JavaScript, the programming language that browsers use.
Wouldn’t it be great if you could run your existing C++ code on the web with only minor effort?
That way, loads more people would be able to see your results, interact with your algorithm, and apply it for their own purposes.
In this blog, we’ll show you how to take a simple algorithm written in C++ and make it available as a web application. Subsequent blogs in this series will expand on the current one by laying out more advanced topics, specifically how to make the app interactive, how to visualize the results, and how to deal with long running tasks.
Root finding
So today’s aim is to have a simple web app that determines the root of a mathematical function 2x³ — 4x² + 6, i.e. the value of x where y = 0.
For this, we’ll use an iterative method known as the Newton-Raphson root finding method. Remember Newton? Quiet fellow, fabulous hair? Yes, that Newton. The way Newton-Raphson works is, you give it the equation whose root you want to find, along with the derivative of that equation. Then you take an initial_guess
of what you think the value of the root could be, then let the method iterate towards the solution. The solution is approximate within a tolerance
, which you can also set. Anyway, the algorithm is written C++, but with some trickery, we'll be able to use that C++ code from the browser, without the need to port it first!
Now before you say “That’ll be so much slower than running it native!” or “C++ from the browser? Impossible!”, just hold your horses for a sec. With the right tools, it is possible to run C++ code in the browser, with an acceptable performance penalty. For example, Gabriel Cuvillier was able to run the video game Doom 3 in the browser. He was able to do this by compiling the game’s source code into WebAssembly, a low-level language that browsers can run. And if it works for video games, it will likely work for your research software, too.
What we’ll need
OK, now that you’re fully on board with this, let’s get to it. Here’s a list of what we need:
- We are going to write a small HTML page, so you will need basic knowledge of HTML and JavaScript.
- Some C++ code to illustrate the process. We’ll use our Newton-Raphson C++ code.
- A program that can take our existing C++ code and compile it into a WebAssembly module. For this, we’ll use Emscripten’s
emcc
compiler, the most popular C++ to WebAssembly compiler of the bunch. - To use the WebAssembly functionality from JavaScript, a binding is required. The binding will map C++ constructs to their JavaScript equivalent and back. For this, we’ll use embind.
- A web server to serve our files. We’ll use Python 3’s
http.server
, but other web servers work equally well.
Tying it all together
The C++ code
Here is the equation whose root we want to find, along with its derivative, since that’s what Newton-Raphson requires:
The snippet below shows the contents of the file newtonraphson.hpp
. It is the header file for the Newton-Raphson iterative root finding algorithm. It defines a class named NewtonRaphson
. Besides the constructor method NewtonRaphson(float tolerance_in)
, NewtonRaphson
has one other public method, solve
, which takes a float
, and returns another float
. Furthermore, NewtonRaphson
also has a private member, tolerance
of type float
, which is used to store the class instance's private data.
File newtonraphson.cpp
contains the corresponding implementation:
From this definition, NewtonRaphson
instances need to be initialized with a value for tolerance_in
, which is then stored as the private member tolerance
. Once the object instance has been constructed, users can call its solve
method to iteratively find equation
's root, with equation
and its derivative
being imported from problem.hpp
via the include
line near the top.
Check on command line
The following code is a minimal command line program that we can use to check if everything is working correctly:
Our command line program can be compiled with:
g++ -o cli.exe problem.cpp cli.cpp newtonraphson.cpp
Subsequently running it should give the following output:
./cli.exe
The value of the root is : -1.00
Now we’re ready to move on to the WebAssembly part.
Binding
To use the Newton-Raphson code from JavaScript, we’ll need to define the bindings file. The binding allows compiled code to be called from JavaScript. For our Newton-Raphson code, the binding file looks like this:
The binding file uses embind
binding statements to expose the NewtonRaphson
class, its constructor method, as well as its public method solve
.
Compiling to WebAssembly
First we need to download and install Emscripten to get the compiler. The Newton-Raphson source and its binding can be compiled into a WebAssembly module with Emscripten’s emcc
compiler, as follows:
emcc -I. -o newtonraphson.js -Oz -s MODULARIZE=1 \
-s EXPORT_NAME=createModule --bind \
problem.cpp newtonraphson.cpp bindings.cpp
This will generate a WebAssembly module newtonraphson.wasm
, along with a JavaScript file newtonraphson.js
. We also export the createModule
JavaScript function in the compile command so it can be used to load and initialize the WebAssembly module. Using the newtonraphson.js
JavaScript library, we can find the root of the mathematical function, and subsequently display its value with the following HTML:
Hosting the app with a web server
We’ll need a web server to display the HTML page in a web browser. For this, we’ll use the http.server module from Python 3 to host all files in the current directory on port 8000, like so:
# change to directory with index.html and newtonraphson.* files
python3 -m http.server 8000
From the figure at the top of the article, the root of the equation should be at x = -1.00
. Visit http://localhost:8000/ to see if your browser shows the correct result.
Recap
- We wrote a simple algorithm in C++
- We defined the JavaScript interface by writing Emscripten bindings
- We compiled the algorithm and bindings to a WebAssembly module with Emscripten compiler
- We ran the algorithm in a web browser using some JavaScript to talk to the WebAssembly module.
The nice thing about this solution is that we don’t need expensive infrastructure to perform computation as the computation is done in the user’s web browser — we just need somewhere to host the files.
Get in touch with us
This blog was written by the Generalization Team of the Netherlands eScience Center. The team consists of Stefan Verhoeven, Faruk Diblen, Jurriaan H. Spaaks, Adam Belloum and Christiaan Meijer. Feel free to get in touch with the generalization team at generalization@esciencecenter.nl.
Where to go from here?
In upcoming blogs we will cover:
- Help! My C++ web app is not responding: how to perform computations without blocking the user interface.
- Interact with your C++ web app using React forms: how to let the user supply their own input values for
tolerance
andinitial_guess
. - Spice up your C++ web app with visualizations: how to visualize data from the algorithm.
We’ll wrap up the series in a final blog that combines the topics of the whole series in a full-featured web application. If you’re curious what that’s going to look like, make sure to check out the live demo on GitHub Pages.
If you enjoyed this article, leave a comment and give us a clap!
These blogs were written as part of the “Passing XSAMS” project. To learn more about the project, check out its project page. Thank you to our proof readers Jan van Dijk, Daan Boer, Lourens Veen and Patrick Bos.