WebAssembly was just released this past March but has already generated a lot of excitement in the web community. It’s a new binary format for machine code that was specifically designed with browsers in mind. Because apps compiled to WebAssembly can run as fast as native apps, it has the potential to change the way software is written on the web.

Many people have started experimenting with toy WebAssembly projects on the side, but it’s hard to tell what the real-world performance gains will be unless you have a large compatible code base for comparison. Because our product is written in C++, which can easily be compiled into WebAssembly, Figma is a perfect demonstration of this new format’s power. If you haven’t used Figma before, it’s a browser-based interface design tool with a powerful 2D WebGL rendering engine that supports very large documents.

We thought our findings would be useful to the web community as an example of WebAssembly’s impact. Spoiler: it’s much faster.

WebAssembly allows developers to create desktop-quality experiences on the web without compromising on performance. All major browsers are adding support for it and devoting significant resources to making it as fast as possible.

Before WebAssembly, C++ code could be run in the browser by cross-compiling it to a subset of JavaScript known as asm.js. The asm.js subset is basically JavaScript where you can only use numbers (no strings, objects, etc.). This is all you need to run C++ code since everything in C++ is either a number or a pointer to a number, and pointers are also numbers. The C++ address space is just a giant JavaScript array of numbers and pointers are just indices into that array.

WebAssembly is a drop-in replacement for asm.js that is more efficient in every way. But it still maps 1:1 with asm.js so it has the same limitations. Since it can only load and store numbers, it needs to call out to JavaScript code to do anything interesting (create DOM nodes, make network connections, etc.). WebAssembly code is still inside the browser sandbox and can only use the browser APIs that JavaScript has access to.

There are many benefits to running C++ code using WebAssembly instead of cross-compiling it to JavaScript:

The format is very compact so it takes less time to transfer over the network than the equivalent cross-compiled JavaScript, even when compressed.

The format was designed to be as fast as possible for the browser to parse. This isn’t true for JavaScript syntax, which was designed for humans and contains lots of redundancy and extra rules that must be checked before it can be run. WebAssembly parses around 20x faster than asm.js.

C++ code is heavily optimized by the LLVM toolchain before it’s even encoded in WebAssembly. This means the browser can just translate it directly to native code without doing any optimizations. In contrast, browsers don’t skip optimizations for JavaScript code because it’s usually hand-written and needs many layers of optimizations to be fast.

It’s trivial for browsers to cache the translation of a WebAssembly module to native code. This means the second time you load a page using a WebAssembly module, there’s virtually no load time at all! This isn’t true for asm.js, which is mixed in with regular JavaScript and requires a complex verification pass to validate that it actually follows the asm.js restrictions.

WebAssembly has native support for 64-bit integers. JavaScript only has 64-bit floating-point numbers so it only supports 53-bit integers. 64-bit integers have to be emulated in JavaScript, which can be much slower.

If you’re interested in learning more about WebAssembly, I can recommend both Lin Clark’s high-level intro presentation and a low-level post by Rasmus Andersson, a designer and engineer who works at Figma, which describes the WebAssembly format in excellent detail. The official docs have a getting started guide if you’d like to play around with it yourself.

The biggest benefit we saw from using WebAssembly at Figma was faster load time. This isn’t surprising since one of WebAssembly’s primary goals is to reduce load time. When we measure load time at Figma we include the time to initialize our app, download the design file, and render the whole design for the first time.