WebAssembly has a succinct and descriptive definition on the official site, but it's only a piece of the puzzle. There are several other components that fall under the umbrella of WebAssembly. Understanding the role each component plays will give you a better understanding of the technology as a whole. In this section, we will provide a detailed breakdown of WebAssembly's definition and describe potential use cases.
What exactly is WebAssembly and where can I use it?
Official definition
The official WebAssembly website (https://webassembly.org) offers this definition:
Let's break that definition down into parts to add some clarification.
Binary instruction format
WebAssembly actually encompasses several elements—a binary format and text format, which are documented in the Core Specification, the corresponding APIs (JavaScript and web), and a compilation target. The binary and text format both map to a common structure in the form of an abstract syntax. To better understand abstract syntax, it can be explained in the context of an abstract syntax tree (AST). An AST is a tree representation of the structure of source code for a programming language. Tools such as ESLint use JavaScript's AST to find linting errors. The following example contains a function and the corresponding AST for JavaScript (taken from https://astexplorer.net).
A simple JavaScript function follows:
function doStuff(thingToDo) {
console.log(thingToDo);
}
The corresponding AST is as follows:
{
"type": "Program",
"start": 0,
"end": 57,
"body": [
{
"type": "FunctionDeclaration",
"start": 9,
"end": 16,
"id": {
"type": "Identifier",
"start": 17,
"end": 26,
"name": "doStuff"
},
"generator": false,
"expression": false,
"params": [
{
"type": "Identifier",
"start": 28,
"end": 57,
"name": "thingToDo"
}
],
"body": {
"type": "BlockStatement",
"start": 32,
"end": 55,
"body": [
{
"type": "ExpressionStatement",
"start": 32,
"end": 55,
"expression": {
"type": "CallExpression",
"start": 32,
"end": 54,
"callee": {
"type": "MemberExpression",
"start": 32,
"end": 43,
"object": {
"type": "Identifier",
"start": 32,
"end": 39,
"name": "console"
},
"property": {
"type": "Identifier",
"start": 40,
"end": 43,
"name": "log"
},
"computed": false
},
"arguments": [
{
"type": "Identifier",
"start": 44,
"end": 53,
"name": "thingToDo"
}
]
}
}
]
}
}
],
"sourceType": "module"
}
An AST may be verbose, but it does an excellent job at describing the components of a program. Representing source code in an AST makes verification and compilation simple and efficient. WebAssembly code in text format is serialized into an AST and compiled to the binary format (as a .wasm file), which is fetched, loaded, and utilized by a web page. When the module is loaded, the browser's JavaScript engine utilizes a decoding stack to decode the .wasm file into an AST, perform type checking, and interpret it to execute functions. WebAssembly started as a binary instruction format for an AST. Due to the performance implications of verifying Wasm expressions that return void, the binary instruction format was updated to target a stack machine.
A stack machine consists of two elements: a stack and instructions. A stack is a data structure with two operations: push and pop. Items are pushed onto the stack and subsequently popped from the stack in last in, first out (LIFO) order. A stack also includes a pointer, which points to the item at the top of the stack. Instructions represent actions to perform on the items in the stack. For example, an ADD instruction might pop the top two items from the stack (the values 100 and 10), and push a single item with the sum back onto the stack (the value 110):
WebAssembly's stack machine operates in the same way. A program counter (pointer) maintains the execution position within the code and a virtual control stack keeps track of blocks and if constructs as they are entered (pushed) and exited (popped). The instructions are executed with no reference to an AST. Thus, the binary instruction format portion of the definition refers to a binary representation of instructions that are in a format readable by the decoding stack in the browser.
Portable target for compilation
WebAssembly was designed from the beginning with portability in mind. Portability in this context means that WebAssembly's binary format can be executed efficiently on a variety of operating systems and instruction set architectures, on and off the web. The specification for WebAssembly defines portability in the context of an execution environment. WebAssembly was designed to run efficiently in environments that meet certain characteristics, most of which are related to memory. WebAssembly's portability can also be attributed to the absence of a specific API around the core technologies. Instead, it defines an import mechanism where the set of available imports is defined by the host environment.
In a nutshell, this means that WebAssembly isn't tied to a specific environment, such as the web or desktop. The WebAssembly Working Group has defined a Web API, but that's separate from the Core Specification. The Web API caters to WebAssembly, not the other way around.
The compilation aspect of the definition indicates that WebAssembly will be simple to compile down to its binary format from source code written in high-level languages. The MVP focuses on two languages, C and C++, but Rust can also be used given its similarities to C++. Compilation will be achieved through the use of a Clang/LLVM backend, although we'll be using Emscripten in this book to generate our Wasm modules. The plan is to eventually add support for other languages and compilers (such as GCC), but the MVP is focused on LLVM.
The core specification
The official definition gives some high-level insight into the overall technology, but for the sake of completeness, it's worth digging a little deeper. WebAssembly's Core Specification is the official document to reference if you want to understand WebAssembly at a very granular level. If you're interested in learning about the characteristics of the runtime structure with regard to the execution environment, check out section 4: Execution. We won't cover that here, but understanding where the Core Specification fits in will help in establishing a complete definition of WebAssembly.
Language concepts
The Core Specification states WebAssembly encodes a low-level, assembly-like programming language. The specification defines the structure, execution, and validation of this language as well as the details of the binary and text formats. The language itself is structured around the following concepts:
- Values, or rather value types that WebAssembly provides
- Instructions that are executed within the stack machine
- Traps produced under error conditions and abort execution
- Functions into which code is organized, each of which takes a sequence of values as parameters and returns a sequence of values as a result
- Tables, which are arrays of values of a particular element type (such as function references) that are selectable by the executing program
- Linear Memory, which is an array of raw bytes that can be used to store and load values
- Modules, WebAssembly binary (.wasm file) that contains function, tables, and linear memories
- Embedder, the mechanism by which WebAssembly can be executed in a host environment, such as a web browser
Functions, tables, memory, and modules have direct correlations with the JavaScript API and are important to be aware of. These concepts describe the underlying structure of the language itself and how to write or encode WebAssembly. With regard to usage, understanding the corresponding semantic phases of WebAssembly provides a complete definition of the technology:
Semantic phases
The Core Specification describes the different phases an encoded module (.wasm file) undergoes when it is being utilized in a host environment (such as a web browser). This aspect of the specification represents how the output is handled and executed:
- Decoding: The binary format is converted into a module
- Validation: The decoded module undergoes validation checks (such as type checking) to ensure the module is well formed and safe
- Execution, Part 1: Instantiation: A module instance, which is the dynamic representation of the module, is instantiated by initializing the Globals, Memories, and Tables, and invokes the module's start() function
- Execution, Part 2: Invocation: Exported functions are called from the module instance:
The following diagram provides a visual representation of the semantic phases:
The JavaScript and Web APIs
The WebAssembly Working Group also released API specifications for interacting with JavaScript and the web, which qualifies them for inclusion in the WebAssembly technology space. The JavaScript API is scoped to the JavaScript language itself, without being specifically tied to an environment (for example, web browsers or Node.js). It defines classes, methods, and objects for interacting with WebAssembly and managing the compilation and instantiation processes. The Web API is an extension of the JavaScript API that defines functionality specific to web browsers. The Web API specification currently only defines two methods, compileStreaming and instantiateStreaming, which are convenience methods that simplify the use of Wasm modules in the browser. These will be covered in greater detail in Chapter 2, Elements of WebAssembly - Wat, Wasm, and the JavaScript API.
So will it replace JavaScript?
WebAssembly's ultimate goal is not to replace JavaScript, but rather to complement it. JavaScript's rich ecosystem and flexibility still makes it the ideal language for the web. WebAssembly's JavaScript API makes interoperability between the two technologies relatively simple. So will you be able to build a web application using just WebAssembly? One of the explicit goals of WebAssembly is portability, and replicating all of JavaScript's functionality could inhibit that goal. However, the official site includes a goal to execute and integrate well with the existing web platform, so only time will tell. It may not be practical to write the entire code base in a language that compiles down to WebAssembly, but moving some of the application logic to Wasm modules could be beneficial in terms of performance and load times.
Where can I use it?
WebAssembly's official site has an extensive list of potential use cases. I'm not going to cover them all here, but there are several that represent significant enhancements to the capabilities of the web platform:
- Image/video editing
- Games
- Music applications (streaming, caching)
- Image recognition
- Live video augmentation
- VR and augmented reality
Although some of these use cases are technically feasible with JavaScript, HTML, and CSS, using WebAssembly can offer significant performance gains. Serving up a binary file (instead of a single JavaScript file) can greatly reduce the bundle size, and instantiating the Wasm module on page load speeds up code execution.
WebAssembly isn't just limited to the browser. Outside the browser, you could use it to build hybrid native apps on mobile devices or perform server-side computations of untrusted code. Using Wasm modules for phone apps could be incredibly beneficial in terms of power usage and performance.
WebAssembly also offers flexibility with regard to how it can be used. You can write your entire code base in WebAssembly, although this may not be practical in its current form or in the context of a web application. Given WebAssembly's robust JavaScript API, you could write the UI in JavaScript/HTML and use Wasm modules for functionality that doesn't directly access the DOM. Once additional languages are supported, objects can be easily passed between the Wasm module and JavaScript code, which will greatly simplify integration and increase developer adoption.