WebAssembly Explained: What It Is, When It Matters, and How to Get Started
A practical introduction to WebAssembly — what problems it solves, how it works under the hood, which languages compile to it, and when you should actually use it.
WebAssembly is one of those technologies that people either dramatically overestimate or completely ignore. The truth is somewhere useful in between. It's not replacing JavaScript. It's not turning the browser into a full operating system. But it is solving real problems that JavaScript genuinely cannot solve well, and understanding when to reach for it is becoming an increasingly valuable skill.
What WebAssembly Actually Is
WebAssembly (Wasm) is a binary instruction format that runs in a virtual machine. That virtual machine lives inside every modern browser — Chrome, Firefox, Safari, Edge — and increasingly outside browsers too.
Think of it this way: JavaScript is a high-level language that browsers interpret and JIT-compile. WebAssembly is a low-level compilation target that browsers execute at near-native speed. You don't typically write WebAssembly by hand. You write code in another language — C, C++, Rust, Go, Zig, AssemblyScript — and compile it to Wasm.
The key properties:
- Fast: executes at near-native speed, consistently. No JIT warmup, no deoptimization surprises.
- Safe: runs in a sandboxed environment with no direct access to the host system.
- Portable: the same
.wasmbinary runs on any platform with a Wasm runtime. - Language-agnostic: any language with an LLVM backend can target Wasm.
Why JavaScript Isn't Enough
JavaScript is remarkably fast for a dynamically typed language. V8 and SpiderMonkey are engineering marvels. But there are categories of work where JavaScript hits a ceiling.
Consistent performance. JavaScript JIT compilers optimize hot paths, but they can also deoptimize them. Performance can be unpredictable. WebAssembly runs at consistent, predictable speed because it's already compiled to a low-level format. CPU-intensive computation. Image processing, video encoding, physics simulation, cryptography, compression — these tasks involve tight loops over large data sets. JavaScript can do them, but Wasm does them faster, often 2-10x faster depending on the workload. Existing codebases. There are millions of lines of battle-tested C and C++ code. Rewriting a mature image codec or physics engine in JavaScript is impractical and risky. Compiling it to Wasm preserves the existing code while making it run in the browser. Memory control. JavaScript's garbage collector decides when to pause your application. For real-time applications like games or audio processing, those pauses are unacceptable. Wasm gives you manual memory control — no GC pauses, deterministic performance.How It Works Under the Hood
When you compile code to WebAssembly, the compiler produces a .wasm binary file. This binary contains:
- Functions with typed signatures (i32, i64, f32, f64 — integers and floats)
- Linear memory — a contiguous, resizable byte array
- Tables — for indirect function calls
- Imports and exports — the interface between Wasm and the host environment
Here's what loading a Wasm module looks like in JavaScript:
// Fetch and instantiate a Wasm module
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {
log: (value) => console.log('From Wasm:', value),
},
});
// Call an exported function
const result = instance.exports.fibonacci(40);
console.log(result);
The Wasm module exports functions that JavaScript can call, and it can import functions from JavaScript. This is the bridge between the two worlds.
Languages That Compile to WebAssembly
Rust is the most popular language for Wasm development. Thewasm-pack toolchain makes compilation straightforward, the resulting binaries are small (no runtime or GC to bundle), and the Rust ecosystem has excellent Wasm libraries. If you're starting from scratch, Rust is the default recommendation.
// A simple Rust function compiled to Wasm
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 2..=n {
let temp = b;
b = a + b;
a = temp;
}
b
}
C/C++ via Emscripten is how many existing codebases reach the web. SQLite, FFmpeg, OpenCV, game engines — all have been compiled to Wasm through Emscripten. The toolchain is mature but the output can be large.
// C code compiled to Wasm via Emscripten
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}
Go can target Wasm, but the output is large (several megabytes minimum) because the Go runtime and garbage collector are included. Fine for server-side Wasm, less ideal for browser delivery.
AssemblyScript is a TypeScript-like language designed specifically for Wasm. If you're a TypeScript developer who doesn't want to learn Rust or C, AssemblyScript is the gentlest on-ramp.
// AssemblyScript — looks like TypeScript, compiles to Wasm
export function fibonacci(n: u32): u64 {
if (n <= 1) return n as u64;
let a: u64 = 0;
let b: u64 = 1;
for (let i: u32 = 2; i <= n; i++) {
let temp = b;
b = a + b;
a = temp;
}
return b;
}
Zig produces very small Wasm binaries with no hidden runtime cost — it's gaining traction in the Wasm space for performance-critical modules.
Real-World Use Cases
Figma is the canonical success story. Their design tool runs a C++ rendering engine compiled to Wasm. The result is a browser-based app that performs like a native desktop application. Without Wasm, Figma couldn't exist as a web app. Google Earth ported their existing C++ codebase to the web via Wasm. Rewriting it in JavaScript would have taken years and produced worse results. Squoosh (Google's image compression app) uses Wasm codecs for image formats like AVIF, WebP, and MozJPEG. The compression runs client-side at near-native speed. Photoshop on the web uses Wasm extensively. Adobe compiled decades of C++ image processing code to run in the browser. SQLite in the browser — the official SQLite team maintains a Wasm build that lets you run a full relational database client-side. Cloudflare Workers and Fastly Compute run Wasm at the edge. This is where Wasm outside the browser gets interesting — the same binary that runs in Chrome can run on a CDN edge node.WebAssembly Outside the Browser
This is where things get forward-looking. The WebAssembly System Interface (WASI) is a standard for running Wasm outside browsers — on servers, at the edge, on embedded devices.
The pitch is compelling: write once, run anywhere, safely. A Wasm binary is sandboxed by default. It can't access the file system, network, or environment variables unless you explicitly grant those capabilities. This is the opposite of Docker containers, where you start with full access and try to restrict it.
# Compile a Rust program to WASI
rustup target add wasm32-wasi
cargo build --target wasm32-wasi --release
# Run it with Wasmtime
wasmtime target/wasm32-wasi/release/my_program.wasm
Popular Wasm runtimes outside the browser:
- Wasmtime — the reference implementation, fast and production-ready
- Wasmer — focuses on ease of use and language embeddings
- WasmEdge — optimized for edge computing and AI inference
- WAMR — lightweight runtime for embedded and IoT devices
The Component Model: Wasm's Next Big Thing
Raw Wasm only understands integers and floats. Passing a string from JavaScript to Wasm means manually encoding it into linear memory and passing a pointer. This is painful.
The Component Model fixes this. It defines a higher-level type system (strings, lists, records, variants) that lets Wasm modules communicate using rich types. It also enables composing multiple Wasm modules together — a Rust module calling a Python module calling a Go module, all running in the same sandboxed environment.
This is still maturing, but tools like wit-bindgen and wasm-tools are making it practical today. The Component Model is what turns Wasm from "fast code execution" into "universal software component system."
When NOT to Use WebAssembly
Wasm is not always the answer. In fact, for most web applications, you don't need it.
DOM manipulation. Wasm cannot directly touch the DOM. Every DOM operation goes through JavaScript. If your bottleneck is DOM updates, Wasm won't help — and might hurt, since the Wasm-to-JS bridge has overhead. Simple CRUD apps. If your app is fetching data from an API and rendering it, JavaScript is fine. The performance ceiling doesn't matter because you're nowhere near it. Small scripts. Wasm modules have a baseline size cost. For a 10-line utility function, the overhead of loading a Wasm module isn't worth it. When you need fast iteration. The compile step adds friction. JavaScript's edit-refresh cycle is faster for rapid prototyping.The rule of thumb: use Wasm when you have CPU-intensive computation, when you need consistent performance without GC pauses, or when you're porting existing native code to the web.
Getting Started: The Practical Path
The fastest way to try WebAssembly depends on your background.
If you know Rust, start withwasm-pack:
# Install wasm-pack
cargo install wasm-pack
# Create a new project
cargo new --lib my-wasm-project
cd my-wasm-project
# Add wasm-bindgen to Cargo.toml
# [lib]
# crate-type = ["cdylib"]
#
# [dependencies]
# wasm-bindgen = "0.2"
# Build for the web
wasm-pack build --target web
If you know TypeScript, start with AssemblyScript:
npm init
npm install --save-dev assemblyscript
npx asinit .
npm run asbuild
# Your compiled .wasm file is in build/
If you know C/C++, start with Emscripten:
# Install Emscripten SDK
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
# Compile C to Wasm
emcc hello.c -o hello.js -s EXPORTED_FUNCTIONS='["_add"]'
For all paths, the workflow is: write code in your source language, compile to .wasm, load it from JavaScript, call the exported functions. Start with something small — a math function, an image filter, a compression routine — and measure whether the performance gain justifies the added complexity.
The Honest Assessment
WebAssembly is a genuinely important technology, but it's not a JavaScript replacement. It's a JavaScript complement. The web platform is stronger because Wasm exists. Applications that were impossible as web apps — professional design tools, CAD software, video editors, game engines — are now possible.
For most web developers, Wasm is something you'll consume (using libraries that happen to be compiled from Rust or C) rather than something you'll produce directly. And that's fine. But if you work on anything performance-sensitive, understanding Wasm's capabilities and limitations makes you a more effective engineer.
The technology is mature enough for production use today and getting better rapidly. The Component Model, WASI, and the growing ecosystem of Wasm-first tools suggest that the next few years will be where Wasm moves from "impressive demos" to "standard infrastructure."
Practice building and integrating WebAssembly modules alongside other web technologies on CodeUp.