Running WASI in Javascript with Wasmer-JS

Aaron Turner
Wasmer
Published in
5 min readOct 1, 2019

WebAssembly System Interfaces (WASI) is an exciting new specification that allows running POSIX-like applications anywhere, safely and securely with WebAssembly.

However, there is not much support for running WASI modules in both Node and the browser. We’ve been working hard on a solution for this problem and with the help of the WebAssembly community I am stoked to share what we made! 🎉

Today, drumroll please… 🥁, we are excited to announce Wasmer-JS! A collection of open-source, installable packages for running WASI modules in Node and the browser! 📦

Using these new packages we are launching we were able to get QuickJS, compile it to WASI, and run the QuickJS Wasm module in the browser. Thus in the QuickJS example, you can compile a JavaScript engine to WebAssembly, that is then run in the browser using the WebAssembly runtime, to run JavaScript. Meaning, you can now finally run JavaScript within your web browser! 🤡 (Joking!)

Here is an example of running the Javascript Interpreter quickjs in the Browser with the @wasmer/wasm-terminal package:

Gif of running QuickJS in the Browser with @wasmer/wasm-terminal

This demo shows off what could be done with running Wasm / WASI within Javascript. Similar to our example above, PSPDFKit is currently using Duktape compiled to Wasm to sandbox Javascript execution. Other ideas are:

  • If you are a web developer, you could compile your favorite tool written in a WASI compiled language. That you can then run, and use it’s output in your Node or web application. 🛠
  • If you are not much of a web developer, you could bring your application to the web by compiling it to WASI, and running it with a web frontend for your users. 🎨
  • And much more! 😃

Now that we have a good idea of what Wasmer-JS can do, let’s take a step back and see how it works. 🤔

Why is running WASI in JavaScript difficult?

WASI has an awesome simple, low-level POSIX-like API and the team did a great job in its design. However, here are some reasons why running WASI in JavaScript is difficult:

  • WASI modules expect certain imports from the host runtime, which are not currently available in web browsers or Node. 🌐
  • Wasm supports 64 bit integers, where JavaScript only recently added support for BigInt (64 bit integers). Thus, calling BigInts between JavaScript and Wasm is currently not supported, though it is currently in a proposal. ✍️
  • Some of the API calls are synchronous, whereas JavaScript normally is run in an asynchronous environment. ♻️
  • A lot of the API requires file system access, which is not readily available in the browser. 📁

There are some solutions that solve some of these issues. The awesome folks at Mozilla have a web polyfill that runs in the browser by polyfilling the WASI imports. This can be used to show the standard output of the run module. But it does not solve all of the issues above, does not support standard input can not run WASI modules that use i64 imports, and is not meant to be used in a modern JavaScript project. Next, there is node-wasi made by devsnek. This project polyfills the WASI imports, and leverages Node’s fs implementation to provide file system access. It also could be used in a modern Node project, but it can not run WASI modules that use i64 imports or be used in the browser.

By leveraging the great work done in node-wasi, we were able to expand on their work to solve all of these issues to get WASI running in both the browser and Node. 🎉

How does Wasmer-JS run WASI in JavaScript?

Wasmer-JS can run WASI modules in JavaScript using its following packages:

  • @wasmer/wasi is a WASI implementation based on and extending from node-wasi. It adds types as it is written in TypeScript. It is following the same API as the Node WASI fork. It also works in the browser using the appropriate bindings. 🌐
  • @wasmer/wasmfs is a sandboxed file system, that works in the browser, built on top of memfs. This also provides convenience functions for I/O. 📁
  • @wasmer/wasm-transformer is a library built with wasm-pack, to perform fast modifications to WebAssembly binaries. This can be used for injecting trampoline functions to handle i64/BigInt calls between Wasm and JavaScript. 🦀

Let’s take a look at an example:

To see all of these packages working together at a greater scale, we can take a look at the @wasmer/wasm-terminal package.

This package offers a a terminal-like interface using Xterm.js, that can fetch and run Wasi modules using the other Wasmer-JS packages. It even supports custom Javascript callback commands, common terminal hotkeys, tab autocomplete, piping between modules, and running commands in parallel web workers using Comlink! 😍

Thank you!

This announcement is super exciting, and we are proud of the work we have accomplished! 🎉 But this definitely could not have been done without the help of the WebAssembly community, and the open source projects we build our work on top of. We want to give a huge thank you to the authors of node-wasi, wasm-pack, memfs, Xterm.js, Comlink, and the other dependencies we are building on top of! 🙏

And most of all, Thank YOU for reading this article! Please feel free to try out the packages in Wasmer-JS. All of these things are open-source, and contributions are definitely welcome! Contributions can be opening an issue, submitting a PR, or even telling a friend! 💪

Feel free to reach out on Twitter or Spectrum, and we are very excited to see what you build! Cheers! 🍻

--

--

Aaron Turner
Wasmer
Writer for

Skate. Music. Video Games. Code. Developer / Developer Relations at Wasmer. All opinions expressed are my own. Please excuse the spelling, I am a lazy typist.