Getting Started With WebAssembly
What Is WebAssembly?
WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.
WebAssembly is a new type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++, C# and Rust with a compilation target so that they can run on the web. It is also designed to run alongside JavaScript, allowing both to work together.
Who Are Using WebAssembly?
- Google: Using WebAssembly technology to make Gmail faster, and Squoosh, an image compression tool produced by their ChromeLab team, also compiles several common image codecs into WebAssembly for efficient image compression;
- Mozilla: Developing WebAssembly as a new generation of Web technology;
- Unity: Use WebAssembly to build a high-performance game engine;
- AutoCAD: Using WebAssembly to implement CAD applications that run on the Web;
- SketchUp: Using WebAssembly to implement a 3D modeling application that runs on the Web;
- Figma: A collaborative design tool that runs on the Web using WebAssembly;
- Dropbox: Building a client-side accelerator with WebAssembly;
- Microsoft: Using WebAssembly to accelerate JavaScript in the Edge browser;
- TensorFlow.js: Use WebAssembly to run TensorFlow models in the browser;
- HandBrake: Build a video converter using WebAssembly to improve performance;
- WordPress In Browser: Through WebAssembly, you can run this classic PHP CMS application on the browser without any server;
- Computer Museum in the Web Page The webmaster built a WebAssembly application that allows us to load some ancient operating systems on the page, which is very interesting.
How Do I Use It?
Through the introduction of WebAssembly - WebAssembly | MDN document, we can see that it has 4 loading methods. Among them, 1 and 2 will directly return a Promise that generates a WebAssembly instance, and 3 and 4 will return a WebAssembly.Module, which Returns a WebAssembly instance after being executed. It can also be seen from their names: 1 and 2 are instantiated, and 3 and 4 are only compiled (not instantiated).
- WebAssembly.instantiate()
The main API for compiling and instantiating WebAssembly code, returning a Module and its first Instance instance. - WebAssembly.instantiateStreaming()
Compile and instantiate a WebAssembly module directly from the streaming underlying source, returning both the Module and its first Instance instance. - WebAssembly.compile()
Compiles the WebAssembly binary into a WebAssembly.Module without instantiation. - WebAssembly.compileStreaming()
Compiles WebAssembly.Module directly from streaming underlying source code, instantiating it as a separate step.
Whether it is to initialize the Web Assembly module from Streaming or bufferSource, it is just a process of loading .wasm
file, and we can choose proper loading method depends on our actual needs. After loading through the above method, we will get a WebAssembly.Instance object. Through this object we can access its internal variables and functions.
One Simple Example
Instantiating A WebAssembly Module Via WebAssembly.instantiateStreaming
Demo Page Can Be Visited In Here –>
1 |
|
The .wasm file in this example is compiled from the .wat
file. What is a .wat
file? WAT - WebAssembly Text Format.
WebAssembly has a binary format and a text format. The binary format (.wasm) is a compact binary instruction format for a stack-based virtual machine and is designed to be a portable compilation target for other higher-level languages such as C, C++, Rust, C#, Go, Python and many more. The text format (.wat) is a human readable format designed to help developers view the source of an WebAssembly module. The text format can also be used for writing codes that can be compiled to the binary format.
1 | (module |
We can compile .wat
files into .wasm
binaries via wabt.
More demos can be found in webassembly-examples.
What If We Instantiate It In Another Way?
WebAssembly.instantiate() and WebAssembly.instantiateStreaming() have exactly the same result, the difference is that they accept different input parameters, use WebAssembly.instantiate() Its input parameters need to use ArrayBuffer.
1 |
|
How Do We Get The WebAssembly Module Code?
WebAssembly binaries can be compiled from multiple languages (C/C++, Rust, Assembly Script, C#…), see I want to… - WebAssembly.
Here I use C code to compile .wasm
as an example.
Install The Compilation Tool Emscripten
Follow the installation steps in Emscripten Documentation. After executing
1 | source ./emsdk_env.sh |
We can use emcc
in our shell to compile .c
file to .wasm
file.
Writing C Code For Compilation
Still I use a+b
as an example. This C file module contains only one add function, which takes two numbers as input and returns the sum of their addition.
1 |
|
There is a strange
#include <emscripten/emscripten.h>
, what is it?emscripten.h
provides some public C++ APIs, see emscripten.h for details.
You can see that I added the
EMSCRIPTEN_KEEPALIVE
macro to the add function, which tells the compiler to keep it and export it, which allows us to access it when we access the WebAssembly instance with JavaScript.
Compile the .wasm
file
Excute the following command in our shell
1 | emcc add.c -s WASM=1 -o add.html |
The above command arguments is explained as follows:
emcc
is the command line command for Emscripten-s WASM=1
tells Emscripten to output wasm files, if this parameter is not specified, asm.js will be output by default-o add.html
tells the compiler to generate an HTML document named add.html to run the code, as well as the wasm module and the corresponding JavaScript glue code used to compile and instantiate wasm, so that wasm can be used in the web environment used in
After running the above command, there should be three more files in your WebAssembly directory:
- add.wasm Binary wasm module code
- add.js A JavaScript file containing the glue code, through which native C functions are translated into JavaScript/wasm code
- add.html An HTML file used to load, compile and instantiate wasm code, and display the output of wasm code on the browser
Run It With JavaScript
Open the add.html
file generated above with an http server. After opening the page and using devtools, we can find that the .wasm file we compiled has been automatically loaded. This is because the add.js we compiled has already generated the relevant code needed to instantiate WebAssembly for us.
We can find the instantiateAsync
function which is the key to load our WebAssembly Module.
1 | function instantiateAsync(binary, binaryFile, imports, callback) { |
In this example, add.js initializes the WebAssembly instance and puts it in a global module Module, so we can access the add function in the C module through Module.asm.add
.
Implement WebAssembly In Other Applications
Through the demo above, we know that we can use JavaScript to initialize WebAssembly and use it, but there is a problem that the js glue code generated by emcc creates a global variable named Module. But what if I:
- Don’t want to use Module as a variable name, because I may already have this name
- Don’t want to load WebAssembly modules in the first place
- No need to use WebAssembly modules in Node.js environment
- Want to simplify the js glue code to achieve faster loading speed
Here are my answers:
- Customize our WebAssembly module name
When compiling with emcc, add the-s EXPORT_NAME="CustomModuleName"
parameter to customize the module name - Loading WebAssembly modules asynchronously
When compiling with emcc, export an ES6 JavaScript module with-o target.mjs
, or modularize it with-s MODULARIZE
.-s MODULARIZE
can be used with-s EXPORT_NAME="CustomModuleName"
. After loading the js glue code, you can useCustomModuleName()
to instantiate WebAssembly. Among them,CustomModuleName
function returns a Promise. - When I don’t need Node.js related code
When compiling with emcc, add-s ENVIRONMENT=web
, the compiled glue js code will not contain Node.js related logic. - Simplify the glue js code
When compiling with emcc, add the-O3
parameter to minify. The effect of add.c in this example is: add.js(10KB/53KB), add.wasm(72B/916B).
You can find more answers on Emscripten FAQ.
WebAssembly + Web Worker?
When we use WebAssembly for more complex calculations, in order to avoid affecting the browser rendering process, we can put it into Web Worker to run.
You can refer to Using WebAssembly with Web Workers - SitePen.
This Demo Can Be Visited In Here
1 |
|
1 | importScripts("add.js"); |
Getting Started With WebAssembly