Running Io in the browser via WebAssembly.
Historically the Io VM was a native C binary with a per-platform build matrix (macOS, Linux, Windows, BSD), platform-specific coroutine assembly, and a native addon model that compiled C extensions against the host toolchain. That model works, but every new platform multiplies the work: new assembly for coroutines, new build recipes, new binary artifacts, new ways for addons to break.
Compiling the VM to WebAssembly collapses that matrix to a single portable module. The same io_static.wasm runs under wasmtime, Node.js, and directly in the browser, with no platform-specific code paths in the VM itself.
fetch, Web Audio, WebGL — any capability the host page exposes — without a separate “web Io” fork.Trade-offs are real: the WASM target is early-access, JIT throughput depends on the host runtime, and some classic native addons (notably anything linking C libraries) don’t carry over — their roles are now filled by JavaScript libraries reached through the bridge.
In 2026 the Bytecode Alliance shipped WASI 0.3, whose headline change is that async is now native to WebAssembly components. The component model's canonical ABI gains three first-class constructs — stream<T>, future<T>, and async functions — and the host runtime takes over scheduling with a single shared, completion-based event loop (in the style of io_uring and IOCP) instead of each component polling readiness through pollable handles. Wasmtime 46 ships these interfaces with async enabled by default, and jco is bringing the same model to JavaScript hosts.
future<T> while the scheduler runs other coroutines, then resumed when the host completes the operation.@ (futureSend) and @@ (asyncSend) already give programs a future-based concurrency surface. A WASI 0.3 future<T> is the host-level version of the same idea, so an Io future awaiting a network response could be backed one-to-one by a host future and consume no VM scheduling at all until completion.start/finish/subscribe pattern over pollables. 0.3 collapses that into stream<T> values paired with completion futures that can finally distinguish “stream closed” from “stream failed.” Io's File and any future socket primitives map onto these cleanly.io_static.wasm as a WebAssembly component gives it typed, language-neutral interfaces. Components compose in-process (“service chaining”), so an Io component could sit in a pipeline next to components written in Rust or Go with nanosecond rather than millisecond call overhead — a substantial upgrade to the embedding story above.The migration path is incremental rather than architectural, and the first two steps have landed. The VM now also builds as a real WASI 0.2 component (make component, via wasi-sdk's wasm32-wasip2 target) that passes the full correctness suite under wasmtime. And the scheduler gained a timer queue: wait parks the calling coroutine instead of busy-spinning, timed coroutines run concurrently, and when nothing is runnable the VM blocks in a single host wait until the nearest deadline — the one idle point where a WASI 0.3 future<T> will be awaited instead. The remaining step — backing that idle point and the I/O primitives with 0.3 futures and streams — is gated on host and C-toolchain support (Wasmtime 46 and future<T>/stream<T> lowering from C), a change the Bytecode Alliance describes as “entirely mechanical” on the interface side.
Io runs in the browser as a WebAssembly module. The browser build compiles the full VM into a WASM reactor module that JS loads and drives via exported functions.
View →Io code running in the browser can query, create, and manipulate HTML elements through the DOM object and Element instances.