Getting Started With WebAssembly

Download

WebAssembly is a new technology that makes it possible to run highly performant, low-level code in the browser. It can be used to bring large C and C++ codebases like games, physics engines and even desktop apps to the web platform.

As of right now, WebAssembly can be used in Chrome and Firefox, with Edge and Safari support almost complete as well. This means that very soon, you will be able to run wasm in every popular web browser.

In this lesson, we will be demonstrating how to compile a simple C file down to wasm and include it in a web page.

How Does WebAssembly Work?

First, a quick overview on what WebAssembly actually does. We'll try not to get too technical here.

In browsers today, JavaScript is executed in a virtual machine (VM) which optimizes your code and squeezes every ounce of performance. As things stand, JS is one of the fastest dynamic languages around. Even though it is fast, it still can't compete with raw C and C++ code. This is where WebAssembly comes in.

Wasm runs in the same JavaScript VM, but performs much better. The two can communicate freely and don't exclude each other. This way you can have the best of both worlds - JavaScript's huge ecosystem and friendly syntax, combined with the near-native performance of WebAssembly.

Most people write WebAssembly modules in C and compile them to .wasm files. These wasm files aren't recognized by the browser directly, so they need something called JavaScript glue code to be loaded.

wasm-chain.png

In the future, this process will likely be shortened with WebAssembly frameworks and native wasm module support.

What You'll Need For This Lesson

You need quite a bit of tooling for writing Web Assembly. However, most things on the list are very common and you probably have them available already.

  • Browser with support for WebAssembly, an up-to-date Chrome or Firefox should do the trick (caniuse)
  • C to WebAssembly compiler, we will be using Emscripten portable.
  • A compiler for C (GCC on Linux, Xcode on OS X, Visual Studio for Windows).
  • Simple local web server to run the example (e.g. python -m SimpleHTTPServer 9000)

Installing Emscripten takes a while and is definitely not fun, but it's the most recommended compiler right now. Also, make sure you have enough free hard drive space, it took up nearly 1GB on my machine.

Step 1: Writing C Code

We'll create a super simple C example that picks a random number between 1 and 6. In a working directory of your choice, make a new dice-roll.c file.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <emscripten/emscripten.h>

int main(int argc, char ** argv) {
    printf("WebAssembly module loaded\n");
}

int EMSCRIPTEN_KEEPALIVE roll_dice() {
    srand ( time(NULL) );
    return rand() % 6 + 1;
}

When we compile to wasm and load this code into the browser, the main will be automatically executed. The printf inside of it will be translated into a console.log and called.

We want the roll_dice function to be available in the JavaScript whenever we need it to. To tell the Emscripten compiler our intentions we have to add EMSCRIPTEN_KEEPALIVE.

Step 2: Compile C to WebAssembly

Now that we have our simple c program, we need to compile it into wasm. Not only that, but we also need to generate JavaScript glue code that will help us actually run it.

Here we have to use the Emscripten compiler. There are tons of CLI options available and lots of different approaches. We found the following combination most user-friendly:

emcc dice-roll.c -s WASM=1 -O3 -o index.js

Here is a breakdown of what's happening:

  • emcc - The Emscripten compiler.
  • dice-roll.c - The input file containing our C code.
  • -s WASM=1 - Specify we are working with WebAssembly.
  • -03 - The level of code optimization we want, 3 is pretty high.
  • -o index.js - Tells emcc to generate a JS file with all the needed glue code for our wasm module.

After running the command, in our working directory will be added two files: index.wasm and index.js. They hold the WebAssembly module and its glue code, respectively.

Although these emcc options work well for our example, in more complex situations a different approach might be better. You can read more here.

Step 3: Load WebAssembly Code In The Browser

Now we are in familiar web dev territory. We create a basic index.html file and include the JavaScript glue code in a script tag.

<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>WebAssembly Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>

    <!-- Include the JavaScript glue code. -->
    <!-- This will load the WebAssembly module and run its main. --> 
    <script src="index.js"></script>

  </body>
</html> 

Because of cross-origin issues, to run the project we will need a local web server. On Linux/OS X you can start one by running the following code in the project directory:

python -m SimpleHTTPServer 9000

Now in the browser, go to localhost:9000 to view the app. If you open the browser console, there should be the greeting message we printf-ed in the main of our C code:

wasm-console.png

Step 4: Call WebAssembly Functions

The last step of our experiment is to connect JavaScript and WebAssembly (just like in the wasm logo). This is actually very easy tanks to the JavaScript glue code generated from Emscripten. It handles all the wiring for us.

There is a pretty powerful API for working with WebAssembly in the browser. We won't get into it in detail as it is way beyond the scope of this tutorial. The only parts we will need are the Module interface and its ccall method. This method allows us to call a function from the C code by name, and use it just like a JS one.

var result = Module.ccall(
    'funcName',     // name of C function 
    'number',       // return type
    ['number'],     // argument types
    [42]);          // arguments

After the ccall, in result we will have whatever is returned from the respective C function. All arguments apart from the first one are optional.

A shortened version is also available:

// Call a C function by adding an underscore in front of its name:
var result = _funcName();

Our roll_dice C function doesn't require any parameters. Calling it in the JavaScript code is super easy:

// When a HTML dice is clicked, it's value will change.
var dice = document.querySelector('.dice');

dice.addEventListener('click', function(){

    // Make a call to the roll_dice function from the C code.
    var result = _roll_dice();
    dice.className = "dice dice-" + result;                   

});

If your browser supports WebAssembly (caniuse), you should be able to see the working demo below.

Conclusion

We may still be in the early stages of WebAssembly but the new standard already shows its huge potential. The ability to run fast low-level code in the browser will give way to new apps and web experiences not possible with JavaScript alone.

Admittedly, working with WebAssembly is a bit tedious at the moment. The documentation is split over several places, tools are difficult to use, and you need JavaScript glue code to require wasm modules. All of these issues should be resolved as more and more people get into the new platform.

Presenting Bootstrap Studio

a revolutionary tool that developers and designers use to create
beautiful interfaces using the Bootstrap Framework.

Learn more
Web Browser Frame DevKit Box Mouse Cursor

Related Articles

Comments 2

  • Looks like the line #include <time.h> is missing in dice-roll.c. That's preventing the compilation from completing. Once you add it the error dice-roll.c:10:11: error: implicit declaration of function 'time' is invalid in C99 [-Werror,-Wimplicit-function-declaration] srand(time(NULL)); should go away and the index.js and index.wasm files will be generated. Great intro to WebAssembly though! I really enjoyed it.

  • I'm glad you liked the tutorial, Webmley!

    Sorry, I forgot to add time.h to the article. Thanks for pointing that out. I'll fix it asap.