CosmicCode

Getting started with Web Assembly

Created May 30th, 2018

Last updated May 30th, 2018

WebAssembly
C++

Here we take a quick look at how to compile C++ code into Web Assembly (wasm) and get that running in a modern browser

WebAssembly is the assembly language for the web (duh!). It provides a way for browsers to execute code at near-native speed. For developers it allows important performance-sensitive components of webapps to be written in a more low-level language such as C++ and compile this into bytecode that can be accessed by Javascript in the front-end environment.

In this guide we are going to take a quick look at how to compile C++ to WebAssembly and interface with this from the javascript runtime.

We are going to be using Emscripten, a toolchain specialised in compiling to WebAssembly and asm.js (a performant sub-set of javascript). You can install the kit by following the Emscripten installation instructions.

If you are using WSL like I am you will need to also install CMake if you have not already (Linux x86_64).

You should have a project layout looking like this:

.
├── emsdk
├── src
│   ├── emscripten.h -> ../emsdk/./emscripten/incoming/system/include/emscripten.h
│   └── main.cpp
└── templates
    └── shell_minimal.html

Source your emsdk environments:

./emsdk activate latest
source ./emsdk_env.sh

Emscripten will generate a Javascript wrapper for our Wasm code. We need to create the HTML template that Emscripten will use to populate with the generated JS code.

shell_minimal.html

<!doctype html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Emscripten-Generated Code</title>
  </head>
  <body>
   {{{ SCRIPT }}}
  </body>
</html>

Here {{{ SCRIPT }}} will be substituted by the <script/> that points to our JS wrapper for the wasm module

Script exposes Module to the global scope. We can then reference this - useful when we have exported members. To export we need to define some functions in main.cpp

main.cpp

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

int main() {
    cout << "Wasm Module initialized!" << endl;
}

#ifdef __cplusplus
extern "C" {
#endif

void EMSCRIPTEN_KEEPALIVE exportedFunction() {
  cout << "Exported function has been called" << endl;
}

#ifdef __cplusplus
}
#endif

Here EMSCRIPTEN_KEEPALIVE is important as it will ensure that myFunction is included in the compilation (otherwise by default only main() will be included). This is included as part of the emscripten.h

We can now use the commandline tool em++ (analogous to g++) to compile our C++ code to Wasm and also produce wrapper JS + an HTML template file.

em++ -o index.html main.cpp -O3 -s WASM=1 --shell-file templates/shell_minimal.html -s NO_EXIT_RUNTIME=1  -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall"]'

This should produce the following artifacts:

  • index.html - the file that includes our JS wrapper
  • index.js - our JS wrapper
  • index.wasm - our C++ code compiled to Wasm

Typically we should be able to run emrun to serve the webpage but this has some issues in WSL. Instead you can use something like http-server

sudo npm install http-server -g to install a http server
http-server . 

You should see that in the browser console there will be Wasm Module initialized! printed. Success! The Wasm code has executed the main entrypoint to the module but not the exported functions. These can be accessed from Javascript in the following way:

Module.ccall('exportedFunction', // name of C function 
                             null, // return type
                             null, // argument types
                             null); // arguments

Reload your page and you should see

Exported function has been called

Andrew

Web development enthusiast with a keen interest in anything frontend.