What is WebAssembly? The next-generation web platform explained

WebAssembly is a binary instruction format and virtual machine that brings near-native performance to web browser applications, and allows developers to build high-speed web apps in the language of their choice.

fabio ballasina (CC0)

For two decades now, we’ve had only one programming language available to use natively in a web browser: JavaScript. The slow death of third-party binary plug-ins has ruled out other languages, such as Java and Flash’s ActionScript, as first-class citizens for web development. Other web languages, like CoffeeScript, are merely compiled to JavaScript.

But now we have a new possibility: WebAssembly, or Wasm for short. WebAssembly is a small, fast binary format that promises near-native performance for web applications. Plus, WebAssembly is designed to be a compilation target for any language, JavaScript being just one of them.

With every major browser now supporting WebAssembly, it’s time to start thinking seriously about writing client-side apps for the web that can be compiled as WebAssembly.

It is worth noting that WebAssembly apps aren’t meant to replace JavaScript apps—at least, not yet. Instead, think of WebAssembly as a companion to JavaScript. Where JavaScript is flexible, dynamically typed, and delivered through human-readable source code, WebAssembly is high-speed, strongly typed, and delivered via a compact binary format.

Developers should consider WebAssembly for performance-intensive use cases such as games, music streaming, video editing, and CAD applications. Many web services have already made the move, such as Google Earth. Figma, a collaborative drawing and diagramming app, turned to WebAssembly to cut load times and execution speed even when WebAssembly was relatively new.

How WebAssembly works

WebAssembly, developed by the W3C, is in the words of its creators a “compilation target.” Developers don’t write WebAssembly directly; they write in the language of their choice, which is then compiled into WebAssembly bytecode. The bytecode is then run on the client—typically in a web browser—where it’s translated into native machine code and executed at high speed.

WebAssembly code is meant to be faster to load, parse, and execute than JavaScript. When WebAssembly is used by a web browser, there is still the overhead of downloading the Wasm module and setting it up. For larger Wasm projects, those modules can run to several megabytes, so those delays can be significant. But all else being equal, WebAssembly runs faster.

WebAssembly also provides a sandboxed execution model, based on the same security models that exist for JavaScript now. Wasm applications can’t access anything outside the sandbox directly, including the DOM of the web page they’re running on. Any interactions with the rest of the machine must use ABIs like the WebAssembly System Interface (WASI). WASI provides controlled access to files, networking, system clock, and other system services often needed in programs.

Right now, running WebAssembly in web browsers is the most common use case, but WebAssembly is intended to be more than a web-based solution. The Wasmer project runs WebAssembly applications server-side, in much the same way the Node.js runtime runs JavaScript outside of the browser.

WebAssembly use cases

The most basic use case for WebAssembly is as a target to write in-browser software. The components that are compiled to WebAssembly can be written in any of a number of languages; the final WebAssembly payload is then delivered through JavaScript to the client.

WebAssembly has been designed with a number of performance-intensive, browser-based use cases in mind: games, music streaming, video editing, CAD, encryption, and image recognition, to name just a few.

More generally, it’s instructive to focus on these three areas when determining your particular WebAssembly use case:

  • High-performance code that already exists in a targetable language. For instance, if you have a high-speed math function already written in C, and you want to incorporate it into a web application, you could deploy it as a WebAssembly module. The less performance-critical, user-facing parts of the app can remain in JavaScript.
  • High-performance code that needs to be written from scratch, where JavaScript isn’t ideal. Previously, one might have used asm.js to write such code. You can still do so, but WebAssembly is being positioned as a better long-term solution.
  • Porting a desktop application to a web environment. Many of the technology demos for asm.js and WebAssembly fall into this category. WebAssembly can provide a substrate for apps that are more ambitious than just a GUI presented via HTML. See the demos of WebDSP and Windows 2000 in the browser, for two examples.

If you have an existing JavaScript app that isn’t pushing any performance envelopes, it’s best left alone at this stage of WebAssembly’s development. But if you need that app to go faster, WebAssembly may help.

WebAssembly language support 

WebAssembly isn’t meant to be written directly. As the name implies, it’s more like an assembly language, something for the machine to consume, than a high-level, human-friendly programming language. WebAssembly is closer to the intermediate representation (IR) generated by the LLVM language-compiler infrastructure, than it is like C or Java.

Thus most scenarios for working with WebAssembly involve writing code in a high-level language and turning that into WebAssembly. This can be done in any of three basic ways:

  • Direct compilation. The source is translated into WebAssembly by way of the language’s own compiler toolchain. Rust, C/C++, Kotlin/Native, and D now all have native ways to emit Wasm from compilers that support those languages.
  • Third-party tools. The language doesn’t have native Wasm support in its toolchain, but a third-part utility can be used to convert to Wasm. Java, Lua, and the .Net language family all have some support like this.
  • WebAssembly-based interpreter. Here, the language itself isn’t translated into WebAssembly; rather, an interpreter for the language, written in WebAssembly, runs code written in the language. This is the most cumbersome approach, since the interpreter may be several megabytes of code, but it allows existing code written in the language to run all but unchanged. Python (by way of PyScript, for example) and Ruby both have interpreters translated to Wasm.

WebAssembly features

WebAssembly is still in the early stages. The WebAssembly toolchain and implementation remain closer to proof-of-concept than production technology. That said, WebAssembly’s custodians have their sights set on making WebAssembly more useful through a series of initiatives:

Garbage collection primitives

WebAssembly doesn’t directly support languages that use garbage-collected memory models. Languages like Lua or Python can be supported only by restricting feature sets or by embedding the entire runtime as a WebAssembly executable. But there is work under way to support garbage-collected memory models regardless of the language or implementation.

Threading

Native support for threading is common to languages such as Rust and C++. The absence of threading support in WebAssembly means that whole classes of WebAssembly-targeted software can’t be written in those languages. The proposal to add threading to WebAssembly uses the C++ threading model as one of its inspirations.

Bulk memory operations and SIMD

Bulk memory operations and SIMD (single instruction, multiple data) parallelism are must-haves for applications that grind through piles of data and need native CPU acceleration to keep from choking, like machine learning or scientific apps. Proposals are on the table to add these capabilities to WebAssembly via new operators.

High-level language constructs

Many other features being considered for WebAssembly map directly to high-level constructs in other languages.

  • Exceptions can be emulated in WebAssembly, but cannot be implemented natively via WebAssembly’s instruction set. The proposed plan for exceptions involves exception primitives compatible with the C++ exception model, which could in turn be used by other languages compiled to WebAssembly.
  • Reference types make it easier to pass around objects used as references to the host environment. This would make garbage collection and a number of other high-level functions easier to implement in WebAssembly.
  • Tail calls, a design pattern used in many languages.
  • Functions that return multiple values, e.g., via tuples in Python or C#.
  • Sign-extension operators, a useful low-level math operation. (LLVM supports these as well.)

Debugging and profiling tools

One of the biggest problems with transpiled JavaScript was the difficulty of debugging and profiling, due to the inability to correlate between the transpiled code and the source. With WebAssembly, we have a similar issue, and it’s being addressed in a similar way (source map support). See the project’s note on planned tooling support.