Independent project guideSource checked · 15 OCT 2026
Source on GitHub

Technical explainer

How PS2 Web Recomp works

Ahead-of-time CPU translation is one part of a complete console runtime. The rest still has to behave like a PS2.

Source reviewed 15 October 2026 · Evidence & method

The compilation pipeline

Owned disc image→PS2 executable→Generated C++→WebAssembly + runtime

eerecomp follows the executable’s control flow and emits C++ in units corresponding to 16 KiB of guest code. Emscripten compiles that output for the browser. Instead of interpreting each Emotion Engine instruction as the game runs, the translated code executes as WebAssembly.

Finding functions is a practical challenge. Calls through pointers are not all visible to static analysis. The porting workflow explores the game in a native runner, logs missing entry addresses and translates again. A finished compiler run therefore depends on how much execution the exploration reached, not merely whether a file existed.

Preserving R5900 behavior

The PS2’s main CPU has features ordinary C++ does not model automatically: 128-bit registers, multimedia instructions, branch delay slots and different floating point rules. The generated code preserves those behaviors explicitly. The README describes tail calls between units and resuming a guest thread at its recorded address, rather than keeping a host stack for every guest thread.

Why “without an emulator” can be misleading

The current upstream wording makes the distinction carefully: the main CPU is statically recompiled, while the operating system and remaining hardware are emulated by a runtime. High-level services implement threads, semaphores, alarms, interrupts and communication with the I/O processor. The IOP’s R3000 code is interpreted, including game-supplied modules for sound, disc streaming and controllers. Static recompilation does not remove the need to reproduce console behavior.

Vector microprograms

VU1 handles much of a game’s geometry work. Microprograms seen during exploration can be compiled ahead of time into SIMD C++. Programs first encountered by the page can be translated into a small WebAssembly module at runtime. Pipeline timing, clip flags and the PS2’s handling of special floating point values still matter. The author notes that host arithmetic can differ in its last bit from the console.

WebGPU and the Graphics Synthesizer

The GS’s 4 MB of video memory stays on the GPU in native swizzled layouts. Compute shaders bin triangles into tiles and rasterize with console-specific texture, palette, blending and depth behavior. Keeping memory representation consistent helps effects that reinterpret an existing framebuffer. A software rasterizer remains a reference against which GPU output can be compared.

Increasing internal resolution raises graphics work even if CPU recompilation is efficient. The documented native, 2× and 3× options are rendering settings; they do not change the PS2 game’s timing target. Compare scene and resolution together when judging performance.

Threads, audio and movies

Emulation runs in a worker, with another worker for VU1. GS commands pass through a ring buffer to the page thread that owns the WebGPU device. SharedArrayBuffer supports these exchanges, which is why the actual game page requires cross-origin isolation.

The SPU2 model implements its 48 voices, envelopes and streaming DMA, sending audio through shared memory to an AudioWorklet. An IPU implementation handles MPEG-2 movie decoding. Correct sound or cutscenes therefore involves more than a working GPU and a compiled main CPU.

Where a new game can stop

A new title may reach an unimplemented system call, an IOP library function, a graphics feature or a function missed in exploration. The two documented games exercise different hardware paths. This is why a per-game progress record and exact commit are more useful than a broad claim that the PS2 library now runs in a tab.

Source: Upstream project documentation · reviewed 15 October 2026.