WebAssembly (WA or Wasm) is a binary instruction format for a stack based virtual machine. It is designed as a portable target for the compilation of high-level languages like C, C++ and Rust, enabling deployment on the Web for client and server applications.
WebAssembly or Wasm is a technology that makes it possible to run high-performing, low-level code in the browser. In simple terms, Wasm is a programming language format (Abstract Syntax Tree) and execution environment (portable stack machine) that can be a compilation target for other programming languages. It can be embedded into the Web browser to provide a common way to support other languages, as an alternative to JavaScript.
WebAssembly goals
WebAssembly is being created as an open standard inside the W3C WebAssembly Community Group with the following goals.
- To be fast, efficient and portable: WebAssembly code can be executed at near-native speed across different platforms by taking advantage of common hardware capabilities.
- To be readable and debuggable: Wasm is a low-level assembly language, but it does have a human-readable text format (the specifications for which are still being finalised) that allows code to be written, viewed and debugged by hand.
- To stay secure: Wasm is designed to be run in a safe, sandboxed execution environment. Like other Web code, it will enforce the browser’s same origin and permissions policies.
- Not to break the Web: Wasm is designed so that it works well with other Web technologies and maintains backward compatibility.
How WebAssembly works
Here’s a simple overview of what WebAssembly actually does.
In browsers today, JavaScript (JS) is executed in a virtual machine (VM), which optimises your code and squeezes out every ounce of performance. As things stand, JS is one of the fastest dynamic languages around. Yet, even though it is fast, it still can’t compete with raw C and C++ code. This is where Wasm comes in.
WebAssembly runs in the same JavaScript VM, but performs much better. The two can communicate freely and don’t exclude each other. This way you can have the best of both worlds — JavaScript’s huge ecosystem and friendly syntax, combined with the near-native performance of WebAssembly.
Most people write Wasm modules in C and compile them to .wasm files, which aren’t recognised by the browser directly; so they need something called JavaScript glue code to be loaded.
How WebAssembly fits in a Web platform
The Web platform can be thought of as having two parts:
- A virtual machine (VM) that runs the Web app’s code, e.g., the JavaScript code that powers your apps.
- A set of Web APIs that the Web app can call to control browser and device functionality, and that makes things happen (DOM, CSSOM, WebGL, IndexedDB, Web Audio API, etc).
Historically, the VM has been able to load only JavaScript. This has worked well for us as JavaScript is powerful enough to solve most problems people have on the Web today. We have run into performance problems, however, when trying to use JavaScript for 3D games, virtual and augmented reality, computer vision, image or video editing, and a number of other domains that demand native performance.
WebAssembly is a language that is different from JavaScript, but it is not intended as a replacement. Instead, it is designed to complement and work alongside JavaScript, allowing Web developers to take advantage of both languages’ strong points.
- JavaScript is a high-level language that is flexible and expressive enough to write Web applications. It has many advantages — it is dynamically typed, requires no compile steps, and has a huge ecosystem that provides powerful frameworks, libraries and other tools.
- WebAssembly is a low-level Assembly-like language with a compact binary format that runs with near-native performance. It provides languages with low-level memory models such as C++ and Rust with a compilation target so that they can run on the Web.
With the advent of WebAssembly in browsers, they can now load and run two types of code — JavaScript as well as WebAssembly.
The WebAssembly dictionary
There are several keywords that users need to be aware of to understand how WebAssembly runs in the browser. These are mentioned below.
- Module: This represents a WebAssembly binary that has been compiled by the browser into executable machine code. A module is stateless and thus, like a blob, can be explicitly shared between windows and workers (via postMessage()). A module declares imports and exports just like an ES2015 module.
- Memory: This is a resizable array buffer that contains the linear array of bytes read and written by WebAssembly’s low-level memory access instructions.
- Table: This is a resizable typed array of references (e.g., to functions) that cannot otherwise be stored as raw bytes in memory (for safety and portability reasons).
- Instance: This is a module paired with all the state it uses at runtime, including a memory, table, and a set of imported values.
The JavaScript API provides developers with the ability to create modules, memories, tables and instances. Given a WebAssembly instance, JavaScript code can synchronously call its exports, which are exposed as normal JavaScript functions.
Let us now look at an example that has been broken down into three steps.
Step 1: Writing C code
We are writing a simple C program that picks the random numbers, 1 and 6. Let’s make a new file in a working directory of your choice and name it dice-roll.c.
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <emscripten/emscripten.h> int main(int argc, char ** argv) { printf(“WebAssembly module loaded\n”); } int EMSCRIPTEN_KEEPALIVE roll_dice() { srand ( time(NULL) ); return rand() % 6 + 1; }
When we compile the Wasm file and load this code into the browser, the function main() will be automatically executed. The contents of printf() will be displayed in the console.log when executed. We need the roll_dice function to be available in JavaScript and let the Emscripten compiler know about it by adding EMSCRIPTEN_KEEPALIVE. The Emscripten compiler is a source-to-source compiler that runs as a back end. It is used in this case because the Emscripten tool is able to take any C/C++ source code and compile it into a .wasm module, while loading the necessary JavaScript code for running the module as well as an HTML file to display the results of the code.
Step 2: Compiling C to WebAssembly
Now that we have our simple C program, we need to compile it into Wasm. Not only that, we also need to generate JavaScript glue code that will help us actually run it.
Here we have to use the Emscripten compiler. There are tons of CLI options available and many different approaches. I found the following combination most user friendly:
emcc dice-roll.c -s WASM=1 -O3 -o index.js
After running the command, two new files (index.wasm and index.js) will get added to the working directory. These files hold the WebAssembly module and its glue code, respectively.
Step 3: Loading WebAssembly code in the browser
Now, we are in familiar Web development territory. We can create a basic index.html file and include the JavaScript glue code in a script tag.
<!DOCTYPE html> <head> <meta charset=”utf-8”> <meta http-equiv=”X-UA-Compatible” content=”IE=edge”> <title>WebAssembly Example</title> <meta name=”viewport” content=”width=device-width, initial-scale=1”> </head> <body> <!-- Include the JavaScript glue code. --> <!-- This will load the WebAssembly module and run its main. --> <script src=”index.js”></script> </body> </html>
Once we are done with the compilation of the above code and have created the .wasm file, we can place the three files (index.wasm, index.js and index.html) in the document root of any working Web server and call index.html in a browser to test it.
This article has covered what WebAssembly does, how it works and how it fits into the Web, along with a simple example. The ability to run fast, low-level code in the browser will give way to new apps and Web experiences not possible with JavaScript alone.