Docker images are hundreds of MB; a full game engine compiles to 35MB WASM

I exported a game skeleton to WebAssembly a few hours ago and was surprised by the artifact size. Full 3D engine: GL Compatibility renderer, Jolt physics, GDScript runtime, Ink narrative interpreter. The binary: 35MB. Runs in any browser, zero install.

Facebook’s homepage loads 44MB. The game engine is 35MB.

# Try it

Fullscreen, WASD, Esc.

# The thing that weighs less than a base image
▶ play in browser · 35 MB

python:3.14-slim-trixie, the slim base before you add a single dependency, is 144MB. Even a careful minimal build with uv lands at 282MB.

# Landscape

Sizes from my browser and local Docker cache:

ItemSize
Google homepage (all resources, 43 requests)10MB
this game (Godot 4, full engine)35MB
Facebook homepage (all resources, 379 requests)44MB
livekit/livekit-server (Go, WebRTC)75MB
python:3.14-slim-trixie144MB
python:3.14-slim-trixie + minimal deps282MB
REST API from my job300–400MB
node:latest (19M pulls/week)421MB
ghcr.io/gohugoio/hugo423MB
Python-based AI agent from my job1.45GB

Hugo: 423MB to generate static HTML. The game engine is 35MB 😌

The Go binaries (livekit at 75MB) are already close.

# The open question

On one hand, Go could be a solution, but wasip1 is still preview: no sockets in the standard runtime, no threads. Zig is closer, but not there either. Only Rust and C/C++ are practical options today.

On the other hand, Cloudflare Workers can load WASM modules, containerd has runwasi, Kubernetes has kwasm experiments, WASI runtimes exist.

So why has WASM adoption stalled? The transfer-size case is already there: roughly 10×. Why isn’t that enough to become standard practice?

Same as ARM nodes a few years ago: cheaper, denser, widely available. Still not the default choice.