WebAssembly
Applications that run within a browser (instead of from a hard drive) are becoming more and more common. Much like office software developers like Microsoft (Microsoft 365) and Google (Docs and Sheets), which add new functions to their packages all the time, browser-based games are becoming increasingly complex and are using more resources. In many cases, these web applications are written using JavaScript. More recently, however, a growing number of developers are turning to WebAssembly – a new approach with brilliant results.
What is WebAssembly?
WebAssembly (Wasm) provides web developers with a new way of making applications available online. Previously, the only option was JavaScript. The problem is that JavaScript runs relatively slowly and performance can be a struggle in certain situations. Thus, the World Wide Web Consortium (W3C) came up with a new method – WebAssembly. In order for Wasm to function, the browser used must be able to handle the language. Mozilla (Firefox), Microsoft (Edge), Apple (Safari) and Google (Chrome) have all been involved in the development process. WebAssembly applications are compatible with all of the latest browser versions by these developers.
To experience the power of WebAssembly for yourself, try your hand at Funky Karts. The game was originally developed as a mobile app, but has been compiled to WebAssembly so that it can be played in a browser. The developer wrote an interesting blog post about the project, giving a step-by-step description of how he went about the compilation process.
Theoretically speaking, WebAssembly is represented in bytecode. You can think of it as a middle ground between machine code, which can only be understood by a computer, and a typical programming language, which is understood by humans but only after it has been compiled. This is what makes WebAssembly so fast in comparison to other languages – computers can compile the code with virtually no effort. Indeed, writing in bytecode is somewhat different. The advantage of Wasm is that you don’t have to work in this programming language yourself. In practice, developers write their applications using C or C++, for example.
The source text is then compiled using the Emscripten application. This tool existed long before WebAssembly and was used to compile C/C++ code into JavaScript (or ams.js). It can now also be used to convert code to Wasm. This means the code is pre-compiled, so that it doesn't have to be compiled or interpreted when the program is run. When a user opens the application in a browser, a small virtual machine starts up and runs the application.
Advantages of WebAssembly
WebAssembly currently only has one real disadvantage: it isn’t catching on fast enough. Web developers are used to working with JavaScript and there are no plans to replace JavaScript. The project managers have stated very clearly that they want to promote Wasm as an alternative to JavaScript. However, thanks to the support of the major browser providers and the W3C, the popularity of WebAssembly is growing. This is also helped by the fact that website visitors don’t actually have to do anything themselves – web applications using WebAssembly load just as easily as JavaScript code, only more quickly.
Developers who are already familiar with languages such as C, C++ or Rust can now write programs directly for the web. This choice of programming language also opens up more design possibilities. If you cannot find the necessary JavaScript libraries or frameworks for your program, there are other ways to make it work. Here are some of the reasons why developers should take a closer look at WebAssembly:
- W3C open web standard
- High performance and small file sizes
- Perfect for mobile browsing
- Even virtual reality programs can theoretically run in a browser
- No need to learn a new programming language
- C, C++ and Rust can now all be used to program web applications
- Supported by all of the major browser manufacturers
- No limitations for users
WebAssembly practical examples
Developers don’t actually need to write their programs in WebAssembly. One of the major advantages of the solution is that programmers can use any language such as C and later transfer their code into Wasm format. Nevertheless, it makes sense to learn more about the compiled code to uncover how WebAssembly works.
The source code is available in two versions: WebAssembly Text Format (WAT) and WebAssembly Binary Format (Wasm). The latter is the actual code a machine would run. Because Wasm is mostly binary code and difficult to understand, it is more or less unusable for human analysis. Thus, the WAT sub-form has been developed. This type of code uses readable expressions and can be examined by a programmer. However, it doesn’t provide the same programming comfort more established programming languages provide.
Let’s look at an example using a simple line of code written in C:
#define WASM_EXPORT __attribute__((visibility("default")))
WASM_EXPORT
int main() {
return 1;
}
The same code in WAT format is significantly longer:
(module
(type $t0 (func))
(type $t1 (func (result i32)))
(func $__wasm_call_ctors (type $t0))
(func $main (export "main") (type $t1) (result i32)
i32.const 1)
(table $T0 1 1 anyfunc)
(memory $memory (export "memory") 2)
(global $g0 (mut i32) (i32.const 66560))
(global $__heap_base (export "__heap_base") i32 (i32.const 66560))
(global $__data_end (export "__data_end") i32 (i32.const 1024)))
Although readability is diminished, certain elements can still be deciphered. WebAssembly puts everything into modules which are further classified into functions that can be specified using parameters. In the code above, we can make out five different elements:
- module: main unit in WebAssembly
- function: grouping within a module
- memory: array with bytes
- global: a value that can be used across multiple modules
- table: reference storage
Once the code is translated into binary code, it becomes unreadable:
0000000: 0061 736d ; WASM_BINARY_MAGIC
0000004: 0100 0000 ; WASM_BINARY_VERSION
; section "Type" (1)
0000008: 01 ; section code
0000009: 00 ; section size (guess)
000000a: 02 ; num types
; type 0
000000b: 60 ; func
000000c: 00 ; num params
000000d: 00 ; num results
; type 1
000000e: 60 ; func
000000f: 00 ; num params
0000010: 01 ; num results
0000011: 7f ; i32
0000009: 08 ; FIXUP section size
; section "Function" (3)
0000012: 03 ; section code
0000013: 00 ; section size (guess)
0000014: 02 ; num functions
0000015: 00 ; function 0 signature index
0000016: 01 ; function 1 signature index
0000013: 03 ; FIXUP section size
; section "Table" (4)
0000017: 04 ; section code
0000018: 00 ; section size (guess)
0000019: 01 ; num tables
; table 0
000001a: 70 ; funcref
000001b: 01 ; limits: flags
000001c: 01 ; limits: initial
000001d: 01 ; limits: max
0000018: 05 ; FIXUP section size
; section "Memory" (5)
000001e: 05 ; section code
000001f: 00 ; section size (guess)
0000020: 01 ; num memories
; memory 0
0000021: 00 ; limits: flags
0000022: 02 ; limits: initial
000001f: 03 ; FIXUP section size
; section "Global" (6)
0000023: 06 ; section code
0000024: 00 ; section size (guess)
0000025: 03 ; num globals
0000026: 7f ; i32
0000027: 01 ; global mutability
0000028: 41 ; i32.const
0000029: 8088 04 ; i32 literal
000002c: 0b ; end
000002d: 7f ; i32
000002e: 00 ; global mutability
000002f: 41 ; i32.const
0000030: 8088 04 ; i32 literal
0000033: 0b ; end
0000034: 7f ; i32
0000035: 00 ; global mutability
0000036: 41 ; i32.const
0000037: 8008 ; i32 literal
0000039: 0b ; end
0000024: 15 ; FIXUP section size
; section "Export" (7)
000003a: 07 ; section code
000003b: 00 ; section size (guess)
000003c: 04 ; num exports
000003d: 04 ; string length
000003e: 6d61 696e main ; export name
0000042: 00 ; export kind
0000043: 01 ; export func index
0000044: 06 ; string length
0000045: 6d65 6d6f 7279 memory ; export name
000004b: 02 ; export kind
000004c: 00 ; export memory index
000004d: 0b ; string length
000004e: 5f5f 6865 6170 5f62 6173 65 __heap_base ; export name
0000059: 03 ; export kind
000005a: 01 ; export global index
000005b: 0a ; string length
000005c: 5f5f 6461 7461 5f65 6e64 __data_end ; export name
0000066: 03 ; export kind
0000067: 02 ; export global index
000003b: 2c ; FIXUP section size
; section "Code" (10)
0000068: 0a ; section code
0000069: 00 ; section size (guess)
000006a: 02 ; num functions
; function body 0
000006b: 00 ; func body size (guess)
000006c: 00 ; local decl count
000006d: 0b ; end
000006b: 02 ; FIXUP func body size
; function body 1
000006e: 00 ; func body size (guess)
000006f: 00 ; local decl count
0000070: 41 ; i32.const
0000071: 01 ; i32 literal
0000072: 0b ; end
000006e: 04 ; FIXUP func body size
0000069: 09 ; FIXUP section size
; section "name"
0000073: 00 ; section code
0000074: 00 ; section size (guess)
0000075: 04 ; string length
0000076: 6e61 6d65 name ; custom section name
000007a: 01 ; function name type
000007b: 00 ; subsection size (guess)
000007c: 02 ; num functions
000007d: 00 ; function index
000007e: 11 ; string length
000007f: 5f5f 7761 736d 5f63 616c 6c5f 6374 6f72 __wasm_call_ctor
000008f: 73 s ; func name 0
0000090: 01 ; function index
0000091: 04 ; string length
0000092: 6d61 696e main ; func name 1
000007b: 1a ; FIXUP subsection size
0000096: 02 ; local name type
0000097: 00 ; subsection size (guess)
0000098: 02 ; num functions
0000099: 00 ; function index
000009a: 00 ; num locals
000009b: 01 ; function index
000009c: 00 ; num locals
0000097: 05 ; FIXUP subsection size
0000074: 28 ; FIXUP section size
If you’re keen to give WebAssembly a go, check out the WebAssembly Studio – an online development environment for Wasm.