The future has already arrived! That’s right, WebAssembly support is already in almost 100% of the main browsers (Chrome, Firefox, Safari, Brave, Opera, Microsoft Edge). What once seemed like something very cool, but far from being achieved (we’ve been through this many times, right?) is now a reality. So let’s talk about the first steps of getting started with WebAssembly. If you don’t know exactly how and when it will help you, or if you think it’s something very complicated, I invite you to keep reading.
What is WebAssembly
WebAssembly, or just wasm, is a new, portable, lightweight, and load-time efficient format suitable for web compilation.
1) New format
Just like our old acquaintance .js, we now have .wasm.
WebAssembly defines a new standard text format that encodes a WebAssembly module with all definitions to its binary format.
We don’t need to delve into the innards of WebAssembly, but it’s important (and really cool) to understand how some things work.
The WebAssembly text format uses S-expressions, which is a notation for linked data lists invented for use in Lisp.
Getting started with WebAssembly: examples
Let’s see some code.
As the ultimate goal is to compile a program written in another language (C/C++, Rust, for example) to wasm, let’s see a simple C program, which just increments a value:
/* counter.c */
int counter = 100; int count() {
counter += 1;
return counter;
}
After compiling the counter.c program above (we’ll see how to do this later), the final result, which will be consumed by the browser (or other platform), will be the counter.wasm file:
0000000001100001 0111001101101101 0000000100000000 0000000000000000 0000000000001100 0000011001100100 0111100101101100 0110100101101110 0110101110010000 1000000011000000 0000001000000000 0000000110001000 1000000010000000 1000000000000000 0000001001100000 0000000000000001 0111111101100000 0000000000000000 0000001011000001 1000000010000000 1000000000000000 0000010000000011 0110010101101110 0111011000001010 0110110101100101 0110110101101111 0110110101101111 0100001001100001 0111001101100101 0000001101111111 0000000000000011 0110010101101110 0111011000000110 0110110101100101 0110110101101111 0111001001111001 0000001000000000 1000000000000010 0000001101100101 0110111001110110 0111011000000110 0110110101100101 0110110101101111 0111001001111001 0000001000000000 1000000000000010 0000001101100101 0110111001110110 0000010101110100 0110000101100010 0110110001100101 0000000101110000 0000000000000000 0000001101100101 0110111001110110 0000100101110100 0110000101100010 0110110001100101 0100001001100001 0111001101100101 0000001101111111 0000000000000011 1000010010000000 1000000010000000 0000000000000011 0000000000000001 0000000100000110 1001000010000000 1000000010000000 0000000000000011 0111111100000001 0100000100000000 0000101101111111 0000000101000001 0000000000001011 0111111100000000 0100000100000000 0000101100000111 1011100010000000 1000000010000000 0000000000000100 0000011001011111 0110001101101111 0111010101101110 0111010000000000 0000000000010010 0101111101011111 0111000001101111 0111001101110100 0101111101101001 0110111001110011 0111010001100001 0110111001110100 0110100101100001 0111010001100101 0000000000000010 0000101101110010 0111010101101110 0101000001101111 0111001101110100 0101001101100101 0111010001110011 0000000000000001 0000100001011111 0110001101101111 0111010101101110 0111010001100101 0111001000000011 0000010000001001 1000000110000000 1000000010000000 0000000000000000 0000101011000011 1000000010000000 1000000000000000 0000001110011000 1000000010000000 1000000000000000 0000000100000001 0111111100000010 0111111100100011 0000000000100011 0000000000101000 0000001000000000 0100000100000001 0110101000100010 0000000000110110 0000001000000000 0010000000000000 0000101100001011 1000001110000000 1000000010000000 0000000000000000 0000000100001011 1001100010000000 1000000010000000 0000000000000000 0000001001000000 0010001100000000 0100000100010000 0110101000100100 0000001000100011 0000001001000001 1000000010000000 1100000000000010 0110101000100100 0000001100010000 0000000100001011 0000101100001011 1000011110000000 1000000010000000 0000000000000001 0000000000100011 0000000000001011 0000000101100100
Still not very useful, but now we have the general knowledge of how things work.
We still need to show an interesting code, our counter.c program, compiled for wasm and represented in S-expression:
(module (type $t0 (func (result i32))) (type $t1 (func)) (import “env” “memoryBase” (global $g0 i32)) (import “env” “memory” (memory $M0 256)) (import “env” “table” (table $T0 0 anyfunc)) (import “env” “tableBase” (global $g1 i32)) (func $f0 (export “_count”) (type $t0) (result i32) (local $l0 i32) (block $B0 (result i32) (i32.store (get_global $g0) (tee_local $l0 (i32.add (i32.load (get_global $g0)) (i32.const 1)))) (get_local $l0))) (func $f1 (export “runPostSets”) (type $t1) (nop)) (func $f2 (export “__post_instantiate”) (type $t1) (block $B0 (set_global $g2 (i32.add (get_global $g0) (i32.const 16))) (set_global $g3 (i32.add (get_global $g2) (i32.const 5242880))) (call $f1))) (global $g2 (mut i32) (i32.const 0)) (global $g3 (mut i32) (i32.const 0)) (global $g4 (export “_counter”) i32 (i32.const 0)) (data (get_global 0) “d”))
Note that the code above is not a .wasm, but a .wat, which is the recommended extension for WebAssembly code in text format.
But this code will hardly be written by “humans”. The key point is to remember that the purpose of WebAssembly is to be an easy-to-compile format, that is, we will not write WebAssembly modules, but rather port existing code, or even create solutions that require high performance in other languages.
Other WebAssembly examples
A nice example, cited by Lim Clark, in the excellent series on WebAssembly on MDN, is the case of React, where, for example, it would be possible to port all that complex part responsible for the virualDOM to WebAssembly.
For those who use React, this change would not affect anything, but the performance gain would be noticeable.
Lin even gave a talk at the last ReactEurope, talking precisely about WebAssembly and React:
2) Portable
WebAssembly is designed to run efficiently on different types of operating systems and different architectures, on the web and off the web.
An interesting point is that WebAssembly was created to run on the Web (in browsers), but it is possible to run wasm in other environments, such as servers, IoT devices, desktop apps, mobile apps, etc.
Obviously Node.js comes to mind, right? And yes, it is possible to run WebAssembly in the Node.js environment, or any similar platform, however, WebAssembly goes further and is able to run even in environments without a JavaScript interpreter.
3) Lightweight and fast
WebAssembly was born with the aim of being very light, and by light I mean the size of the file trafficked on the network.
Because it is a binary and optimized format, wasm files are much lighter than JavaScript files, for example.
This is one of the factors that greatly contribute to more efficient charging.
WebAssembly and parsing
Another important point of WebAssembly is in relation to parsing. JavaScript engines interpret an intermediate representation resulting from an AST that has been previously parsed. Hard? Well, a lot of things happen until that beautiful code of yours is executed.
The important thing here is the fact that WebAssembly does not “suffer” with these steps, as there is no need to execute them, since there is no transformation.
The WebAssembly code is already the intermediate representation itself, so the decode is enough for everything to work.
WebAssembly x JavaScript?
WebAssembly is very fast indeed, and we are still in the early days.
There are still many points that can be improved, but depending on the type of software you are developing or porting to WebAssembly, it is possible to have from 10% to 800% speed gain.
But what about JavaScript?
Calm down folks! A lot of people have asked me: “But then WebAssembly is the replacement for JavaScript”?
On the contrary, WebAssembly is yet another super important layer of the web platform, which works hand in hand with JavaScript wasm modules only work by being invoked by the JavaScript API available in browsers. It’s working together, just like HTML, CSS, SVG, WebGL, etc.
Here’s an example of integration:
var importObj = {
js: {
import1: () => console.log(“hello,”),
import2: () => console.log(“world!”)
}
};
fetch(‘demo.wasm’)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.instantiate(buffer, importObj))
.then(({module, instance}) => instance.exports.f());
After compiling to wasm (that hex code shown above), we can import the module with a simple fetch. The API isn’t the most human readable yet, but we’re getting there.
Working with WebAssembly’s possibilities
We’ve seen that WebAssembly is now available in almost 100% of major browsers. We did a brief review on the goals of the technology, we saw that WebAssembly introduces a new text format that represents its binary format.
We also saw code examples in their possible formats, to have a real idea of everything that happens behind all this magic. It was also possible to notice that WebAssembly is lighter and more performant than JavaScript, not forgetting that the technology does not replace JavaScript, on the contrary, it works together.
Note: This article was written by our partners over at BrazilJS and is being reproduced here with their authorization.
…
🌎 VANHACK, LinkedIn Talent Awards Winner 2021, is Canada’s most respected recruitment company. With more than 1,900 hires, VanHack is on a mission of increasing diversity and creating a borderless world. So if you are a software developer looking for a job abroad, in Canada, the US, or Europe, join VanHack today. 100% free for candidates, plus you will get all the preparation you need when your profile is shortlisted.
Visit our platform to become one of our many VanHackers hired abroad 😃
For success stories and tips about working in Canada, check out the VanHack Podcast 🎧
Be part of VanHack’s Learning Hub 📒
Check out the next VanHack event 🗓