Skip to content
micro-frontend.dev 2.0

Compiling and running WebAssembly code

Author: Natalia Venditto

Date First Published: Wed Jan 04 2023

Where does WebAssembly code run?

WebAssembly code runs on top of the JavaScript engine in place, that could be V8, SpiderMonkey, etc.

The main difference is, that because the code is binary and it doesn’t need to be interpreted by the engine, it is much faster in execution time, than JavaScript. How fast? It will depend on many factors, including the system resources or capacity -don’t forget we spoke about the fact that those may greatly vary per user, in our architecting for the user UX section, but the claims are that it may execute at near native speed.

WebAssembly Code Examples

I know you’re itching to see some code, already. But I won’t show you the binary, first, because the binary won’t tell you much. Also the actuall binary output will depend on the compiler.

Let’s see what the Web Assembly text instruction would look like, if we wanted to write a program that was called hello_world that would print to the screen: “I love to compose micro frontends in the cloud”.

  (module 
      (import 'env' 'println'func $println (param i32))) 
        (func $hello_world (export  'hello_world') 
          (block $block 
            (call $println 
          (i32.const 0))
        )) 
      (memory 1) 
    (data (i32.const 0)'I love to compose micro frontends in the cloud0') ))

Dissecting the textual instructions above.

In .wat, each instruction between a pair of a beginning ( and closing ), is an s-expression. We can use them to form a tree of nodes. Let’s explain the code above: the first node is a top-level construct or root node that defines the structure, in this case, the fundamental unit of code of WebAssembly: a module.

The second node, a child node, is declaring an imported function, in this case println, from an external module -and this is the beauty of it, the ability to interoperate-, env, taking a single param of type i32 -yes, integer 32-bit in this case-. When we see a $ means we are declaring a local entity, in this case, the local name of the imported function $println block declares a new block of instructions that we locally call $block and then we are invoking our imported function $println inside of this block with call. Pay attention to how we are now pushing a constant value 0 to the stack, that we will use later (i32.const 0).

The following child node, declares the memory with 1 page, equivalent to 65536 bytes.

Finally, we use the data node to stora a string constant in memory, starting at the address 0 that we previously declared, with the \00 or null terminator that represents the end of a string in WebAssembly.

    fetch("hello_world.wasm") 
     .then(response => response.arrayBuffer()) 
     .then(bytes => { 
      const wasmModule = WebAssembly.compile(bytes); 
      const instance = new WebAssembly.Instance(wasmModule, { 
        env: { 
        println: console.log 
      } 
     }); 
     instance.exports.hello_world(); 
    });
  

Do JavaScript developers write binary WebAssembly binary code?

Short answer is: No. I don’t think so. At least I wouldn’t. What developers may write, is WebAssembly Text like the one we just dissected, which is a more human-readable format and is saved in files of extension ´.wat´, that can then be compiled to ´.wasm´.

Additionally, you need to learn the JavaScript WebAssembly object interface and all the methods it implements, and do it in a way that is clean and efficient.

I still don’t recommend you write WebAssembly text or ´.wat´files, yourself, but use a language you’re familiar with that can compile to WebAssembly, like Rust, C, C++, Go, etc.