Webassembly Tutorial using Emscripten

Inspiration

At NDC conferences Steve Sanderson had an impressive talk called Web Apps can’t really to *that* that I would highly recommend.  He showed an experimental .Net runtime compiled into a webassembly, enabling running a C# Razor based web application in the browser.  More info on his blog.

What is webassembly or WASM

Webassembly is an type of low level assembly-like language with a binary format that runs almost as fast as native applications.  You will need a compiler in some language to create the webassembly, currently there is a small number to choose from, the most commonly used is Emscripten, a LLVM based C/C++ compiler able to create Javascript asm.js format files.

Currently there are production ready compilers for C/C++, and experimental compilers for languages like Rust, C#, TypesScript, Java, Haxe, Kotlin, and Go.  In near future, I expect to see many production ready alternatives, including writing code in C#, Java, enabling code to be shared beetween front-end and back-end.

Most browsers have implemented web assembly, even on devices like iphone, android..

I decided to have a go with Emscripten, since it is production ready.

Prerequesites

Installing node.js, and create simple web server

The application needs a simple webserver.  The simplest one I know is a node.js application called http-server. If you have not installed node.js, download from https://nodejs.org/en/.  Node.js has a package manager called npm, to install the web server into your global node repository isue the first command below, second command starts the web server.

npm install http-server -g
http-server

Note that http-server, will serve anything from current directory and below.

Installing the Emscripten C++ compiler

Download Emscripten from this site:
https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html

In the root of the folder you install the compiler, you will find a bat file (or shell script, depending on your operating system) to set up paths to compiler, include files and other stuff needed to compile.

A simple application

To check out Webassembly, I decided to try to create a simple application, where all logic happens in C++ in the webassembly code (no javascript).  I created simple HTML form with ID, Name and Address fields, and buttons to load and clear form fields.

I would then need to figure out how to do following operations from C/C++

  • Getting strings from HTML form controls
  • Setting value on form control
  • Doing async ajax call to fetch data
  • Parse json files to extract data
  • Create C/C++ events on the HTML buttons
  • Create a simple HTML file

Last things first:

Create a simple HTML file

I created a simple HTML file with three fields and two buttons.  Note the script tag to include the javascript loader for the webassembly binary file.  The javascript loader and the webassembly binary are both created during compilation.

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>WebAssembly Example</title>
</head>
<body>

  <h2>WebAssembly Example</h2>
  id <br>
  <input id="id"   name="id" size="15" type="text" /> <br>
  Name <br>
  <input id="name"   name="name" size="15" type="text" /> <br>
  Address <br>
  <input id="address" name="address" size="15" type="text" /> <br>
  
  <br><br>
  <input id="btLoad" name="Load" type="submit" value="Load" />
  <input id="btClear" name="Clear" type="submit" value="Clear" />
  
  <!-- Include the JavaScript glue code. -->
  <!-- This will load the WebAssembly module and run its main. --> 
  src="test_html5.js">
 
</body>
</html> 

 

Create C/C++ events on the HTML buttons

The code below shows how to set assign a callback function to a button with specific Id.

int main()
{
  emscripten_set_click_callback("btLoad", (void*)0, 0, on_load_click);
  emscripten_set_click_callback("btClear", (void*)0, 0, on_clear_click);
  
  // To avoid exit after leaving main(), keeping events alive.
  EM_ASM(Module['noExitRuntime'] = true);
  
  return 0;
}

Parse json files to extract data

To parse json data from a server (typically a rest api), I chose to use Niels Lohmann’s JSON for Modern C++. This is hosted at github: https://github.com/nlohmann/json.  I preferred this because it is simple to use, and everything is hosted in one file: json.hpp.  C or C++ is not my first programming language, and he have done a brilliant job making his library easy to use with operator overloads, and helpers.

As you can see from code below, a one liner is enough to parse the json string, and another oneliner to read a node.

I do not have a black belt in C/C++,  and I am not sure all the string handling is done in an optimal manner.  However it seems that the returned string is not null terminated, so sometimes my program crashed.  I did a kludge to add a zero at the end, it now seems stable, but should probably incremented the buffer before adding the zero.

void DisplayFetchedData(emscripten_fetch_t *fetch)
{
  // Null terminates data, dodgy, may overwrite something in memory
  ((char*)fetch->data)[fetch->numBytes]='\0';
    
  printf ("%s\n", fetch->data);
  auto j4 = json::parse(fetch->data);
  setInput("id", j4["id"]);
  setInput("name", j4["name"]);
  setInput("address", j4["address"]);
}

Doing async ajax call to fetch data

Emscripten_Fetch api will do ajax calls for you.  Note you set up two callbacks, one for success, and one for error.   I have done this in a button click event.  I created two json files (data1.json and data2.json) just to simulate a rest api.

void downloadSucceeded(emscripten_fetch_t *fetch) {
  printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);    
  DisplayFetchedData(fetch);  
  emscripten_fetch_close(fetch); // Free data associated with the fetch.
}

void downloadFailed(emscripten_fetch_t *fetch) {
  printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
  emscripten_fetch_close(fetch); // Also free data on failure.
}

int on_load_click(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
{
  char* jsonUrl;
  asprintf(&jsonUrl, "/HTML5Api/data%s.json", getInput("id").c_str());

  emscripten_fetch_attr_t attr;
  emscripten_fetch_attr_init(&attr);
  strcpy(attr.requestMethod, "GET");
  attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY | EMSCRIPTEN_FETCH_REPLACE;
  attr.onsuccess = downloadSucceeded;
  attr.onerror = downloadFailed;
  emscripten_fetch_t *fetch = emscripten_fetch(&attr, jsonUrl);   

  return 0;
}

Setting value on HTML Control

To access the DOM, use the val.h.  Documentation says this is under construction. I’m not sure if it means that the library or documentation is under construction.

void setInput (std::string fieldname, std::string value )
{
  val document = val::global("document");
  val elem = document.call<val>("getElementById", std::string(fieldname));                    
  elem.set("value", value);
}

Getting string from HTML form control

Setting a value on the DOM is done in a similar way.

std::string getInput (std::string fieldName) 
{
  val document = val::global("document");
  val value = document.call<val>("getElementById", fieldName)["value"];
  return value.as<std::string>();
}

Compiling the program

To compile the program i used the following command:

emcc --bind test_html5.cpp -s FETCH=1 -s WASM=1 -O3 -o test_html5.js

Compiling will generate two files, a javascript loader and a wasm binary.

Running the program

If you have the web server up and running, you may point your browser to the location where you have your source (note that I have a subdirectory below where i started the web server).

Enter 1 or 2  in the id field, and press load, and it will load the corresponding json file that simulates a rest api.

WASMAs you can see, it loaded the wasm file,  retrieved the data1.json file and populated the form fields.

Summary

This is probably not a typical use case for a webassembly. In a near future there will probably be better tools for compiling different languages and api’s (like the dotnet api in Blazer).  But if you have a large C/C++ application that needs performance, and you want to Webify it, this may be a way to go.  There are some graphical applications, games and even operating systems ported to web using Enscripten/wasm.

There are not many sites with examples for Emscripten and WASM online, so it took me a lot of digging and experimenting to create this small application.

The entire project is hosted on my Git site: at https://github.com/magneka/Wasmtutorial

7 thoughts on “Webassembly Tutorial using Emscripten

    • There is support to access the DOM from webassembly, which took me quite a time and a lot of google to figure out. You will have to google for it. Today there will be a lot more documentation available. Personally I am eagerly waiting for the Blazor framework to be done, then you will be able to do it from C# in a quite easy way. You may try it out today, it is actually quite stable already.

  1. Appreciating the persistence you put into your blog and detailed information you present. It’s good to come across a blog every once in a while that isn’t the same old rehashed material. Excellent read! I’ve bookmarked your site and I’m including your RSS feeds to my Google account.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.