Experimenting with Web Assembly and Node.js
Tags: javascript, node, webassembly
Published: 2019-03-12 23:42:50

---
title: Experimenting with Web Assembly and Node.js
published: true
description: Web Assembly promises to change no less than the future of how we develop JavaScript applications, but we're still early in the life of WASM. APIs are changing, and little focuses on Node.js. Here's how I got it working
tags: javascript,node,wasm
series: Adventures in Web Assembly
---

I've been wanting to play around with [Web Assembly](https://webassembly.org/) for a while, and finally had a chance to. Since I'm a mostly Node.js developer, I wanted to play around with it here instead of in the browser. There's not a lof of documentation on Web Assembly though, and the APIs are changing rapidly. I finally got it to work though, and here's how I did it.

I mostly followed the [emscripten Getting Started guide](https://emscripten.org/docs/getting_started/index.html), and tweaked it to fit both Node.js and to account for parts of this doc that are out of date.

## Installing emscripten

The first step is to install emscripten, which is well documented on [emscripten's website](https://emscripten.org/docs/getting_started/downloads.html). I installed it inside of Windows Subsystems for Linux running Ubuntu, so YMMV if you're using a different OS.

The first step is to install some dependencies. This takes a while, so I recommend brewing a nice cup of tea during this time. I chose a nice Silver Needle this time around :)

```bash
# Install Node.js using NodeSource's Ubuntu PPA, if not already installed
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -

# Install dependencies, as defined in emscripten's getting started guide
sudo apt-get install python2.7 nodejs cmake default-jre
```

Then we're ready to install emscripten itself. This also takes some time, so enjoy some more tea.

```bash
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
```

I ran into a bit of an issue with this approach. I don't know if I made a mistake, or things are just locked in. Either way, emscripten installed version 8.x.x of Node.js on it's own and overrode my 10.x.x install of Node.js. Since I'm using async/await for my Node.js code (which requires Node 10 in practice, if not in theory), I needed to manually override this by editing `~/.emscripten` so the following line matches the code below:

```ini
NODE_JS = '/usr/bin/node' # This line used to have a super long path to the emsdk directory
```

I never ran into any problems swapping out the version of Node.js that emscripten expected, but as always, YMMV.

## Creating the code

First, I created a very simple C file, courtesy of an [older blog post at Dynamsoft](https://www.codepool.biz/use-webassembly-node-js.html), named `test.c`. (Note: the instructions in this blog post no longer work).

```c++
int add(int a, int b) {
  return a + b;
}
```

Then, I created the JavaScript file that consumes this C module:

```javascript
const fs = require('fs').promises;
const util = require('util');

async function run() {
  async function createWebAssembly(bytes) {
    const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
    const env = {
      abortStackOverflow: (err) => { throw new Error(`overflow: ${err}`); },
      table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc' }),
      __table_base: 0,
      memory,
      __memory_base: 1024,
      STACKTOP: 0,
      STACK_MAX: memory.buffer.byteLength,
    };
    return WebAssembly.instantiate(bytes, { env });
  }

  const result = await createWebAssembly(new Uint8Array(await fs.readFile('./output.wasm')));
  console.log(util.inspect(result, true, 0));
  console.log(result.instance.exports._add(9, 9));
}
run();
```

A few things to note about this JavaScript code. First, the code is a masheup of the code from the Dynamsoft blog post and the emscripten getting started guide. I recommend reading the emscripten getting started guide for an in-depth explanation of what's going on here. The next thing to note is the `__table_base` and `__memory_base` entries. These are named `tableBase` and `memoryBase` in the emscripten documentation and in almost all other documentation I've found, but crashes when run. I'm guessing the API changed very recently. Hat tip to [this gist](https://gist.github.com/kripken/59c67556dc03bb6d57052fedef1e61ab#gistcomment-2779875) for pointing me in the right direction.

## Running the code

Now that we've written our files, it's time to run the code. First we compile this code with the following command:

```bash
emcc -s WASM=1 -s ONLY_MY_CODE=1 -s EXPORTED_FUNCTIONS="['_add']" -o output.js *.c
```

This command will generate the file `output.wasm` that's referenced in the JavaScript code. Now we're ready to run our code!

```
> node index.js
{ instance: Instance [WebAssembly.Instance] {},
  module: Module [WebAssembly.Module] {} }
18
```

And it works! We can indeed add 9 and 9 together to get 18, calling from JavaScript to C++ and back again, all using Web Assembly!

My next step is to take the [C++ messaging stack](https://github.com/nebrius/RaverLightsMessaging) from my [LED synchronization and animation project](https://github.com/nebrius/raver-lights), wrap it in Web Assembly so Node.js can talk to my boards, and integrate it with [Azure IoT Edge](https://docs.microsoft.com/en-us/azure/iot-edge/?WT.mc_id=devto-blog-brhugh) so I can control my LEDs from the cloud!