Loading WebAssembly Modules in JavaScript

Urvashi
4 min readAug 30, 2020

According to the official website, WebAssembly is described as below —

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

For the purpose of this article, I am going to assume that you are aware of what WebAssembly is and what it aims to achieve. You can read more about it here.

Disclaimer: I don’t claim to be an expert in WebAssembly. Most of my knowledge of this article comes from “Learn WebAssembly” — by Mike Rourke, https://emscripten.org/, WebAssembly on MDN. If you find any errors in the article below, please let me know! I am just learning and playing around with it.

We have two API specifications for interacting with WebAssembly modules — the WebAssembly JavaScript Interface (JavaScript API) and the WebAssembly Web API.

Today, we will be talking about the various ways in which we can load .wasm modules in our JavaScript code. You need to have Emscripten downloaded and installed to follow the examples below.

Using WebAssembly Object

We are going to use a C++ program to demonstrate the same. Below is a simple function to add two numbers —

First, let’s compile our main.cc into a .wasm module without generating any glue code that Emscripten provides.

$ em++ -std=c++11 main.cc -Os -s WASM=1 -s SIDE_MODULE=1 -s BINARYEN_ASYNC_COMPILATION=0 -o main.wasm

For loading and using a WebAssembly module in JavaScript, it requires the following three steps-

  • First, get .wasm bytes and convert it into a typed array or an ArrayBuffer
  • Compile the bytes into a WebAssembly.Module
  • Instantiate the WebAssembly.Module

The JavaScript API has a global WebAssembly object available in the browser. It acts as the namespace for all WebAssembly-related functionality. It has the following static methods —

  • WebAssembly.instantiate() is used to compile and instantiate bytes at the same time. It returns both a Module and it's first Instance.
  • WebAssembly.instantiateStreaming() performs the same functionality as instantiate(), but it uses streaming to compile and instantiate the module, which eliminates an intermediate step of converting the .wasm bytes into an ArrayBuffer.
  • WebAssembly.compile()only compiles a WebAssembly module, but doesn’t instantiate it
  • WebAssembly.compileStreaming() also only compiles a WebAssembly module, but it uses streaming similar to instantiateStreaming() thus eliminating the intermediate step.
  • WebAssembly.validate() checks the WebAssembly binary code to ensure the bytes are valid and returns true or false.

Using WebAssembly.instantiate()

Create an index.html file and add a script tag where we will fetch our main.wasm file. We will be using the fetch API here but you can also use XMLHttpRequest (read how to use it here). We then create an ArrayBuffer of the response and instantiate it using WebAssembly.instantiate(). The resultcontains the instance object that we need to reference to call exported functions from the module.

Streaming the WebAssembly module

The newer WebAssembly.compileStreaming/WebAssembly.instantiateStreaming methods are a lot more efficient — they fetch, compile/instantiate a module all in one step, directly from the raw bytecode, cutting out the need for the ArrayBuffer step.

Using Emscripten’s Module Object

For this example, we will be using the following C++ file —

EMSCRIPTEN_KEEPALIVE forces LLVM to not dead-code-eliminate a function. We wrap our function in extern “C” block to avoid name mangling which happens in C++.

Now compile it into main.wasm and generate some glue code javascript as well —

$ em++ -std=c++11 main.cc -o main.js -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'

The above command tells Emscripten to generate main.wasm and main.js which contains Emscriptens’s Module object. Emscripten provides two functions for calling compiled C/C++ functions from JavaScript: ccall() and cwrap(). EXTRA_EXPORTED_RUNTIME_METHODS exports ccall and cwrap functions which can then be accessed from Module object.

The Module object is available globally after including main.js in our index.html file.

The Module provides some helpful functionality out of the box that would otherwise require a custom implementation in WebAssembly. Emscripten’s Module can be seen as a combination of WebAssembly’s Module and Instance objects, which are present in the result object that WebAssembly’s instantiation function returns.

Module.onRuntimeInitialized() is called when the runtime is fully initialized, that is, when compiled code is safe to run, which is after any asynchronous startup operations have completed (such as asynchronous WebAssembly compilation, file preloading, etc.). If you don’t use this, you might encounter the following error —

Uncaught RuntimeError: abort(Assertion failed: native function `Sum` called before runtime initialization) at Error

Module.cwrap(ident, returnType, argTypes) returns a native JavaScript wrapper for our exported C++ function. It can be reused as many times as needed whereas Module.ccall(ident, returnType, argTypes, args, opts) executes a compiled C++ function from JavaScript and returns the result.

Thank you for reading! I will be writing more about how to call JavaScript functions in our C/C++ using Emscripten in the next post.

--

--