Docker · Linux · WebAssembly

Docker,
in the browser.

Real OCI/Docker containers on unmodified Alpine Linux, booted by a from-scratch RISC-V emulator — entirely client-side in WebAssembly. No server, no daemon, no install. Just a URL.

RV64GC
full base + M/A/F/D/C
100%
client-side, offline-capable
byte-exact
Spike-matched execution
native≡wasm
deterministic by construction
What it does

Not a toy. A whole machine.

Every layer of a real computer, re-implemented and verified — then compiled to run in the one runtime everyone already has.

🐧

Unmodified Linux

Boots the stock Alpine riscv64 kernel + userland to a login shell — OpenRC, busybox, apk, the works.

// boot-to-login
💾

Persistent disk

A copy-on-write overlay in IndexedDB survives tab reloads, with crash-consistent virtio-blk flush semantics.

// IndexedDB overlay
🌐

Real networking

A user-mode TCP/IP stack (slirp) with DHCP + DNS, plus a Tailscale transport — the guest gets a real IP.

// slirp + tailscale
📦

OCI containers

Pull and run real Docker Hub images as isolated containers with wvrun — pid/mount/uts namespaces.

// wvrun run/exec
⏱️

Snapshot & resume

Freeze the whole machine — CPU, RAM, devices — to a coherent blob and resume instruction-exact, fsck-clean.

// save_resume
🎯

Provably correct

Differentially tested byte-for-byte against Spike, with a native≡wasm determinism gate on every build.

// diff vs spike
By the numbers

Built the long way. On purpose.

0
engineered tasks, ledger-tracked
0
epics, ignition → transcendence
0
RISC-V extensions (IMAFDC)
0×
browser tab · zero servers
Node system workload matrix

What the Node surface actually costs.

One portable script measures verified file reads/writes, streaming, server startup, and local HTTP request/response behavior across native Node, wasm-vm, WebContainers, almostnode, and WebVM.

Measured, per operation

Same fixture, every environment.

The complete row set is the current source of truth. Unsupported cells remain visible and explained; they are never replaced with a different benchmark or an inferred value.

Captured loading…
Environment File read File write Stream read Stream copy Server lifecycle HTTP round trip
Loading raw workload evidence…

Each cell shows median time, p95, and throughput where bytes were transferred. The fixture is deterministic, every measured sample checks its output digest, and the raw JSON keeps the individual timings, runtime identity, script digest, sampling policy, and verification results. This is a workload measurement, not a claim that the environments provide identical storage or network isolation. Runner · Raw comparison →

Measured, steady-state compute

Where a JIT can actually amortize.

This is a separate campaign from filesystem, stream, and HTTP boundaries. It keeps one process alive and times only deterministic arithmetic, branching, and typed-array loops.

Captured loading…
Environment Integer mix Branch mix Memory mix
Loading steady-state compute evidence…

Each measured cell shows median time, p95, and verified work rate. The fixture uses fixed checksums; process startup and memory preparation are excluded from timing. Measured browser rows come from the deployed Node guest; any remaining unmeasured row includes its reason. Runner · Raw compute comparison →

Separate native JIT diagnostic

Hot blocks win; boundary blocks barely do.

WorkloadInterpreterJITJIT / interpreter
Loading JIT diagnostic…

This release-only handoff diagnostic is not substituted into the comparable browser table: six-op measures dispatch-bound behavior, while 64-op measures hot-block amortization.

How it works

Fetch → decode → execute, a few hundred million times.

An interpreter core in Rust, compiled to WebAssembly, driving a full virt platform — CLINT, PLIC, UART, virtio-blk/net/rng — off a deterministic clock. The whole stack, top to bottom:

L6
Alpine Linux userland
busybox · apk · OpenRC · wvrun OCI containers
L5
Linux kernel 6.6.63
unmodified riscv64 — MMU (Sv39/48), interrupts, virtio drivers
L4
The virt platform
CLINT · PLIC · 16550 UART · virtio-blk / net / rng · goldfish RTC
L3
RV64GC interpreter core
Rust · precise traps · Zicsr · a deterministic instruction-retirement clock
L2
WebAssembly
the core compiled to wasm32 — native ≡ wasm, proven every build
L1
Your browser tab
no plugins, no server round-trips — it all runs on your machine
wasm-vm — ttyS0
# booting unmodified Alpine riscv64 in WebAssembly…
[    0.000000] Linux version 6.6.63 (riscv64) — in your browser
[    2.664] Run /init as init process
wasm-vm:~# uname -m
riscv64
wasm-vm:~# cat /proc/cpuinfo | grep isa
isa : rv64imafdc
wasm-vm:~# wvrun run busybox echo "hello from a container"
hello from a container
wasm-vm:~# _

Runs entirely in this tab — a real busybox userland in ~15–30s. Nothing leaves your browser.

What it isn't

A real machine — not a clever illusion.

Plenty of things look like a computer in a browser. This one actually is one.

// not a recording
Real execution

Every instruction runs for real, retiring one at a time — not a pre-baked screencast or a canned transcript.

// not a remote VM
Nothing leaves your tab

There's no server doing the work. The CPU, RAM, disk, and network stack all live in the page — offline-capable after first load.

// not transpiled
An actual RISC-V CPU

Unmodified riscv64 binaries execute against a spec-correct core — differentially checked byte-for-byte against Spike.

Ready when you are

Boot a Linux box. Right now.

It runs entirely on your machine — nothing you do here leaves the tab.