Skip to main content
On this page

WebAssembly

Designed to be used alongside JavaScript to speed up key application components, WebAssembly (WASM) can have much higher, and more consistent execution speed than JavaScript - similar to C, C++, or Rust. Deno can execute WebAssembly modules with the same interfaces that browsers provide.

Using WebAssembly in Deno Jump to heading

To run WebAssembly in Deno, all you need is a binary to run. The following binary exports a main function that just returns 42 upon invocation:

// deno-fmt-ignore
const wasmCode = new Uint8Array([
  0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127,
  3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0,
  5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145,
  128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97,
  105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0,
  65, 42, 11
]);

const wasmModule = new WebAssembly.Module(wasmCode);

const wasmInstance = new WebAssembly.Instance(wasmModule);

const main = wasmInstance.exports.main as CallableFunction;
console.log(main().toString());

In order to load WebAssembly in a JavaScript program, the following steps need to be performed:

  1. Fetching the binary (usually in the form of a .wasm file, though we are using a simple byte array for now)
  2. Compiling the binary into a WebAssembly.Module object
  3. Instantiating the WebAssembly module

WebAssembly is a binary data format, not intended to be human readable, nor to be written by hand. Your wasm files should be generated by a compiler for a language such as Rust, Go or AssemblyScript.

As an example, a Rust program that compiles to the aforementioned bytes would look something like this:

pub fn main() -> u32 {  // u32 stands for an unsigned integer using 32 bits of memory.
  42
}

Using the Streaming WebAssembly APIs Jump to heading

The most efficient way to fetch, compile and instantiate a WebAssembly module is to use the streaming variants of the WebAssembly API. For example, you can use instantiateStreaming combined with fetch to perform all three steps in one go:

const { instance, module } = await WebAssembly.instantiateStreaming(
  fetch("https://wpt.live/wasm/incrementer.wasm"),
);

const increment = instance.exports.increment as (input: number) => number;
console.log(increment(41));

Note that the .wasm file must be served with the application/wasm MIME type. If you want to do additional work on the module before instantiation you can instead use compileStreaming:

const module = await WebAssembly.compileStreaming(
  fetch("https://wpt.live/wasm/incrementer.wasm"),
);

/* do some more stuff */

const instance = await WebAssembly.instantiate(module);
instance.exports.increment as (input: number) => number;

If for some reason you cannot make use of the streaming methods you can fall back to the less efficient compile and instantiate methods.

For a more in-depth look on what makes the streaming methods more performant, check out this post.

WebAssembly API Jump to heading

Further information on all parts of the WebAssembly API can be found on in the Deno Reference Guide and on MDN.

Working with Non-Numeric Types Jump to heading

The code samples in this document only used numeric types in the WebAssembly modules. To run WebAssembly with more complex types (such as strings or classes) you will need to use tools that generate type bindings between JavaScript and the language used to compile to WebAssembly.

An example on how to create type bindings between JavaScript and Rust, compiling it into a binary and calling it from a JavaScript program can be found on MDN.

If you plan to do a lot of work with Web APIs in Rust+WebAssembly, you may find the web_sys and js_sys Rust crates useful. web_sys contains bindings to most of the Web APIs that are available in Deno, while js_sys provides bindings to JavaScript's standard, built-in objects.

Optimization Jump to heading

For production builds you can perform optimizations on WebAssembly binaries. If you're serving binaries over a network then optimizing for size can make a real difference. If you're mainly executing WebAssembly on a server to perform computationally intensive tasks, optimizing for speed can be beneficial. You can find a good guide on optimizing (production) builds here. In addition, the rust-wasm group has a list of tools that can be used to optimize and manipulate WebAssembly binaries.