FlutterWasmWeek: benchmarked dart2wasm vs dart2js on a CHIP-8 emulator — 2.6–4.4x, plus a benchmarking trap
Since the Flutter team is collecting wasm feedback this week, I wanted numbers where the delta is attributable to codegen alone, not the rendering pipeline. So I wrote a CHIP-8 emulator in pure Dart: interpreter hot loop over Uint8List, zero allocations in the loop, framebuffer blitted in a single CustomPainter. Same source compiled twice — flutter build web vs flutter build web --wasm. Flutter 3.47, both release, same Chrome.
Results:
- dart2wasm: ~130M emulated cycles/sec, and the same number in every environment I tried (interactive window, headless, occluded tab)
- dart2js: 30–51M depending on environment, with a few seconds of JIT warmup before reaching full speed
- speedup: 2.6–4.4x. Migration cost for this codebase: one build flag, zero code changes.
Two things surprised me:
The consistency matters more than the multiplier. Wasm runs at full speed from the first frame, everywhere. JS swings by 70% between environments. For anything latency-sensitive, that predictability is the real win.
A trap if you benchmark this yourself: if your metric is cycles per wall-clock second, Chrome's requestAnimationFrame throttling (occluded or backgrounded window) silently crushes the number ~4x. I chased that ghost for a while. Normalize by actual compute time, or you're measuring the compositor, not the compiler.
Caveats: one compute-heavy workload, one machine. UI-bound apps will see much smaller gains, and packages relying on legacy dart:html / dart:js interop can block the wasm build entirely — that's the first thing to check on a real codebase.
(It also plays the original 1990 Pong ROM, which was not strictly necessary but was the most fun part.)
Happy to answer questions or share details about the setup.