7 min left
Back to Series

The Stack > Article 12 | Intermediate | 7 min read

Article 12Intermediate7 min read

Running a neural model in the browser with ONNX and WebGPU

Simon Willison ported a 0.2B inpainting model from PyTorch to browser-native ONNX and WebGPU — 1.3GB of weights, zero server inference, mostly built with Claude Code.


Browser running neural network inference via WebGPU without a server

Simon Willison ported Moebius — a 0.2B-parameter image inpainting model — from PyTorch/CUDA to run entirely in the browser using ONNX and WebGPU. Weights live on Hugging Face (~1.3GB). Inference happens client-side in Chrome, Firefox, and Safari. The port was executed primarily through Claude Code while Willison worked on other tasks in parallel.

The project is a datapoint on two trends: browser ML is viable for small models, and coding agents can carry multi-step conversion work with minimal hand-holding.

Why move inference to the browser

Server-side inference for image models implies GPU hosting, cold start latency, upload bandwidth for images, and privacy questions when user photos leave the device. Client-side inference trades those costs for download size and device GPU requirements.

For a 0.2B model, the trade often favors the browser:

  • Privacy — pixels never leave the machine.
  • Marginal cost — no per-request GPU bill after assets are cached.
  • Latency after load — no round trip once weights sit in CacheStorage.

The cost is upfront: users download over a gigabyte of weights on first visit. Willison mitigated with browser caching so repeat visits skip the full transfer.

Think of it like installing a desktop app once versus streaming video forever. Heavy first load, then local execution.

PyTorch to ONNX to WebGPU

PyTorch models do not run natively in browsers. The conversion pipeline:

  1. Export to ONNX — a portable graph format many runtimes consume.
  2. Validate numerical parity — spot-check outputs against PyTorch on sample inputs. Conversion bugs are common.
  3. Run via ONNX Runtime Web — compiled for WebAssembly and WebGPU backends.
  4. Wire browser I/O — canvas or WebGL textures for image input/output, CacheStorage for weight persistence.

WebGPU provides GPU acceleration where available; WASM falls back on devices without WebGPU support. The demo targets modern browsers explicitly.

Each step is mechanical but tedious — exactly the sort of multi-file refactor coding agents handle when given a clear target and test criteria.

Agent-assisted porting in practice

Willison describes reviewing little of the generated code directly — running the demo, checking outputs, iterating on failures. That workflow assumes:

  • Automated verification — visual diff on inpainting results, console errors as signals.
  • Clear done criteria — "inpaint this mask in browser" is testable; "make it elegant" is not.
  • Parallel human attention — agent loops run while the human works elsewhere, not unattended forever.

Agent-assisted ports succeed when success is observable without reading every line — tests, demos, or numerical checks.

This is not "vibe coding" as abandonment of engineering judgment. It is delegating translation layers — ONNX export scripts, bundler config, cache headers — while the human owns acceptance criteria.

Size and performance realities

1.3GB is large for a web page. Techniques that matter:

  • CacheStorage — persist weights across sessions after first download.
  • CDN hosting on Hugging Face — reliable large-file delivery.
  • Progressive loading UI — users tolerate download with clear progress; they do not tolerate silent hangs.
  • Model size ceiling — 0.2B works; 7B does not fit this pattern without quantization and aggressive streaming research.

Browser ML today is a niche for small models and patient users — not a replacement for datacenter GPUs on frontier workloads.

StageServer inferenceBrowser inference
First visitLow (no model download)High (GB-scale weights)
Repeat usePer-request GPU costNear-zero marginal
PrivacyData leaves clientData stays local
Device reqsMinimalModern GPU + WebGPU
Model size limitDatacenter scaleSub-billion practical

What this means for builders

Prototype browser ML on sub-billion models first. Prove the UX and conversion pipeline before committing to larger architectures.

Treat ONNX export as a test-gated step. Always compare outputs to PyTorch on fixed fixtures after conversion.

Plan for first-load UX. A gigabyte download needs progress, retry, and cache strategy — not a blank page.

Use agents for boilerplate conversion. Export scripts, webpack config, and glue code are high-leverage delegation targets if acceptance tests exist.

Know the ceiling. Browser inference complements server inference; it does not replace it for large models or low-latency cold starts on first visit.

Conclusion

Willison's Moebius port shows that a meaningful neural network can run entirely in the browser with ONNX, WebGPU, and aggressive caching — and that coding agents can execute much of the conversion grind when humans hold the test harness. For builders, the actionable lesson is narrower: match deployment surface to model size and privacy requirements. Some inference belongs on the server. Some belongs in the tab. The tooling to choose deliberately now exists on both sides.


ONNXWebGPUbrowser MLClaude CodeinferencePyTorchedge computing

Up next in the series

Article 13Live

OpenKnowledge — CRDT sync and AI-native docs

OpenKnowledge is an open-source Obsidian/Notion alternative with ProseMirror, yjs CRDT sync, Git-backed collaboration, and native Claude/Codex/Cursor hooks — a look at how AI-native docs tools are structured.

OpenKnowledgeCRDTyjsProseMirror