How Static Recompilation Runs PS2 Games in the Browser: GT4 at 60fps
The open-source ps2-web-recomp project statically recompiles PS2 MIPS machine code to C++ and then WebAssembly, letting Gran Turismo 4 and God of War 2 run in Chrome at near-native speed via WebGPU compute shaders, with a reusable toolchain that discovers missing functions through headless execution.
What the Project Does
The project ps2-web-recomp (by snuri00, 900+ stars, MIT license) performs static recompilation of PlayStation 2 games: it takes a game ISO, translates the Emotion Engine (MIPS) machine code to C++ ahead of time, compiles that to WebAssembly, and runs the result in a browser. The rest of the system — OS, controllers, GPU, audio — is emulated at runtime. The GPU (Graphics Synthesizer) is implemented with WebGPU compute shaders. This differs from traditional emulators (like PCSX2) that use interpreters or JIT, incurring translation overhead on every frame. Static recompilation pays that cost once during a build step, so runtime only feeds data to the GPU.
The trade-off: each game must be processed individually; there is no plug-and-play support.
Toolchain Components
The repository is a complete toolchain split into four main parts:
Recompiler (recomp/) — eerecomp translates the game's EE executable to C++. It preserves R5900 quirks: 128-bit registers, multimedia instructions, branch delay slots, a floating-point unit without infinities or subnormals, and VU0 used as a coprocessor. Cross-unit jumps become tail calls; thread switching unfolds the host stack and restores it at the guest address, so generated code doesn't need a separate stack per guest thread.
PS2 Kernel Patches (runtime/) — High-level emulation (HLE) replaces OS services: threads, semaphores, DMA controller, V-blank interrupts, alarms, and the SIF channel linking the EE and IOP. The IOP side runs the game's own IRX modules (sound, CD streaming, controller, memory card) via an R3000 interpreter, while the IOP kernel and its libraries are reimplemented in C++.
Vector Units (vurecomp.cpp / vu_jit.cpp) — VU1 microprograms (where most 3D math happens) are modeled with pipeline delays for MAC, status flags, and clipping flags (games schedule around these delays). Floating-point rules match PS2: no infinities/NaN, subnormals flush to zero, overflow clamps to max float.
Graphics (runtime/src/gs + WGSL) — The GS's 4 MB VRAM stays on the GPU in its native swizzled layout. Drawing uses WebGPU compute shaders: triangles are binned by tile, rasterized with GS texture, palette, alpha blending, depth, and destination-alpha rules. Because everything remains in GS VRAM, effects like "render-to-texture then use as palette" behave identically to hardware. Internal resolution can be set to 1×, 2×, or 3× native.
Web Host + Scripts (host/ + web/ + scripts/) — Port script, HTTP server, browser shell.
Case Study: Porting God of War 2
The same port.sh script used for GT4 was run on God of War 2 without writing any game-specific code. The process exposed two bugs in the recompiler that GT4 never triggered:
Missing IOP timer — The sound driver drives its tempo from an IOP hardware timer that wasn't implemented, so no audio samples were mixed.
Branch delay slot at 16 KB boundary — When a branch is the last instruction of a 16 KB code unit, the fall-through path should continue from the delay slot in the next unit, not from the instruction after the delay slot. The bug caused the delay-slot instruction to execute twice or a branch-likely's following instruction to run incorrectly. This corrupted a linked list, overwriting the V-blank handler and crashing before gameplay.
VU clip flag timing — God of War 2 microprograms read clip flags three instructions after a CLIP instruction, but the VU model exposed them only after four instructions. GT4's longer microprograms never hit this boundary.
After fixes, the game reaches combat at 60 fps (native PS2 resolution) and 48–60 fps at 2× resolution on a laptop integrated GPU.
How to Use
Prerequisites: git, CMake, Ninja, C++20 compiler (clang recommended), Python 3, and 7-Zip if the ISO is .7z. First run fetches the Emscripten SDK into tools/emsdk.
git clone https://github.com/snuri00/ps2-web-recomp.git
cd ps2-web-recomp
scripts/port.sh mygame "My Game.iso"
python3 scripts/serve.py build/web-mygame games/mygame/disc.iso
# then open http://localhost:8620/ port.shdoes everything: reads SYSTEM.CNF for the boot executable, translates machine code to C++, then runs the game headless several times . Static analysis cannot see all pointer-called functions, so each run records missing addresses, adds them as entry points, and recompiles until no new entry points appear. It also records and pre-compiles VU microprograms uploaded by the game. Each game consumes ~300 MB disk (generated code + build artifacts); the ISO is linked, not copied.
Is It Worth Using?
Currently only two games work : GT4 and God of War 2. Another game will likely stop at an unimplemented syscall ( [sys] unhandled syscall) or an unsupported GS feature. Therefore it is not a "play games now" tool but a reusable methodology and toolchain :
If you want to put your own game disc on the web, this is the only open-source full path. The author documents the full optimization journey from 3 fps to 70 fps for GT4: reducing compute dispatches from 650 to 80 per frame, cutting duplicate texture uploads by 60% with a 16 MB cache, and solving the nested setTimeout 4 ms clamping issue.
If you want to study how static recompilation + WebAssembly + WebGPU works in practice, this repo is a living reference (C++ ~84 KB, WGSL ~35 KB).
If you want to run other legacy consoles in the browser, the sister project psp-web-recomp uses the same method.
Not for: users who want zero-setup play and avoid a compile toolchain. You must provide your own game ISO and run a local build. The project includes no game data; legally, the generated C++ and WASM are translations of the game executable and belong to the copyright holder — do not publish them, only use on your own machine.
A Detail Showing Where the Cost Lies
Issues and PRs reveal the real growth curve. Nine PRs include community contributions: Midnight Club 3 boots to menu, Burnout 3 Takedown reaches front menu, second controller support, macOS build fixes. Every new game requires adding a few "hardware details the previous game never touched" to the runtime. The pattern is humble: recompiler correctness is forced out game by game, not designed perfectly upfront.
The hard part of running console games in browsers was never "instruction translation" — that's been done. The difficulty is filling every edge-case behavior the PS2 still relies on 20 years later, so the game feels no difference. ps2-web-recomp turns that into a repeatable engineering process.
GitHub: github.com/snuri00/ps2-web-recompSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Geek Labs
Daily shares of interesting GitHub open-source projects. AI tools, automation gems, technical tutorials, open-source inspiration.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
