An Introduction to Web Development with Emscripten

Posted August 4, 2015 by Charles Ofria in Software Development / 6 Comments

As a professor, I am always trying to juggle more tasks than I can possibly handle, which means I usually end up focusing my time on “urgent” matters over things I find merely “important” or “satisfying”.  A bit over a year ago I started making sure that I take time to code every day (and am much happier for it).  A sizable majority of this time I’ve devoted to learning and using the Emscripten compiler.

Emscripten compiles C or C++ code into high-efficiency JavaScript (using asm.js) that can run in any web browser, and is accelerated in newer browsers.  How efficient is it?  Well, in practice it tends to be half to two-thirds the speed of running a C++ application natively.  To put that in a more provocative way, it runs 8 to 10 times faster than hand-written Javascript for anything computationally intensive.  Even better, these speeds promise to only increase from there with browser improvements and the advent of Web Assembly.

The Emscripten team has done an amazing job with the compiler, taking full advantage of LLVM, and including many commonly used C libraries.  The main drawbacks are carried over from JavaScript: no access to the local filesystem (though a virtual one is provided) and no access to threads (though work-arounds are in development).  These impressive achievements make it easy to port fully-working C/C++ code to the web.  However, the area that I believe has the most potential is in wrapping popular JavaScript visualization libraries for use with C/C++ scientific software; such a combination would make for a development framework that takes maximal advantage of both worlds.  I have actually started crafting my own development framework (called Empirical) targeted at producing accessible scientific software, but that will be the topic for future posts when things have progressed a bit further.

For now, I want to get others comfortable with uniting C++ and Javascript code for crafting their own projects.

Setting up Emscripten

The Emscripten team provide a useful Software Development Kit that bundles compatible versions of Emscripten and LLVM, and makes it (relatively) easy to get running on your computer.  Download the SDK and follow the instructions provided about your operating system.

Some notes:
Avoid installers like apt-get, brew, and port.  They often update components out of sync, which causes problems.
– Make sure you have Python2 (called ‘python2’) in your path.  You may need to create a sim-link.
– If you are using Linux or Mac, note that the installer assumes bash-style paths.  If you’re not using bash, you may need to fix the paths yourself.
The first time you run the compiler will take longer as it finishes setting up.  Don’t worry, it’ll be much faster in the future.

Below are a set of guides for building your first programs with Emscripten.  Note that all of the examples used here can be found on github: https://github.com/mercere99/EmscriptenExamples

Your First Emscripten Program

Almost any normal C/C++ program should compile directly in Emscripten.  To get started, I recommend the old standby of Hello World:

  #include <iostream>
  int main() {
    std::cout << "Hello World!" << std::endl;
  }

To compile with Emscripten (assuming this code is saved in Example1.cc), use:

  emcc Example1.cc -o Example1.html

Emscripten will examine the extension on the output file to determine what code to produce. In this case, we are asking for an HTML file so it will generate all of the needed HTML code, including a fake terminal with any output and can be opened in any web browser. (Note that if you used cin, a pop-up window would request input from the user). Had we generated a Javascript output file (*.js), the code would be converted to JS, but a manually-generated HTML file would be needed to call any functions — I’ll explain this option below.

Calling JavaScript Code from C/C++

Emscripten comes with a set of macros that allow you to intersperse JavaScript in your C/C++ code.  The simplest of these is called EM_ASM, which allows you to directly execute enclosed code.

  #include <emscripten.h>

  int main()
  {
    EM_ASM({ alert('Hello World!'); });
  }

Similarly to the previous example, compile using:

  emcc Example2.cc -o Example2.html

In this case, instead of using the standard IO Stream mechanism to print “Hello World”, we use the JavaScript alert() function. Note that inside the macro we have to use braces (not just parentheses) to surround the JS code and have it treated like a single argument in the macro. Also note that we used single-quotes around the literal string; technically we are not allowed to use double-quotes in inline JavaScript code, though in practice I’ve never had a problem with it.

Exchanging Data with Embedded JavaScript Code

While EM_ASM allows you to run JS code directly, other macros allow you to pass in parameters and specify return types.

  #include <emscripten.h>
  #include <iostream>

  int main()
  {
    int val1 = 21;
    int val2 = EM_ASM_INT({ return $0 * 2; }, val1);

    std::cout << "val2 == " << val2 << std::endl;
  }

Again, compile using:

  emcc Example3.cc -o Example3.html

In this example, we use EM_ASM_INT to run JavaScript code that accepts arbitrary parameters of type int or double (in this case a single integer val1), runs some JavaScript code (here, doubling the input parameter), and returning the result to val2. Within the JavaScript code, use $0 to represent the first parameter, $1 for the second, and so on. Emscripten will determine if the input parameters are int or double (or char*, but see later) and treat them properly on the JS side. However, the return type needs to be specified in the macro name. The full set of macros includes:

EM_ASM – Call JS code with NO input parameters and NO return values.
EM_ASM_ARGS – Call JS with arbitrary input parameters, but no value is returned.
EM_ASM_INT – Call JS with arbitrary input parameters and return a value of type int.
EM_ASM_DOUBLE – Call JS with arbitrary input parameters and return a value of type double.
EM_ASM_INT_V – Call JS with NO input parameters and return a value of type int.
EM_ASM_DOUBLE_V – Call JS with NO input parameters and return a value of type double.

Passing Strings to JavaScript

JavaScript and C use different mechanisms to store strings, so we need to convert them.  Fortunately, Emscripten provides tools to make this easy.

  #include <emscripten.h>
  #include <string>

  // Function to trigger alerts straight from C++                                                     
  void Alert(const std::string & msg) {
    EM_ASM_ARGS({
        var msg = Pointer_stringify($0); // Convert message to JS string                              
        alert(msg);                      // Use JS version of alert                                   
      }, msg.c_str());
  }

  int main()
  {
    Alert("Hello from C++!");
  }

For a final time, compile using:

  emcc Example4.cc -o Example4.html

This code creates a function that takes an std::string as an input parameter, msg, and presents it to the user as a JS alert. Emscripten provides a function called Pointer_stringify() (found in emscripten.h) that takes a C-style string as a parameter and returns a JS-style string. Note in the code above how we need to run c_str() on msg to convert it from std::string to a char * before passing it through to JS.

Making C/C++ Functions Callable from JavaScript

C is already designed to allow functions to be exported elsewhere, via the extern command. We can use this method to allow C/C++ functions to be called from JavaScript (along with some compilation hints and setup on the JS side).

  #include <emscripten.h>

  extern "C" {
    double SquareVal(double val) {
      return val * val;
    }
  }

  int main() {
    EM_ASM({
        SquareVal = Module.cwrap('SquareVal', 'number', ['number']);
        var x = 12.5;
        alert('Computing: ' + x + ' * ' + x + ' = ' + SquareVal(x));
      });
  }

This time, we need to lists all exported function names during compilation:

  emcc -s EXPORTED_FUNCTIONS="['_SquareVal', '_main']" Example5.cc -o Example5.html

There are three key steps that we need to use here to make a C/C++ function accessible from JavaScript:

  1. Make sure to declare (or define) the function in an extern “C” block. Normally C++ does name mangling on function names, making them effectively inaccessible to external code BUT doing an extern “C” limits the mangling to just prepending an underscore to the function name.
  2. When the C++ code is compiled, include the name of the function in an EXPORTED_FUNCTIONS setting, as above. Normally, main is the only exported function by default, so if you are expecting main to be run (and I haven’t told you how to do anything else yet), make sure to include it in the list. Note that you do need to prepend function names with an underscore here.
  3. You need to extract a C/C++ function from the Module object before you call it in JavaScript, that is automatically generated by Emscripten. To do so, you need to call Module.cwrap(), providing it with three arguments: the name of the C++ function (in quotes), the return type of the function (‘number’, ‘string’), and an array of parameter types for a function (e.g., [‘number’, ‘number’, ‘string’]). You can also use null for the return type if you’re dealing with a void function.

In my example above I nest the JavaScript code into my C++ program, but again, this could have all been in a script in a regular HTML file, which will be more typical, and what we talk about next.

Letting JavaScript Take Charge

All of the programs we have been examining thus far have had a main() function, which is run automatically at the beginning of execution.  If, however, we want a more interactive program, we need to let JavaScript take charge of program control, calling C/C++ functions as needed.  This means we can have a simpler C++ file:

  extern "C" {
    double SquareVal(double val) {
      return val * val;
    }
  }

When we compile the file, we don’t need to worry about exporting main anymore, and want it to produce a raw JavaScript file that we can call:

  emcc -s EXPORTED_FUNCTIONS="['_SquareVal']" Example6.cc -o Example6.js

Of course, now we now need to write an HTML file ourselves. Here’s Example6.html:

  <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
  <body>
  <h1>Test File</h1>

  <script type="text/javascript" src="Example6.js"></script>
  <script>
  SquareVal = Module.cwrap('SquareVal', 'number', ['number']);
  document.write("result == " + SquareVal(10));
  </script>
  </body>

The Module.cwrap is done similarly to the previous example, except this time it’s embedded directly in our html code.

Finally, all we need to do is open Example6.html in our web browser and it will load up the generated Example6.js file, giving us access to the C++ functions defined within at relatively near native C++ speeds.

Have you used Emscripten ever? Let me know if anything is unclear or what you’d like to hear more about!

Charles Ofria

Charles Ofria is a professor of Computer Science at Michigan State University, director of the Devolab, and deputy director of the BEACON Center for the Study of Evolution in Action.

More Posts - Website

6 responses to “An Introduction to Web Development with Emscripten

  1. Sven Kautlenbach

    Definitely an interesting fwk! Makes me want to develop web now 😀

    Is there any examples of how to use C++ objects?

  2. Shahriar Setu

    This is interesting tool i have ever seen. Can we use this any Object Oriented Programming Language Like (Java,C sharp)?

    • Emscripten is a C++ to Javascript compiler, so it can’t compile languages other than C and C++. I don’t know if similar projects exist for languages like Java and C#, but they might.

      • The core framework for Emscripten can actually be used with any LLVM language as it has a generic JavaScript back-end, BUT only C and C++ have had their standard libraries converted to a web environment (to allow effective I/O, for example). Some people are indeed using Emscripten with other languages, but many more components need to be written in JavaScript and hooked in.

Leave a Reply