Introduction
WebAssembly (Wasm) has evolved beyond the browser to become a universal runtime for high-performance applications. From serverless functions to embedded systems, Wasm provides a safe, portable, and efficient execution environment. This comprehensive guide covers Wasm fundamentals, development workflows, and building production applications.
What is WebAssembly?
The Wasm Promise
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WebAssembly Value Proposition โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โก Near-Native Performance โ
โ - Compiled binary format โ
โ - Linear memory model โ
โ - Predictable execution time โ
โ โ
โ ๐ Safe Execution โ
โ - Memory-safe by design โ
โ - Sandboxed execution โ
โ - No unsafe operations โ
โ โ
โ ๐ Portable โ
โ - Works in browser, server, edge โ
โ - Cross-platform binaries โ
โ - Language-agnostic โ
โ โ
โ ๐ฆ Compact โ
โ - Small binary size โ
โ - Fast loading โ
โ - Efficient parsing โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Developing with Rust
Setup and Compilation
# Install Rust and wasm-pack
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install wasm-pack
# Create new library
cargo new --lib hello-wasm
# Edit Cargo.toml
[package]
name = "hello-wasm"
version = "0.1.0"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
# Build for web
wasm-pack build --target web
Rust to Wasm
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[wasm_bindgen]
pub struct Counter {
count: u32,
}
#[wasm_bindgen]
impl Counter {
pub fn new() -> Self {
Self { count: 0 }
}
pub fn increment(&mut self) {
self.count += 1;
}
pub fn get(&self) -> u32 {
self.count
}
}
Memory Management
use wasm_bindgen::prelude::*;
use std::slice;
use std::str;
#[wasm_bindgen]
pub fn process_string(ptr: *const u8, len: usize) -> *mut u8 {
// Safe access to linear memory
let data = unsafe {
slice::from_raw_parts(ptr, len)
};
// Process data
let string = str::from_utf8(data).unwrap();
let processed = string.to_uppercase();
// Allocate and return
let mut result = processed.into_bytes();
let ptr = result.as_mut_ptr();
// Leak memory to return pointer (caller must free)
std::mem::forget(result);
ptr
}
#[wasm_bindgen]
pub fn free_memory(ptr: *mut u8, len: usize) {
unsafe {
Vec::from_raw_parts(ptr, len, len);
}
}
WebAssembly System Interface (WASI)
WASI Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WASI Architecture โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ WebAssembly Module โ โ
โ โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ โ
โ โ โ WASI โ โ App โ โ โ โ โ
โ โ โ Core โ โ Logic โ โ โ โ โ
โ โ โโโโโโฌโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ โ
โ โโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโผโโโโโโโโ โ
โ โ WASI โ โ
โ โ Preview 2 โ โ
โ โ โ โ
โ โ - Filesystem โ โ
โ โ - Network โ โ
โ โ - Clock โ โ
โ โ - Random โ โ
โ โโโโโโโโโฌโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโผโโโโโโโโ โ
โ โ Runtime โ โ
โ โ (Wasmtime) โ โ
โ โโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
WASI Implementation
// wasi-demo/src/lib.rs
use std::fs;
use std::io::Write;
#[no_mangle]
pub fn process_file(input_path: &str, output_path: &str) -> Result<(), i32> {
// Read file using WASI
let contents = fs::read_to_string(input_path)
.map_err(|_| 1)?;
// Process
let processed = process_contents(&contents);
// Write output
let mut file = fs::File::create(output_path)
.map_err(|_| 2)?;
file.write_all(processed.as_bytes())
.map_err(|_| 3)?;
Ok(())
}
fn process_contents(contents: &str) -> String {
// Processing logic
contents.to_uppercase()
}
// Run with: wasi-run target/wasm32-wasi/debug/wasi-demo.wasm
WASI Runners
# Install Wasmtime
curl https://wasmtime.dev/install.sh -sSL | bash
# Run WASI module
wasmtime run --dir=. myprogram.wasm
# Wasmtime as library
cargo add wasmtime
Browser Integration
JavaScript Interop
// Loading WASM in browser
async function init() {
const wasm = await WebAssembly.instantiateStreaming(
fetch('my-module.wasm'),
importObject
);
// Call exported functions
const result = wasm.instance.exports.add(1, 2);
console.log(result); // 3
// Use exported memory
const memory = wasm.instance.exports.memory;
const view = new Uint32Array(memory.buffer);
console.log(view[0]);
}
// Using wasm-bindgen generated glue code
import init, { greet, Counter } from './hello_wasm';
await init();
greet("World"); // "Hello, World!"
const counter = Counter.new();
counter.increment();
counter.increment();
console.log(counter.get()); // 2
High-Performance Processing
// Image processing with WASM
async function processImage(imageData) {
const wasm = await initWasm();
// Get pointer to WASM memory
const inputPtr = wasm.get_input_buffer();
const outputPtr = wasm.get_output_buffer();
// Copy image data to WASM memory
const wasmMemory = new Uint8ClampedArray(wasm.memory.buffer);
wasmMemory.set(imageData, inputPtr);
// Process
wasm.process_image(inputPtr, outputPtr, width, height);
// Get results
return wasmMemory.slice(outputPtr, outputPtr + width * height * 4);
}
Server-Side Wasm
Wasm on the Server
// wasm-server/src/main.rs
use std::net::TcpListener;
use std::io::Read;
fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
for stream in listener.incoming() {
let mut stream = stream.unwrap();
let mut buffer = [0u8; 1024];
stream.read(&mut buffer).unwrap();
// Process with WASM
let result = process_request(&buffer);
stream.write(&result).unwrap();
}
}
fn process_request(request: &[u8]) -> Vec<u8> {
// Call WASM module
unsafe {
// ...
}
}
WASM in Containers
# Dockerfile for WASM application
FROM scratch
# Add WASM runtime
ADD wasmtime /usr/local/bin/
# Add WASM module
ADD app.wasm /app.wasm
# Run
CMD ["wasmtime", "/app.wasm"]
Use Cases
1. High-Performance Web Apps
// Image processing library
#[wasm_bindgen]
pub fn apply_filter(image_data: &mut [u8], filter: &str) {
match filter {
"blur" => apply_blur(image_data),
"sharpen" => apply_sharpen(image_data),
"grayscale" => apply_grayscale(image_data),
_ => {}
}
}
2. Serverless Functions
// Serverless handler
#[no_mangle]
pub fn handle_request(ptr: *const u8, len: usize) -> *mut u8 {
let request = unsafe {
std::str::from_utf8_unchecked(slice::from_raw_parts(ptr, len))
};
let response = process(request);
let mut vec = response.into_bytes();
let ret_ptr = vec.as_mut_ptr();
std::mem::forget(vec);
ret_ptr
}
3. Plugin Systems
// Plugin host
fn load_plugin(path: &str) -> Result<CompiledPlugin> {
let bytes = std::fs::read(path)?;
let module = Module::new(&bytes)?;
let instance = Instance::new(&module, &imports)?;
Ok(CompiledPlugin { instance })
}
// Plugin interface
trait Plugin {
fn process(&self, input: &[u8]) -> Vec<u8>;
}
Performance Optimization
Compilation Best Practices
# Cargo.toml optimization
[profile.release]
opt-level = 3 # Maximum optimization
lto = true # Link-time optimization
codegen-units = 1 # Better optimization
strip = true # Remove debug symbols
Memory Optimization
// Use smaller types
#[wasm_bindgen]
pub fn process(data: &[u8]) -> Vec<u8> {
// Use u8 instead of u32 where possible
// Reuse buffers
let mut buffer = Vec::with_capacity(data.len());
// Work in-place
// ...
buffer
}
Tooling Ecosystem
Development Tools
| Tool | Purpose |
|---|---|
| wasm-pack | Rust โ Wasm toolchain |
| wasm-bindgen | JS/Rust interop |
| Wasmtime | Fast WASI runtime |
| wasmer | Universal WASM runtime |
| wasm-opt | Binary optimization |
Best Practices
1. Minimize Boundary Crossings
// โ Multiple calls across boundary
for item in large_array.iter() {
wasm.process_item(item); // Expensive!
}
// โ
Batch processing
wasm.process_batch(&large_array); // Single call!
2. Use Appropriate Data Types
// โ Passing complex structures
#[wasm_bindgen]
pub fn process(item: MyComplexStruct) { }
// โ
Use simple types
#[wasm_bindgen]
pub fn process_data(ptr: *const u8, len: usize) { }
3. Handle Errors Gracefully
#[wasm_bindgen]
pub fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("Division by zero".into())
} else {
Ok(a / b)
}
}
Conclusion
WebAssembly provides a powerful, portable runtime. Key takeaways:
- Near-native performance: Compile once, run anywhere
- Safe execution: Sandboxed by default
- Universal runtime: Browser, server, edge, embedded
- WASI: Standardized system interface
Wasm is transforming computing from a browser technology to a universal runtime.
Comments