NashTech Blog

Table of Contents
wasm introduction

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

Source: WebAssembly

WebAssembly is also designed to run alongside JavaScript, allowing both to work together. It is being developed in a W3C Community Group (CG) whose members include Mozilla, Microsoft, Google and Apple.

WebAssembly is a portable target for the compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

It doesn’t allow untrusted code to read or write memory outside of a sandbox. That alone makes it a compelling choice for application plug-ins, function-as-a-service services, smart contracts and more.

Features

  • Performance: WebAssembly is designed to maintain the performance characteristics of the web platform. WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms.
  • Compact and Efficient: WebAssembly is designed to be a compact format that is both fast to parse and fast to generate.
  • Safe and Secure: WebAssembly is designed to be a safe and secure execution environment for code on the web.
  • Open and Debuggable: WebAssembly is designed to be open and debuggable, enabling developers to easily inspect, debug, and optimize their code.

WASI (WebAssembly System Interface)

WebAssembly System Interface (WASI) is a modular system interface for WebAssembly. It is designed to be portable and secure, and to provide a standard interface for WebAssembly programs to interact with their host environments.

WASI is designed to be a portable and secure system interface for WebAssembly. It provides a standard interface for WebAssembly programs to interact with their host environments, including file I/O, networking, and other system-level functionality.

Writing Wasm

Text Format

WebAssembly text format is a human-readable representation of WebAssembly binary format. It is designed to be a more compact and easily readable alternative to the binary format.

(module
  (func $add (param i32 i32) (result i32)
    get_local 0
    get_local 1
    i32.add)
  (export "add" (func $add)))

Compile the text format to binary format using the wat2wasm tool.

wat2wasm add.wat -o add.wasm

Rust

Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety. It supports WebAssembly as a compilation target.

#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

Compile the Rust code to WebAssembly using the rustc tool.

rustc --target wasm32-unknown-unknown add.rs

Alternatively, you can use the wasm-pack tool to build and work with Rust-generated WebAssembly.

AssemblyScript

AssemblyScript is a strict and type-safe subset of TypeScript that compiles to WebAssembly. The best part is that you can write AssemblyScript code similar to TypeScript.

export function add(a: i32, b: i32): i32 {
  return a + b;
}

Compile the AssemblyScript code to WebAssembly using the asc tool.

asc add.ts -b add.wasm

Go

Go 1.11 added an experimental port to WebAssembly. Go 1.12 has improved some parts of it, with further improvements expected in Go 1.13. Go 1.21 added a new port targeting the WASI syscall API.

package main

import "C"

func add(a, b int) int {
  return a + b
}

func main() {}

Compile the Go code to WebAssembly using the go tool.

GOOS=js GOARCH=wasm go build -o add.wasm

Benchmarks

According to the this benchmark, WebAssembly is faster than JavaScript in most cases.

But which is the faster between version of WebAssembly generated from Go, Rust, and AssemblyScript?

LanguageFile-size (kb)Runtime (ms)Memory (mb)
JavaScript (JS)1.368,72055.7
AssemblyScript (AS)4.76,40521.5
Rust74.02,98221.1
Go37.09,71721.5

As you can see, Rust is the fastest, followed by AssemblyScript and Go. In any case, WebAssembly is faster than JavaScript by a large margin.

But the file-size of Rust is the largest, followed by Go and AssemblyScript. JavaScript has the smallest file-size.

Running Wasm

Browser

<!DOCTYPE html>
<html>
  <head>
    <title>WebAssembly</title>
  </head>
  <body>
    <script>
      fetch('add.wasm')
        .then(response => response.arrayBuffer())
        .then(bytes => WebAssembly.instantiate(bytes, {}))
        .then(results => {
          const { instance } = results;
          // Call the exported function from the WebAssembly module
          const add = instance.exports.add;
          console.log(add(1, 2));
        });
    </script>
  </body>
</html>

To debug WebAssembly in the browser, you can use the native WebAssembly debugging in Chrome DevTools.

Please install it by going to this link: goo.gle/wasm-debugging-extension

You also need to enable the WebAssembly debugging feature in Chrome DevTools by clicking the gear (⚙) icon in the top right corner of DevTools pane, go to the Experiments panel and tick WebAssembly Debugging: Enable DWARF support.

Node.js

Node has excellent support for WebAssembly and WASI. Yet, the latter still isn’t enabled by default, and requires command-line flags (–experimental-wasm-bigint –experimental-wasi-unstable-preview1).

import fs from 'fs';
import { WebAssembly } from 'webassembly';

const buffer = fs.readFileSync('add.wasm');
const module = new WebAssembly.Module(buffer);
const instance = new WebAssembly.Instance(module);

// Call the exported function from the WebAssembly module
const add = instance.exports.add;
console.log(add(1, 2));

Other runtimes

WebAssembly runtimes are standalone tools that can run WebAssembly modules outside of a web browser. Some popular WebAssembly runtimes include:

Wasmer: Wasmer is a standalone runtime for WebAssembly, WASI, and Emscripten by the Wasmer team.

Wasmer has the best overall support compatibility with every programming language at super-speed

Wasmtime: Wasmtime is a standalone runtime for WebAssembly, WASI, and the Component Model by the Bytecode Alliance.

Wasmtime is lightning-fast and compact, with good configurability but fewer languages supported

Wasmedge: WasmEdge is a lightweight, high-performance, and extensible WebAssembly runtime for cloud-native, edge, and decentralized applications. It powers serverless apps, embedded functions, microservices, udf, smart contracts, and IoT devices. WasmEdge is currently a CNCF (Cloud Native Computing Foundation) Sandbox project.

Bun also provides a WebAssembly runtime. Unfortunately, JavaScriptCore currently doesn’t perform as well as V8 for WebAssembly. As of today, if you’re looking for a JavaScript+WebAssembly engine, I would thus recommend V8.

It doesn’t support WASI out of the box,

Conclusion

The future of WebAssembly is bright. It is a powerful technology that can be used to build high-performance web applications and server applications. WebAssembly is designed to be fast, compact, safe, and open. It is supported by all major web browsers and is being actively developed by a number of companies and organizations.

WebAssembly definitely shines at some use cases, including:

  • Web Applications: WebAssembly can be used to run high-performance web applications.
    • Games
    • Video and image editing
    • CAD/CAM applications
    • Scientific simulations
  • Server Applications: WebAssembly can be used to run high-performance server applications.

WebAssembly can be used in a number of ways:

  • Entirely in WebAssembly
  • Alongside JavaScript
  • In a Web Worker, compute-oriented task offload.

It still not sure when WebAssembly will be the default choice for web development, but it is definitely a technology to keep an eye on.

Bonus: Leptos a Rust-based web framework

Leptos makes it easy to build applications in the most-loved programming language, combining the best paradigms of modern web development with the power of Rust.

It comes with a built-in WebAssembly runtime, allowing you to run your Rust code directly in the browser.

use leptos::*;

#[component]
fn HomePage() -> impl IntoView {
    // Creates a reactive value to update the button
    let (count, set_count) = create_signal(0);
    let on_click = move |_| set_count.update(|count| *count += 1);

    view! {
        <h1>"Welcome to Leptos!"</h1>
        <button on:click=on_click>"Click Me: " {count}</button>
    }
}

You can find a demo of Leptos here. Clone the repository, go to root directory and run:

cargo leptos watch

Now you can see the example in http://localhost:3000. Open DevTools > Network to see .wasm file loaded.

To install Leptos and Rust, please follow instructions here and here

Picture of Joey Nguyen

Joey Nguyen

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top