Milica Mihajlija Performance taskforce @ChromiumDev, all things web @mozTechSpeakers, prev @ServoDev + @Outreachy Web gotta go fast 🚀

WebAssembly: How and why

8 min read 2457

How to run native code in the browser, why would you do that, and what does it all mean for JavaScript and the future of web development


In every browser, whether you use Chrome, Firefox, Edge, or Safari, code is interpreted and executed by a JavaScript engine — which only runs JavaScript. Unfortunately, JavaScript is not ideal for every task we want to perform. That’s where WebAssembly steps in.

WebAssembly is a new type of code that can be run in modern browsers. It was created to get better performance on the web. It’s a low-level binary format that has a small size, so it’s fast to load and execute. You do not write WebAssembly, you compile other higher level languages to it.

Assembly typically refers to humanly readable languages that are similar to machine code. Machine code is what your processor understands, a bunch of numbers.

Assembly languages and machine code

Every high-level programming language gets translated down to machine code in order to run on the processor. Different kinds of processor architectures need different machine codes and different kinds of assembly for each of them.

Compiling source code for different processor architectures

Despite its name, WebAssembly is not quite an assembly language because it’s not meant for any specific machine. It’s for the browsers, and when you’re delivering code to be executed in the browser, you don’t know what kinds of machines will your code be running on.

WebAssembly as an intermediary compiler target

WebAssembly is a language for a conceptual machine that’s the least common denominator of the popular real world hardware. When the browser downloads the WebAssembly code it can quickly turn it to any machine’s assembly.

This is what WebAssembly looks like — it has a textual format that’s easy to read (.wat), but binary representation is what you actually deliver to the browser (.wasm).

WebAssembly textual and binary format

What WebAssembly enables you to do is to take things like C, C++ or Rust code and compile it into what is called a WebAssembly module. You can load that into your web application and call it from JavaScript.

It’s not a replacement for JavaScript, it works alongside JavaScript.

WebAssembly module in an application

Why we need WebAssembly

Think about the cases where you need to use software outside of the browser: video games, video editing, 3D rendering, or music production. These applications do a lot of calculations and require a high degree of performance. That kind of performance is hard to get from JavaScript.

JavaScript started as a simple scripting language meant to bring some interactivity to the web full of lightweight hypertext documents. It was designed to be easy to learn and write, but it wasn’t designed to be fast. Over the years, browsers added optimizations in the way they interpret JavaScript that brought major performance improvements.

As it got faster, the list of things that you could do in the browser started expanding. New APIs brought things like interactive graphics, video streaming, offline browsing and many more. In turn, more and more rich applications, that were previously native-only, started coming to the web. Today you can easily edit documents and send emails from a browser, but there are areas where JavaScript performance is still a struggle.

Video games are particularly challenging because they have to coordinate not only audio and video, but also often physics and artificial intelligence. Being able to reach the performance for running games on the web efficiently would open the doors to bringing many other applications to the web and that’s what WebAssembly set out to do.


Why is the web so attractive

The beauty of the web is that it’s like ✨magic ✨ — it works anywhere. There is no download and no installation. In one click, web applications are delivered as soon as you need them. It’s safer than downloading and running a binary directly on a computer because browsers have established security properties that keep the code running in them from messing with your system. And sharing on the web is as easy as it gets — links are just clickable strings that you can put anywhere.

It is the only truly universal platform that makes your application accessible on any device. This also allows you to maintain a single code base, make the updates simple and be sure that every user can access your application.

Because of these built-in powers and the interactivity that the web offers, we went from hypertext and a small scripting language all the way to an incredibly powerful and popular platform filled with amazing applications and capabilities. But until now, it was still fundamentally powered by the same scripting language that was never really designed to do all of this in the first place.

What WebAssembly brings to the table

Here’s what makes WebAssembly special and such a good fit for the web:

  • speed
  • portability
  • flexibility

WebAssembly was designed for speed. Its binaries are much smaller than textual JavaScript files. Because of their size, they are faster to download and this is especially important on slow networks.

They are also faster to decode and execute. JavaScript is a dynamically typed language, variable types don’t have to be defined upfront and it doesn’t need to be compiled ahead. This makes it easy and fast to write, but it also means that the JavaScript engine has a lot more work to do. It has to parse, compile and optimize the code as it’s being executed on the page.

Parsing JavaScript involves transforming plain text to a data structure called abstract syntax tree (AST) and turning that into binary format. WebAssembly is delivered as binary and decoding it happens much faster. It’s statically typed so, unlike with JavaScript, the engine doesn’t need to speculate during compilation about what types will be used. Most of the optimization happens during the compilation of source code, before it even gets into the browser. Memory is managed manually, just like in languages like C and C++, so there’s no garbage collection either. All of this gives better and more reliable performance. The execution time of WASM binaries is just 20% slower than the execution of same native code.

Relative time spent processing WebAssembly in JavaScript engine

One of the main goals in designing WebAssembly was portability. To run an application on a device, it has to be compatible with the device’s processor architecture and operating system. That means compiling source code for every combination of operating system and CPU architecture that you want to support. With WebAssembly there is only one compilation step and your app will run in every modern browser.

Compiling native code to run on different platforms vs. compiling to WebAssembly

You can port not only your own applications to the web, but also the incredible wealth of C++ libraries and open source applications that exist out there. It is a language that is supported on practically every platform, including iOS and Android. With WebAssembly, it can be used as the common language across your web and mobile deployments.

The most exciting thing about WebAssembly is that it brings more flexibility in writing for the web. Until now, JavaScript has been the only fully supported language in web browsers. With WebAssembly, web developers will be able to choose other languages and more developers will be able to write code for the web. JavaScript will still be the best choice for most use cases but now there will be an option to drop down to a specialized language once in a while when you really need a boost. Parts like UI and app logic could be in JavaScript, with the core functionality in WebAssembly. When optimizing performance in existing JS apps, bottlenecks could be rewritten in a language that is better suited for the problem.

The current fully supported languages are C, C++ and Rust, but there are many others that are in the works, including Kotlin and .NET, both of which have already shipped experimental support.

How it works

You need a tool that will compile your source code to WebAssembly. One way is to use the seasoned modular compiler toolchain LLVM that can be set up to work with different languages. For compiling C and C++ you can use a simpler tool based on LLVM called Emscripten. Rust Nightly has it’s own compiler rustc that can output WebAssembly directly.

If you have a “Hello world” written in C, this Emscripten command will generate the files necessary to run it in the browser. What you get is a WebAssembly module along with HTML and JS files.

emcc hello.c -s WASM=1 -o hello.html

Compiling C/C++ code to WebAssembly with Emscripten

You need HTML and JS files because WebAssembly doesn’t have direct access to any platform APIs — the DOM, WebGL, WebAudio etc. To work with any of these, even to display the output of your WebAssembly code on a page, you have to go through JavaScript. Emscripten creates JS code that sets up your module and makes communicating with Web APIs possible. The HTML file loads that JS and displays WebAssembly output in a textarea or a canvas element.

You can think of WebAssembly binaries as regular app modules: the browser can fetch, load, and execute them. They have imports and exports that allow you to work with them the same way you work with JavaScript objects. You can call WebAssembly functions in JavaScript code and you can call JavaScript functions in WebAssembly modules.

It has only four primitive types and they are all numbers — integers and floats (i32, i64, f32 and f64). This means that passing more complex data types between JavaScript and WebAssembly is not straightforward. If you want to pass a string for example, you have to encode it into an array of numbers and then pass a pointer to it. It can only read and write from its own linear memory and it doesn’t have direct access to external JavaScript variables, unless they are copied into memory or passed through the call stack.

Right now making a lot of calls through JavaScript is not very fast, because the engine has to do some setup work each time. This will likely change in future, but for now good advice is to think of WebAssembly as a system that runs well in isolation and use it to offload big chunks of work.

If you want to try it out without any setup head over to webassembly.studio or WebAssembly Explorer.

Can you use it?

YES!

It’s here and it’s real. WebAssembly support rolled out last year in all major browsers. It’s currently supported for 74.93% of all global users and even 82.92% of desktop users. As a fallback for older browsers you can use Emscripten to compile to asm.js — a subset of JavaScript that uses only numbers (no strings, objects, etc.). It is a format that directly led to creating WebAssembly and it’s used widely on the web, for example for image compression when uploading photos to Facebook and for image editing in Adobe’s Lightroom.

Browsers that support WebAssembly

There are already some very exciting examples of WebAssembly in the real world.

I mentioned video games as a big goal for WebAssembly and both Unity and Unreal Engine 4 already have working demos. You can play a game of Tanks! with WebAssembly running in the Unity engine and Epic has a short WebAssembly demo online.

https://webassembly.org/demo/

Figma is an interface design tool that runs in the browser and allows designers to easily collaborate and share their work. It’s mostly written in C++ and has a 2D WebGL rendering engine that can handle very large documents. Initially they were using asm.js to compile their C++ code for the web. By switching to WebAssembly, their load time improved by more than 3x regardless of document size.

AutoCAD is design software which is mostly used in various engineering fields for making drawings such as floor plans, electrical circuits, piping designs etc. It’s written in C++ and it has been around for 35 years, longer than the web itself. Because of WebAssembly, it is now available as a web app without the need to rewrite such a huge code base in another language.

You can expect more and more applications to use WebAssembly and there are also some interesting demos online, like a video editor, raytracer and facial recognition algorithm running in the browser.

What’s to come

The browsers are already working on new features. Support for threading and garbage collection is coming, which will make WebAssembly a more suitable target for compiling languages like Java, C# and Go. One of the important goals is also creating debugging tools that support source maps which would allow developers to easily map WebAssembly to their source code.

JavaScript will still have its place in web development. It’s a great language, flexible enough to build almost anything and those few gaps that it can’t handle well can now be filled with WebAssembly. Compiling JavaScript to WebAssembly is not possible, and it really wouldn’t make much sense because browsers are already designed to work with JS directly and maximize its performance.

But even if you continue to work with JavaScript only, you can still benefit from WebAssembly and the speed boosts that it brings, through improved libraries and frameworks. Soon enough you’ll be able to download and import these modules like any other ECMAScript module using <script type='module'> and simply call their functions from JavaScript. As for the frameworks, Ember is already investigating WebAssembly implementation for its Glimmer VM and there’s potential for implementing some of the React features in WebAssembly too.

The future is here now and it looks fast and bright 🚀🌞


Plug: LogRocket, a DVR for web apps

https://logrocket.com/signup/

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.

Try it for free.

Milica Mihajlija Performance taskforce @ChromiumDev, all things web @mozTechSpeakers, prev @ServoDev + @Outreachy Web gotta go fast 🚀

One Reply to “WebAssembly: How and why”

Leave a Reply