top of page
Writer's pictureThe Tech Platform

WebAssembly Module in JavaScript: A Complete Guide

WebAssembly, abbreviated as wasm, is an emerging technology that empowers web developers to bring near-native performance to web applications. This binary instruction format, designed as a portable target for the compilation of high-level programming languages like C, C++, and Rust, offers a solution to the performance bottlenecks traditionally associated with JavaScript.


In this comprehensive guide, we will explore the WebAssembly modules in JavaScript. We'll take you through the essential concepts, from creating, importing, and exporting WebAssembly modules to interfacing with them in your JavaScript code.


Table of Contents:

What is WebAssembly?

A WebAssembly module is a binary file that contains WebAssembly code. WebAssembly is a low-level, portable code format that can be executed on a variety of platforms, including the web, servers, and mobile devices.


WebAssembly modules can be used in two common ways:

  1. Use a WebAssembly compiler to compile code written in a different language, such as C or C++, to WebAssembly. This can be useful for improving the performance of JavaScript applications, or for porting existing code to the web.

  2. Use WebAssembly modules to use a pre-compiled WebAssembly module that provides a specific functionality. For example, there are WebAssembly modules that provide high-performance implementations of machine learning algorithms, video codecs, and other computationally intensive tasks.

Here are some examples of how WebAssembly modules can be used in JavaScript:

  • Machine learning: WebAssembly modules can be used to implement high-performance machine learning models in JavaScript applications.

  • Video encoding and decoding: WebAssembly modules can be used to implement high-performance video encoders and decoders in JavaScript applications.

  • Game development: WebAssembly modules can be used to develop high-performance games in JavaScript.

  • Scientific computing: WebAssembly modules can be used to implement high-performance scientific computing algorithms in JavaScript applications.


Structure of WebAssembly Module in JavaScript

The structure of a WebAssembly module is defined by the WebAssembly binary format specification. The specification defines a number of sections that can be included in a WebAssembly module, including:

  • Header: The header section contains information about the module, such as its version and the languages that it uses.

  • Types: The types section defines the types of data that the module uses.

  • Functions: The functions section defines the functions that the module implements.

  • Tables: The tables section defines the tables that the module uses.

  • Memories: The memories section defines the memories that the module uses.

  • Globals: The globals section defines the global variables that the module uses.

  • Exports: The exports section defines the functions, tables, memories, and global variables that are exported from the module.

Each section of a WebAssembly module is made up of a series of elements. Each element has a type and a value. The type of an element determines the structure of the element's value.


For example, the following code shows the structure of a WebAssembly module that defines a single function:

(module   
    (header "WASM" 1)   
    (type $my_function (func))   
    (func $my_function (param i32) (result i32)     
        (i32.add (local.get 0) (i32.const 1))   
    )   
    (export "my_function" (func $my_function)) 
)

This code defines a simple WebAssembly module with a single function, my_function. The header section contains the version of the module (1) and the magic string "WASM". The types section defines a single type, $my_function, which is a function that takes an integer as input and returns an integer as output. The functions section defines the my_function function. The exports section exports the my_function function from the module.


The other sections of a WebAssembly module can be used to implement more complex functionality. For example, the tables section can be used to implement function pointers, and the memories section can be used to store large amounts of data.


Here is an example of a WebAssembly module with a table:

(module   
    (header "WASM" 1)   
    (table $my_table 10 (funcref))   
    (func $my_function (param i32) (result i32)     
        (table.get $my_table (local.get 0))   
    )   
    (export "my_function" (func $my_function)) 
) 

This module defines a table called my_table with 10 entries. It also defines a function called my_function, which takes an integer as input and returns the function pointer at the specified index in the my_table table.


Here is an example of a WebAssembly module with a memory:

(module   
    (header "WASM" 1)   
    (memory $my_memory 1024)   
    (func $my_function (param i32) (result i32)     
        (i32.load (local.get 0))   
    )   
    (export "my_function" (func $my_function)) 
) 

This module defines a memory called my_memory with 1024 bytes of storage. It also defines a function called my_function, which takes an integer as input and returns the value stored at the specified address in the my_memory memory.


Creating WebAssembly Module

Here's a step-by-step guide and code example to create and use a simple WebAssembly module in JavaScript.


STEP 1: Write C/C++ Code:

You first need to write the code that you want to compile into WebAssembly. For this example, we'll write a simple C function that adds two numbers.

// add.c
int add(int a, int b) 
{     
    return a + b; 
} 

STEP 2: Compile the C Code to WebAssembly:

You can use a tool like Emscripten to compile your C/C++ code to WebAssembly. Emscripten is a popular choice for this purpose. Make sure you have it installed.

emcc -o add.wasm add.c 

STEP 3: HTML and JavaScript Code:

Create an HTML file that includes JavaScript code to load and use the WebAssembly module.

<!DOCTYPE html>
<html>
<head>
    <title>WebAssembly Example</title>
</head>
<body>
    <script>
        // Function to load and use the WebAssembly module
        async function loadAndUseWasm() 
        {             
            const response = await fetch('add.wasm'); // Load the WebAssembly module
            const bytes = await response.arrayBuffer();             
            const { instance, module } = await WebAssembly.instantiate(bytes);              
            
            // Call the 'add' function from the WebAssembly module
            const result = instance.exports.add(3, 4);              
            console.log(`Result from WebAssembly: ${result}`);         
        }          
        loadAndUseWasm();     
    </script>
</body>
</html>

STEP 4: Serve Your HTML Page:

You can use a simple HTTP server to serve your HTML page, or you can use a development tool like live-server if you have it installed. Make sure your HTML file and add.wasm are in the same directory.

npx live-server 

STEP 5: View in the Browser:

Open your HTML file in a modern web browser. You should see the result printed in the browser console. You should see the output: "Result from WebAssembly: 7."


Import and Export WebAssembly Module

Importing and exporting WebAssembly modules in JavaScript involves using the WebAssembly APIs to load and work with WebAssembly modules and instances.


Below is a code example that demonstrates how to import and export WebAssembly modules using the provided APIs:

// math.c
int add(int a, int b) {
    return a + b;
}

// Compile math.c to WebAssembly
// emcc -o math.wasm math.c

// Importing a WebAssembly Module
(async () => {
    try {
        // Load the WebAssembly module from a binary file
        const response = await fetch('math.wasm');
        const bytes = await response.arrayBuffer();

        // Instantiate the WebAssembly module
        const { instance, module } = await WebAssembly.instantiate(bytes);

        // Get the exported functions, tables, memories, and global variables
        const exports = WebAssembly.Module.exports(module);

        // You can access the exported functions by their names
        const addFunction = exports.add;
        const result = addFunction(5, 3);
        console.log(`Result from add function: ${result}`);

        // Alternatively, you can use WebAssembly.Instance.exports
        const instanceExports = WebAssembly.Instance.exports(instance);
        const addFunction2 = instanceExports.add;
        const result2 = addFunction2(10, 2);
        console.log(`Result from add function via instance: ${result2}`);
    } catch (error) {
        console.error(error);
    }
})();

In this code example:

  1. We start by defining a simple C function (add) in a file called math.c and compile it to WebAssembly using Emscripten.

  2. We then use the fetch API to load the WebAssembly binary file (math.wasm) and convert it to an array buffer.

  3. The WebAssembly.instantiate function is used to instantiate the WebAssembly module, providing us with an instance and module object.

  4. We access the exported functions, tables, memories, and global variables using WebAssembly.Module.exports(module) or WebAssembly.Instance.exports(instance).

  5. We demonstrate two ways to call the exported add function: directly from the exports object and via the instanceExports object. Both methods produce the same result.

This example shows how to import and use a WebAssembly module in JavaScript, accessing its exported functions and using them to perform operations.


Advantages:

Here are some of the advantages of using WebAssembly modules in JavaScript:

  • Performance: WebAssembly modules can be much faster than JavaScript code, especially for computationally intensive tasks.

  • Portability: WebAssembly modules can be executed on a variety of platforms, including the web, servers, and mobile devices.

  • Interoperability: WebAssembly modules can be easily integrated with JavaScript code.

Disadvantages:

Below are the disadvantages of using WebAssembly modules in JavaScript:

  • Complexity: WebAssembly is a low-level language, and it can be difficult to learn and use.

  • Debuggability: Debugging WebAssembly code can be more difficult than debugging JavaScript code.

  • Tooling: There are fewer tools available for developing and debugging WebAssembly code than there are for JavaScript code.


Conclusion

In this complete guide, we've explored the transformative power of WebAssembly in the world of web development. From its inception to its implementation in modern browsers, we've demystified the core concepts and benefits that this technology brings to the table.


We've seen how to create and structure a WebAssembly module, harnessing its efficiency for computationally intensive tasks, and integrating it seamlessly with JavaScript. The ability to import and export functions, tables, memories, and global variables bridges the gap between two worlds, unlocking new possibilities for web applications.

Comments


bottom of page