spinny:~/writing $ vim webassembly-wasm-complete-guide.md
1~2WebAssembly (WASM) started as a way to run C++ in the browser. In 2026, it runs everywhere - browsers, servers, edge networks, embedded devices - and powers some of the most demanding applications on the web. Figma's rendering engine, Adobe Photoshop on the web, Google Meet's video processing, and Cloudflare's edge compute platform all run on WebAssembly.3~4Chrome Platform Status puts WASM at roughly 5.5% of all Chrome page loads as of early 2026, up from 4.5% the year before. With WASM 3.0 becoming a W3C standard and WASI maturing toward 1.0, the ecosystem has reached a turning point.5~6This guide covers everything you need to know to start building with WebAssembly.7~8## What is WebAssembly?9~10WebAssembly is a binary instruction format designed as a compilation target. You write code in a high-level language (Rust, C, C++, Go, Kotlin), compile it to `.wasm`, and run it in any environment that has a WASM runtime - browsers, Node.js, Cloudflare Workers, Wasmtime, or Wasmer.11~12```mermaid13graph LR14 Rust[Rust / C / C++] --> Compiler[Compiler]15 Compiler --> WASM[.wasm Binary]16 WASM --> Browser[Browser V8/SpiderMonkey]17 WASM --> Server[Server Wasmtime]18 WASM --> Edge[Edge Cloudflare Workers]19 WASM --> Embedded[Embedded WAMR]20```21~22### How It Works23~24WASM is a **stack-based virtual machine**. Functions push and pop values on an operand stack. The host runtime (V8 in Chrome, SpiderMonkey in Firefox) JIT-compiles the WASM bytecode to native machine code, which is why performance is near-native.25~26Key characteristics:27~28- **Sandboxed execution**: WASM modules can only access resources the host explicitly grants. No filesystem, no network, no OS access unless permitted. This is fundamentally different from native code.29- **Linear memory**: a single contiguous `ArrayBuffer` shared between WASM and the host. Complex data (strings, structs) is passed by writing to memory and sharing a pointer.30- **Type-limited**: WASM natively supports only four types: `i32`, `i64`, `f32`, `f64`. Everything else (strings, arrays, objects) requires encoding through linear memory or the Component Model.31- **Portable**: the same `.wasm` binary runs on any platform with a WASM runtime, without recompilation.32~33### WASM vs JavaScript34~35WASM does not replace JavaScript. It complements it.36~37| Aspect | JavaScript | WebAssembly |38|--------|-----------|-------------|39| **Parsing** | Parse + compile at runtime | Pre-compiled binary, decode only |40| **Execution speed** | JIT-optimized, variable | Near-native, consistent |41| **Startup** | Fast for small scripts | Fast decode, predictable |42| **DOM access** | Direct | Indirect (through JS glue) |43| **Best for** | UI, DOM manipulation, event handling | CPU-intensive computation |44| **Garbage collection** | Built-in | WasmGC (new), or manual |45~46Use JavaScript for UI and DOM work. Use WASM for heavy computation: image processing, video encoding, physics simulations, cryptography, data parsing.47~48## WASM 3.0: What's New49~50WebAssembly 3.0 became a W3C standard in September 2025, standardizing nine features that had been in development for years.51~52| Feature | What It Enables |53|---------|----------------|54| **WasmGC** | Native garbage collection in WASM. Managed languages (Java, Kotlin, Dart) can compile to WASM without shipping their own GC runtime. Supported in Chrome 119+, Firefox 120+, Safari 18.2+. |55| **Exception Handling** | Native `try`/`catch` in WASM. Previously, exceptions required expensive roundtrips to JavaScript. |56| **Tail Calls** | Enables efficient recursion without stack overflow. Critical for functional languages. |57| **Relaxed SIMD** | 128-bit vector instructions for parallel data processing. Enables hardware-specific optimizations. |58| **Memory64** | Breaks the 4GB linear memory limit. Required for large-scale data processing. |59| **Multi-memory** | Multiple independent memory regions in one module. |60~61The most impactful is **WasmGC**. Before it, compiling Java or Kotlin to WASM meant shipping an entire garbage collector as part of the binary, bloating file sizes. Now the browser's own GC handles memory management for WASM modules, just like it does for JavaScript.62~63## WASI: WebAssembly Beyond the Browser64~65WASM in the browser is powerful, but **WASI (WebAssembly System Interface)** is what makes WASM a universal runtime. WASI provides standardized interfaces for system resources - files, networking, clocks, random numbers - allowing WASM modules to run outside the browser.66~67```mermaid68graph TD69 subgraph "Browser"70 B[WASM Module] --> Web[Web APIs\nDOM, Fetch, Canvas]71 end72~73 subgraph "Server / Edge / Embedded"74 S[WASM Module] --> WASI[WASI Interfaces]75 WASI --> FS[wasi:filesystem]76 WASI --> Net[wasi:sockets]77 WASI --> HTTP[wasi:http]78 WASI --> Clock[wasi:clocks]79 WASI --> Rand[wasi:random]80 end81```82~83WASI Preview 2 (the current stable release) defines these interfaces:84~85- `wasi:filesystem` - file operations via capability handles (not traditional file descriptors)86- `wasi:sockets` - TCP/UDP networking87- `wasi:http` - HTTP request/response handling88- `wasi:clocks` - wall clock, monotonic clock89- `wasi:random` - cryptographic randomness90- `wasi:cli` - command-line arguments, environment variables, stdio91~92The key principle is **capability-based security**: a WASM module cannot access the filesystem unless the host explicitly grants a handle to a specific directory. This makes WASI fundamentally more secure than running native executables.93~94### The Path to WASI 1.095~96WASI 0.3.0 (adding async/concurrency primitives) is expected in 2026, with WASI 1.0 to follow. The main addition is language-integrated async with zero-copy streaming I/O.97~98## The Component Model99~100Core WASM modules can only exchange numbers. The **Component Model** solves this limitation by adding a rich type system and composability layer on top of WASM.101~102### WIT (WebAssembly Interface Types)103~104WIT is an Interface Definition Language that lets components declare their imports and exports with rich types - strings, records, lists, variants, enums - not just `i32` and `f64`.105~106```wit107// calculator.wit108package myorg:calculator@1.0.0;109~110interface operations {111 record calculation {112 expression: string,113 result: f64,114 timestamp: u64,115 }116~117 add: func(a: f64, b: f64) -> f64;118 multiply: func(a: f64, b: f64) -> f64;119 history: func() -> list<calculation>;120}121~122world calculator {123 export operations;124}125```126~127Toolchains like `wit-bindgen` generate language-specific bindings from WIT files. A Rust component and a Python component can exchange strings, records, and lists through WIT contracts without either side knowing the other's implementation language.128~129## Building Your First WASM Module with Rust130~131Rust has the most mature WASM tooling. Let's build a practical example: an image processing module that runs in the browser.132~133### Setup134~135```bash136# Install the WASM target for Rust137rustup target add wasm32-unknown-unknown138~139# Install wasm-pack (builds Rust to WASM + generates JS bindings)140cargo install wasm-pack141~142# Create a new library project143cargo new --lib image-processor144cd image-processor145```146~147### Configure Cargo.toml148~149```toml150[package]151name = "image-processor"152version = "0.1.0"153edition = "2021"154~155[lib]156crate-type = ["cdylib"]157~158[dependencies]159wasm-bindgen = "0.2"160```161~162### Write the Rust Code163~164```rust165// src/lib.rs166use wasm_bindgen::prelude::*;167~168/// Convert an image buffer to grayscale.169/// Input: RGBA pixel data as a flat u8 array (4 bytes per pixel).170/// Output: Modified in place for zero-copy performance.171#[wasm_bindgen]172pub fn grayscale(pixels: &mut [u8]) {173 for chunk in pixels.chunks_exact_mut(4) {174 let r = chunk[0] as f32;175 let g = chunk[1] as f32;176 let b = chunk[2] as f32;177 // ITU-R BT.709 luminance coefficients178 let gray = (0.2126 * r + 0.7152 * g + 0.0722 * b) as u8;179 chunk[0] = gray;180 chunk[1] = gray;181 chunk[2] = gray;182 // chunk[3] is alpha, leave unchanged183 }184}185~186/// Adjust brightness of an image.187/// factor > 1.0 brightens, < 1.0 darkens.188#[wasm_bindgen]189pub fn adjust_brightness(pixels: &mut [u8], factor: f32) {190 for chunk in pixels.chunks_exact_mut(4) {191 chunk[0] = ((chunk[0] as f32 * factor).min(255.0)) as u8;192 chunk[1] = ((chunk[1] as f32 * factor).min(255.0)) as u8;193 chunk[2] = ((chunk[2] as f32 * factor).min(255.0)) as u8;194 }195}196~197/// Invert all colors in the image.198#[wasm_bindgen]199pub fn invert(pixels: &mut [u8]) {200 for chunk in pixels.chunks_exact_mut(4) {201 chunk[0] = 255 - chunk[0];202 chunk[1] = 255 - chunk[1];203 chunk[2] = 255 - chunk[2];204 }205}206~207/// Calculate the average brightness of an image (0-255).208#[wasm_bindgen]209pub fn average_brightness(pixels: &[u8]) -> f32 {210 let mut total: f64 = 0.0;211 let pixel_count = pixels.len() / 4;212 for chunk in pixels.chunks_exact(4) {213 let luminance = 0.2126 * chunk[0] as f64214 + 0.7152 * chunk[1] as f64215 + 0.0722 * chunk[2] as f64;216 total += luminance;217 }218 (total / pixel_count as f64) as f32219}220```221~222### Build223~224```bash225wasm-pack build --target web226```227~228This produces a `pkg/` directory with:229- `image_processor_bg.wasm` - the compiled WASM binary230- `image_processor.js` - JavaScript glue code with TypeScript definitions231- `package.json` - ready to publish to npm232~233### Use in JavaScript234~235```html236<!DOCTYPE html>237<html>238<head><title>WASM Image Processor</title></head>239<body>240 <canvas id="canvas" width="800" height="600"></canvas>241 <button onclick="applyGrayscale()">Grayscale</button>242 <button onclick="applyBrightness()">Brighten</button>243 <button onclick="applyInvert()">Invert</button>244~245 <script type="module">246 import init, { grayscale, adjust_brightness, invert } from "./pkg/image_processor.js";247~248 let ctx;249 let imageData;250~251 async function setup() {252 await init();253 const canvas = document.getElementById("canvas");254 ctx = canvas.getContext("2d");255~256 // Load an image onto the canvas257 const img = new Image();258 img.onload = () => {259 ctx.drawImage(img, 0, 0);260 imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);261 };262 img.src = "photo.jpg";263 }264~265 window.applyGrayscale = () => {266 grayscale(imageData.data);267 ctx.putImageData(imageData, 0, 0);268 };269~270 window.applyBrightness = () => {271 adjust_brightness(imageData.data, 1.3);272 ctx.putImageData(imageData, 0, 0);273 };274~275 window.applyInvert = () => {276 invert(imageData.data);277 ctx.putImageData(imageData, 0, 0);278 };279~280 setup();281 </script>282</body>283</html>284```285~286The key insight: `imageData.data` is a `Uint8ClampedArray` backed by an `ArrayBuffer`. When passed to WASM, it shares the same memory - no copying. The Rust function modifies pixels in place, and the JavaScript side sees the changes immediately.287~288## Lower-Level: Manual WASM Instantiation289~290If you don't want to use `wasm-bindgen`, you can instantiate WASM modules directly:291~292```javascript293const response = await fetch("calculator.wasm");294const { instance } = await WebAssembly.instantiateStreaming(response, {295 env: {296 // Functions the WASM module can call297 log_result: (value) => console.log("Result:", value),298 },299});300~301// Call exported functions302const { add, multiply } = instance.exports;303console.log(add(5, 3)); // 8304console.log(multiply(4, 7)); // 28305```306~307This is useful when you want minimal overhead and don't need rich type interop.308~309## Performance: WASM vs JavaScript310~311Real-world benchmarks show significant speedups for CPU-intensive tasks:312~313| Task | JavaScript | WASM | Speedup |314|------|-----------|------|---------|315| 4K image processing | 180ms | 8ms (with SIMD) | 22x |316| Image resize (4K) | 250ms | 45ms | 5.5x |317| Physics simulation (10K entities) | Drops frames | Smooth 60fps | ~10x |318| JSON parsing (large payload) | 12ms | 3ms | 4x |319| Cryptographic hashing | 45ms | 6ms | 7.5x |320~321WASM runs at roughly 95% of native code speed. The biggest gains come from:322- Predictable performance (no JIT warmup, no GC pauses)323- SIMD instructions for parallel data processing324- Direct memory access without garbage collector interference325~326Where WASM is NOT faster: DOM manipulation, small computations, I/O-bound tasks. JavaScript is already optimized for these.327~328## Production Use Cases329~330### Figma: Real-Time Vector Rendering331~332Figma's core rendering engine is C++ compiled to WASM. Every shape, gradient, and effect is computed in WASM and drawn to a Canvas element. This allows Figma to handle complex designs with thousands of layers at 60fps in the browser - performance that would be impossible in pure JavaScript.333~334### Adobe Photoshop on the Web335~336Adobe ported key Photoshop filters and tools to WASM using Rust. Their benchmarks show 4K image processing in 22ms with WASM SIMD vs 180ms in JavaScript - an 8x improvement that makes real-time filter previews possible.337~338### Cloudflare Workers339~340Cloudflare runs WASM modules in V8 isolates across 330+ edge locations. Cold starts are 1-5ms (compared to 100-500ms for container-based serverless). In February 2026, they deployed Llama-3-8b inference across their edge network using WASM.341~342### Google Meet343~344Background blur and virtual backgrounds in Google Meet use WASM with SIMD for real-time video processing. The WASM module processes each video frame fast enough to maintain smooth video at 30fps.345~346## Browser Support (2026)347~348| Feature | Chrome | Firefox | Safari | Edge |349|---------|--------|---------|--------|------|350| Core WASM | Full | Full | Full | Full |351| Threads | Yes | Yes | Yes | Yes |352| SIMD (128-bit) | Yes | Yes | Yes | Yes |353| WasmGC | 119+ | 120+ | 18.2+ | Yes |354| Exception Handling | Yes | Yes | Yes | Yes |355| Memory64 | Yes | Yes | Partial | Yes |356~357All major browsers fully support WASM. The newer features (WasmGC, Exception Handling) have reached broad availability.358~359## Tooling Reference360~361| Tool | Purpose | Install |362|------|---------|---------|363| **wasm-pack** | Build Rust to WASM, generate npm packages | `cargo install wasm-pack` |364| **wasm-bindgen** | Rust/JS interop bindings (used by wasm-pack) | Dependency in Cargo.toml |365| **wasm-opt** | Binary size optimization (50%+ reduction) | Part of Binaryen: `brew install binaryen` |366| **wit-bindgen** | Generate bindings from WIT files | `cargo install wit-bindgen-cli` |367| **Wasmtime** | Server-side WASM runtime (reference WASI implementation) | `brew install wasmtime` |368| **Wasmer** | Alternative WASM runtime with WASI support | `curl https://get.wasmer.io -sSfL \| sh` |369| **wasm-feature-detect** | Runtime browser feature detection | `npm install wasm-feature-detect` |370~371### Optimizing Binary Size372~373WASM binaries can be large. Here's how to shrink them:374~375```toml376# Cargo.toml377[profile.release]378opt-level = "z" # Optimize for size379lto = true # Link-time optimization380codegen-units = 1 # Better optimization, slower compile381strip = true # Strip debug symbols382```383~384```bash385# Build in release mode386wasm-pack build --release --target web387~388# Further optimize with wasm-opt389wasm-opt -Oz pkg/image_processor_bg.wasm -o pkg/image_processor_bg.wasm390```391~392A typical Rust WASM module goes from 500KB to under 50KB with these optimizations.393~394## Getting Started Roadmap395~396```mermaid397flowchart TD398 A[Week 1: Basics] --> B[Week 2: Real Project]399 B --> C[Week 3: WASI and Server-side]400 C --> D[Month 2+: Production]401~402 A --> A1[Install Rust + wasm-pack]403 A --> A2[Build hello-world WASM module]404 A --> A3[Call WASM functions from JavaScript]405~406 B --> B1[Build image processor or game physics]407 B --> B2[Use wasm-bindgen for rich types]408 B --> B3[Benchmark against pure JS]409~410 C --> C1[Run WASM with Wasmtime]411 C --> C2[Explore WASI interfaces]412 C --> C3[Try Component Model with WIT]413~414 D --> D1[Optimize binary size]415 D --> D2[Use SIMD for parallelism]416 D --> D3[Deploy to Cloudflare Workers or browser]417```418~419## Conclusion420~421WebAssembly is no longer experimental. It is a production technology used by some of the most demanding applications on the web. Near-native performance, sandboxed security, and universal portability - no other compilation target gives you all three.422~423You don't need to rewrite your entire application in WASM. Start with a single CPU-intensive function - an image filter, a data parser, a physics calculation - compile it to WASM, and call it from JavaScript. Measure the difference. Then decide where else WASM can help.424~425The tooling is mature, the browser support is universal, and the ecosystem is growing. If you write Rust, you are already one command away from the browser.426~427> **Getting Started Checklist:**428>429> - [x] Rust and wasm-pack installed430> - [x] First WASM module built and running in the browser431> - [x] JavaScript interop working (calling WASM from JS)432> - [x] Release build with size optimizations applied433> - [x] Performance benchmarked against pure JavaScript equivalent434> - [x] WASI explored with Wasmtime for server-side use cases435~
NORMAL · webassembly-wasm-complete-guide.md [readonly]435 lines · :q to close