The editorial argues this isn't a cute hack but a benchmark proving WASM's six-year-old 'near-native performance' promise has finally arrived. With SIMD shipping stable in 2023 and multi-memory/tail-calls/GC landing in 2024, plus V8's Liftoff baseline producing code within 20-30% of TurboFan, cold-start latency has dropped to irrelevant — and Pokemon Emerald hitting 100k FPS with the bottleneck being putImageData rather than simulation logic is the proof.
By submitting the project with the headline '100k FPS' framing, the submitter highlights that this leverages the pret community's byte-identical C decompilation pointed at Emscripten rather than a traditional GBA emulator. The 1,666x speedup over the original 16.78 MHz ARM7 hardware is possible only because the game logic runs as compiled WASM directly, with JavaScript shims replacing hardware registers.
A developer shipped `pokeemerald.com` — the full Pokemon Emerald engine, decompiled from the original GBA ROM by the pret community over the last several years, compiled to WebAssembly, and running in any modern browser. The HN submission hit 294 points in under a day, driven almost entirely by one number in the README: roughly 100,000 frames per second with the frame limiter disabled.
The build pipeline is unromantic. The pret project's `pokeemerald` decompilation is a buildable C source tree that produces a byte-identical ROM to the 2004 retail cartridge. Point that source tree at Emscripten instead of `agbcc` (the Game Boy Advance C compiler), stub the ARM7TDMI hardware registers with JavaScript-callable shims for input and a canvas blit for video, and you have a native-speed port — no emulator in the loop. The GBA was a 16.78 MHz ARM7 with no FPU. A 2024-class laptop running V8's Liftoff-then-TurboFan WASM pipeline is, charitably, about 1,666x faster than that.
The interesting part isn't that it runs — it's that the bottleneck is now the canvas blit, not the game logic. With rendering disabled, the simulation loop reportedly pushes past 100k ticks/sec. With a 240x160 ImageData blit on every frame, you're CPU-bound on `putImageData`, not on Pokemon's battle RNG.
The instinct is to file this under 'cute hack.' Don't. WebAssembly's pitch since 2017 has been 'near-native performance in the browser,' and for six years the honest answer was 'near, with an asterisk roughly the size of your SIMD usage.' That asterisk has been shrinking. WASM SIMD shipped stable in all major browsers in 2023. Multi-memory, tail calls, and GC landed in 2024. The Liftoff baseline compiler in V8 now produces code within 20-30% of TurboFan's tier-up output on first execution, which means cold-start latency for WASM modules dropped from 'noticeable' to 'irrelevant' for anything bigger than a button handler.
The Pokemon benchmark is a useful proxy because the workload is *adversarial to WASM's strengths*. Emerald is not a vectorizable shader. It's branch-heavy interpreter-style C code: lookups into Pokemon stats tables, type effectiveness matrices, RNG calls for damage rolls, and a state machine for the battle scripting engine. This is exactly the kind of workload where JIT'd JavaScript falls off a cliff due to type instability and where WASM should — and apparently does — hold a steady 5-10x lead over equivalent JS.
Compare this to the alternatives. An mGBA WebAssembly build (a real emulator, ARM7 interpreter in software) tops out around 1,500-2,000 FPS on the same hardware. The decompiled native port is running 50-70x faster than the emulated equivalent because it skips the entire instruction-decoding tax. That gap is the value of decompilation projects like pret, m1, and ghidra-assisted reverse engineering: once you have source, you have a target for *any* compiler backend, not just the one Nintendo shipped in 2002.
Community reaction on the HN thread has been split between 'this is a flex' and 'this is a quietly important compiler benchmark.' The second camp is right. A senior commenter pointed out the obvious next step: if Emerald runs at 100k FPS, you can stack 100 instances in parallel and run distributed reinforcement learning against the game from a single browser tab. The Pokemon RL research community (yes, that's a thing — see the pokemon-red-experiments work) has been throttled by emulator throughput. WASM at 100k FPS removes that ceiling.
If you've been treating WebAssembly as a niche for Figma, Photoshop Web, and the occasional Rust experiment, the cost of that mental model is rising. Three workload classes now have a credible 'just compile it to WASM' answer where they didn't 18 months ago: deterministic simulation (game engines, physics, financial back-tests), interpreted language runtimes (Python via Pyodide, Ruby via ruby.wasm), and ML inference at the edge (ONNX Runtime Web, transformers.js).
For backend engineers, the lesson is different. The same toolchain that makes Pokemon run at 100k FPS in Chrome is what powers Cloudflare Workers, Fastly's Compute@Edge, and Fermyon Spin. WASM cold-start times in those runtimes are now in the single-digit milliseconds — faster than a Lambda container, an order of magnitude cheaper to keep warm. If your edge compute story is still 'V8 isolates or Node containers,' the Pokemon demo is also a recruiting pitch for whoever's about to migrate your auth middleware to WASM.
The practical caveat: this only works because pret had source. Most production C/C++ codebases that you'd want to port don't have a clean Emscripten target, and `wasm-bindgen` for Rust still has rough edges around large struct passing. The path from 'works on my laptop' to '100k FPS in production' goes through actual portability engineering — file I/O, threading, memory layout. None of that is solved by the benchmark. It's solved by your willingness to do the work.
The next 12 months are going to clarify whether WASM's performance story is 'good enough to displace native in specific niches' or 'good enough to displace V8 isolates as the default edge runtime.' Pokemon at 100k FPS is one data point in a chart that's been steepening since SIMD shipped. The chart points one direction. If you're architecting anything that runs sandboxed user code — plugins, edge functions, in-browser compute — and you're not at least prototyping the WASM version, you're betting against a compiler curve that's been winning for three years straight.
Confirming that saving genuinely works. Interesting stuff. Wonder if we can get trades working too.
Certain entities seem to be displayed as numbers for me, like "You received a 6" etc when getting my first potion. Anyone else seeing this bug?
Really exciting to see more games ported to WASM. For anyone interested, I just did a HN post detailing a port I spent the last 5 days on - Xonotic, an arena FPS. Includes a technical writeup too:https://news.ycombinator.com/item?id=48428133
Working on a fork that has audio. If anyone wants to have Claude continue it, be my guest!https://github.com/ottohg/pokeemerald-wasm
Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.
Cool! I like the speeding up feature. I suggest making the UI indicate that you can press the keys `z` and `x` on the keyboard to trigger the A and B buttons respectively, I figured that out by pressing on my keyboard the A and B keys and then all the letter keys.Btw, it seems there's a crash w