What is Wasm or WebAssembly ? Learn WebAssembly Basics with Rust Part A

  • Updated : 02-07-2024 20:19
  • By : Phosphataz

  • This is a series of Articles about learning WebAssembly with Rust language.


    Why WebAssembly ?


    Before WebAssembly there have been many precursors :

    • - google's native client (Nacl) technology, to provide a near native performance to code running in the browser. This project was discontinued for various reason.
    • - asm.js started as bringing gaming in the browser, it was a Mozilla project, and expanded it's objectives to running software written in other languages such as C and C++ in the browser, while maintaining native performance. The performance and the adoption of the technology was not obtained.


    So all of these attempt bring us in 2015 to webassembly, a project started by brendaneich. This project solve all the issues of it's predecessors such as portability, performance, by providing a binary code format.


    What is WebAssembly or Wasm ?


    Wasm is abbreviation of WebAssembly.

    WebAssembly is a safe, portable binary code format, design to provide near native performance to code executing in the browser. The binary code format is delivered to the browser via a .wasm file.

    WebAssembly has been standardized and now is developed by W3C webassembly group. Big companies such as google, Microsoft, Apple, joined the dance, and almost all browsers now support WebAssembly.


    What WebAssembly is not ?


    • - WebAssembly is not a programming language : Many languages such as C, C++, Rust, Python, C# and more can be compiled into wasm file, a binary code format that can run inside the browser or server.
    • - WebAssembly is not a replacement of JavaScript : If we run a wasm file in the browser, we can interact with it through browser JavaScript api.


    What is WebAssembly advantages ?


    • - One of the selling point of webassembly is performance. It runs at least faster than javascript in the browser.

    JavaScript : block everything => download js file from the web => parse it => compile into bytecode => interpret the bytecode to the machine code, or just in time compile to machine code => the machine run it. That's a lot.

    WebAssembly : wasm module (.wasm file) is delivered to the browser through fetch (no blocking for loading ) => .wasm file is as assembly file, a lower level code, it is directly compiled by the browser to machine code => and run it. And it run faster.


    • - WebAssembly is language agnostic : It's a result of compilation of language. To day multiple languages C, C++, Rust, Python etc... including JavaScript, can be compiled into wasm file. So if you are not JavaScript developer, no problem you still develop software for the web. See figure below.


    • - WebAssembly offer type checking at compile time, to this can avoid runtime errors and improve performance.


    Who uses WebAssembly ?



    How WebAssembly works ?


    1 - We write a code in a specific language Rust or C++ for example.

    2 - We use compilation tool that each language provide for compiling into wasm file :

    3 - The resulting wasm binary file (or wasm modules) can :

    • - run in any major web browser, the primary runtime of wasm. or
    • - run on the server using wasm runtime such as : Wasmetime, wasmer etc...


    4 - the wasm binary code can be converted back to text file format using wasmtowat tool.


    Example of rust based wasm app.


    In the browser (All the source is here) :

    How things may work in real world :

    • - a rust app that will be compiled into wasm module (file)
    • - the wasm module is served to the frontend through a backend (spring-boot, express.js, django etc...)
    • - the file is executed in the browser
    • - javascript can interact with that module.


    BUT we will not do that. We will simply :

    • - create a rust library that do simple stuff, it expose method to multiply 2 by any number we give to that method. Then we will compile the library to wasm module or binary file.
    • - create an index.html file with JavaScript code inside. This JavaScript will fetch the wasm module. And access to the method exposed by rust library from JavaScript.


    1 - Install necessary tools :

    cargo install wasm-pack
    


    • - create a rust program, a library :
    cargo new --lib calc-wa
    


    2 - code :

    • - the lib.rs file :
    use wasm_bindgen::prelude::*;
    
    
    #[wasm_bindgen]
    pub fn multBy2(num: usize) -> usize {
        num * 2
    }
    


    • - the cargo.toml file :
    [package]
    name = "calc-wa"
    version = "0.1.0"
    edition = "2021"
    
    [lib]
    crate-type = ["cdylib"]
    
    [dependencies]
    wasm-bindgen = "*"
    


    • - we compile it to wasm :

    Go inside the calc-wa folder and run this command :

    wasm-pack build --target web
    

    This will generate a pkg folder, containing files, including calc_wa_bg.wasm file.


    • - frontend code :

    Create index.html file inside the pkg folder. And copy this code inside :

    <!doctype html>
    <html lang="en-US">
      <head>
        <meta charset="utf-8" />
        <title>hello-wasm example</title>
    
        <style>
            *{
                margin: 0;
                box-sizing: border-box;
            }
            #container{
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                background-color: whitesmoke;
                height: 100vh;
            }
            input{
                min-width: 200px;
                border: 1px solid grey;
                background-color: white;
                padding: 10px;
                border-radius: 10px;
                margin: 10px 0;
            }
            button{
                background-color: green;
                color: white;
                font-weight: bold;
                border: 1px solid white;
                padding: 10px;
                border-radius: 10px;
                outline: none;
                cursor: pointer;
            }
    
            #input{
                width: fit-content;
            }
            #output{
                width: fit-content;
                display: none;
            }
        </style>
      </head>
      <body>
        <div id="container">
            <div id="input">
                <h3>enter a number to multiply by 2 : </h3>
                <input id="userInput" type="text" required>
                <button onclick="getValue()">Calculate</button>
            </div>
            <h1 id="output">
            </div>
        </div>
    
    
        <script>
            function getValue(){
                let userInputValue = document.getElementById("userInput").value;
                let userInput = parseInt(userInputValue, 10);
                if(typeof userInput === "number" && !isNaN(userInput) ){
    
                    // WebAssembly is the api provided by the browser through javascript
                    // to work with wasm
                    // we fetch wasm module and use `instantiateStreaming` from WebAssembly api 
                    // to instanciate it
                    WebAssembly.instantiateStreaming(fetch("./calc_wa_bg.wasm"))
                    .then(({ instance }) => {
    
                       // we access to the exposed wasm method with the instance.exports
                       let result = instance.exports.mult_by2(userInput);
    
                       let output = document.getElementById("output");
                       output.innerText = result;
                       output.style.display = "block";
                       document.getElementById("userInput").value = '';
                    });
                }
            }
        
        </script>
      </body>
    </html>
    
    
    


    To run it, Just open this index.html in the browser.


    Code explanation :

    1 - we fetch the wasm file from the backend and transmit it to browser api method that is exposed for working with web assembly.

    WebAssembly.instantiateStreaming(fetch("./calc_wa_bg.wasm"))
    


    2 - we get an instance that we use to interact with the exposed wasm module.

    let result = instance.exports.mult_by2(userInput);
    


    Conclusion


    This is the end of part A of this series of articles about wasm and Rust.

    Part B

    Source code