PlayStation 2 games operating in the browser without an emulator. The game's Emotion Engine device code is translated onward of period into C++, compiled to WebAssembly, and connected against a reimplementation of the PS2's functioning scheme and hardware that draws alongside WebGPU.
Watch the complete record (1:24)
This is the brother or sister of psp-web-recomp and follows the identical method. The archetypal heading brought up this way is Gran Turismo 4, chosen since it is among the most demanding games on the system: it leans on the two vector units, on exact floating item behavior for its replays and licence tests, and on graphics tricks that peruse rendered images rear as palettes. It plays from footwear through the beginning movie, the menus and races, alongside music and audio effects, and saves to a recollection cardstock kept in the browser. A competition runs at complete speed, 50 frames per second for this PAL release, in Chrome on a laptop's unified GPU at twice the PS2's resolution, which is how the record complete was made.
God of War II followed through the identical scripts. It needed no game-specific code, but it established two bugs that Gran Turismo 4 had never triggered, one in the recompiler and one in the vector component timing, and a few missing pieces of the PS2's input/output processor. It now plays from the menus through its cutscenes into combat, and holds 60 frames per second in dense combat at the PS2's resolution in Chrome. At twice the resolution it runs at 48 to 60, constricted by the laptop's unified GPU.
No equivalent data is included here. You bring a disc depiction of a equivalent you own, and the scripts in this repository rotate it into a web leaf on your machine.
Recompilation. eerecomp says the game's executable, follows its authority stream to discover the functions and emits C++ for them in units of 16 KiB of visitant code. The output keeps the R5900's quirks: 128-bit registers and the multimedia instructions that activity on them, branch postpone slots, the PS2's floating item unit, which has no infinities or denormals and rounds alternatively from IEEE, and vector component 0 used as a coprocessor. Jumps between units rotate into rear calls, and a thread toggle unwinds the presenter stack and resumes at the visitant address, so the generated code never needs a stack per visitant thread.
A small PS2 kernel. The EE flank of the functioning scheme is answered by high-level emulation: threads, semaphores, interrupt handlers for the DMA controller and the vertical blank, alarms, and the SIF conduit that connects the chief CPU to the I/O processor. The I/O processor runs the game's own IRX modules, the audio driver, the disc streaming code and the controller and recollection cardstock drivers that container on the disc, on an translator of its R3000 CPU, during the IOP kernel and the libraries those modules call are reimplemented in C++. The disc is peruse complete HTTP Range requests, so lone the executable is downloaded up front.
Vector units. Most of a PS2 game's 3D activity happens in microprograms the equivalent uploads to vector component 1. They run alongside bit-exact PS2 drift math and alongside the pipeline delays of the MAC, position and clip flags modelled, since games agenda their code about them. Microprograms seen during a test run are compiled onward of period into SIMD C++; any the leaf meets for the archetypal period are translated into a WebAssembly component on the spot, which takes a few milliseconds per program.
Graphics. The Graphics Synthesizer's 4 MB of video recollection lives on the GPU, in its native swizzled layouts, and example is done by WebGPU compute shaders: triangles are binned into tiles and rasterized alongside the GS's own rules for texturing, palettes, alpha blending, degree and destination alpha tests. Because everything stays in GS memory, effects that render into a texture or reinterpret a framebuffer as a palette activity the way they do on the console. The inner resolution can be one, two or three times the native one. A application rasterizer of the identical GS remains as the citation the GPU output is compared against.
Threads. The emulation runs in a worker, vector component 1 has a employee of its own the way it runs in parallel on the console, and the GS commands go through a circle buffer to the leaf thread, which owns the WebGPU device.
Sound and movies. The SPU2 is reimplemented alongside its 48 voices of ADPCM, ADSR envelopes and the streaming DMA games use for music, and its output goes to an AudioWorklet through shared memory. Movies are decoded by an implementation of the IPU, the PS2's MPEG-2 decoder.
The archetypal competition ran at 3 frames per second natively, alongside a application rasterizer and an translator for vector component 1. Moving the GS to WebGPU compute shaders and compiling the vector component programs brought it to concerning 70 natively, and most of the remaining activity was concerning anywhere the browser spent its period fairly than the emulation.
The archetypal type of the GPU renderer submitted about 650 compute dispatches per frame, since all texture upload and palette burden divided the lot of triangles about it. Hoisting uploads onward of the triangles that do not depend on them, alongside the identical output, brought that to concerning 80.
Gran Turismo 4 uploads the identical 3 MB of textures all example of a race. GS recollection is exchanged between frames, so the uploads cannot merely be skipped, but a 16 MB cache of lately uploaded contents on the GPU lets most of them be served without crossing from WebAssembly to the GPU again, which cut the data sent per example by concerning 60 percent.
At three times the native resolution the example charge dropped to 23. Profiling the shader showed that the per-triangle setup was being loaded following a branch that differed between threads, which made the entire setup divergent, and that uploads divided into 1 MB pieces blocked Chrome's leaf thread. Loading the setup uniformly and penning all frame's data in one part brought three times the resolution to 48 to 50 frames per second on the identical unified GPU.
The browser's own scheduling mattered too. Nested setTimeout calls are clamped to 4 ms, so the emulation iteration now yields through a MessageChannel instead, and moving the emulation into a worker, so that the leaf thread lone feeds the GPU, steadied a competition that had wandered between 40 and 50 frames per second at a changeless 50.
God of War II went from an ISO to a leaf through port.sh alongside lone generic changes, and all one was a bug or gap that the archetypal equivalent had merely not exercised.
It started alongside silence. The game's audio controller drives its tick from among the I/O processor's hardware timers, which nothing had implemented, so the controller never blended a sample. The intro following the difficulty list was a disorder of huge flat polygons, and whenever the gameplay was concerning to commencement the equivalent wrecked following its render catalog ran former the end of recollection and overwrote its own vertical blank handlers. Following a corrupted connected catalog rear alongside hardware watchpoints led to the recompiler: whenever a branch was the final education of a 16 KiB unit, the way that does not obtain the branch continued in the next component at the postpone slot alternatively of following it, operating that education twice, or operating the education a branch-likely should have skipped. That fixed the collision and the equivalent reached combat, motionless alongside elephantine polygons complete parts of the screen. Those came from triangles that should have been culled. God of War II's microcode says the clip flags three instructions following the CLIP that sets them, and the vector component example made them apparent lone following four, which Gran Turismo 4, whose microcode continually waits longer, could not have noticed.
Along the way it additionally needed a footwear way akin the BIOS gives, a component loaded from recollection alternatively of the disc, and the scratchpad DMA's destination sequence mode.
You need git, CMake, Ninja, a C++20 compiler (clang is preferred and picked up automatically), Python 3, and 7-Zip if your disc depiction is in a .7z. Everything has been run on Linux on x86-64. The archetypal run installs the Emscripten SDK into tools/emsdk, or uses the one EMSDK points to, and downloads wgpu-native for the headless test runner. Expect concerning 300 MB of disk area per equivalent for the generated code and the builds; the disc depiction itself is linked, not copied.
git copy 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 # http://localhost:8620/
port.sh takes a disc depiction (an ISO, or a ZIP or 7z archive holding one) and does the entire job. It says the footwear program named in the disc's SYSTEM.CNF, translates its device code to C++, and afterward runs the equivalent headless a few times. Static inspection cannot see all function a equivalent reaches through pointers, so all run writes downward the addresses it could not find, the manuscript adds them as admission points and translates again, until a run gets through without new ones. The identical runs document the vector component microprograms the equivalent uploads, which are compiled onward of period as well. Last it builds the leaf into build/web-mygame. God of War II goes from ISO to leaf in three and a fractional minutes on an 8-core laptop.
The exploring runs media commencement and afterward cross all five seconds, which gets most games through their heading screens, and put an bare recollection cardstock in slot 1 the way the leaf does, so they prosecute the way a participant takes. --press 900:cross replaces that alongside your own inputs and --frames sets how lengthy all run lasts. Frames from the final run district in games/mygame/shots, which is the quickest way to see how far a new equivalent gets. scripts/build_web.sh mygame rebuilds lone the page.
Set your expectations accordingly: two games have been brought up so far. Another equivalent volition most apt halt at a scheme call or an IOP archive function nobody implemented yet, which the runner logs as [sys] unhandled syscall or [iop] missing <library> #<n>, or use a GS characteristic the renderer does not grip yet.
The leaf needs WebGPU for the GPU renderer. Chrome and Edge have it on Windows and macOS. On Linux it is off by default in Chrome and Chromium: rotate on chrome://flags/#enable-unsafe-webgpu and chrome://flags/#enable-vulkan and restart the browser, as the leaf tells you whenever it finds no usable adapter. Without WebGPU the application renderer takes over, which is fine for menus and far too dilatory for 3D. On laptops alongside two GPUs, a canvas drawn on the discrete GPU sometimes cannot be shown on the screen; the leaf tries the two adapters and falls rear to copying frames through a 2D canvas.
The keyboard maps the visage buttons to Z (cross), X (circle), A (square) and S (triangle), the shoulders to Q, W, 1 and 2, commencement and choose to Enter and Backspace, the D-pad to the arrow keys and the remaining rod to I, J, K and L. Gamepads activity through the browser's gamepad API, phones get on-screen controls, and F toggles fullscreen. The resolution selector reloads the leaf at the new size.
The leaf puts a recollection cardstock in slot 1 and keeps it in the browser's retention for the location you perform from, so advancement survives a reload. Games format and use it as they would a genuine card. Export card downloads it as a .ps2 document in the layout PCSX2 uses, and Import card puts specified a document rear in the slot, so a cardstock can journey between browsers or arrive from PCSX2. ?nocard starts alongside the slot empty.
The leaf uses SharedArrayBuffer for its threads, so it have to be served cross-origin isolated, alongside Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. scripts/serve.py sends the two headers and supports the Range requests the disc streaming relies on. Anyone who can attain the server can burden the game, so keep it to yourself.
This repository contains lone first code and third-party code under its own license. It contains no equivalent code or data. The generated C++ and the built WebAssembly are translations of the game's executable, so they pertain to the game's owners: keep them on your own device and do not publish them. Use disc images of games you own. Gran Turismo, God of War and PlayStation are trademarks of Sony Interactive Entertainment; this project is not affiliated alongside or endorsed by Sony, Polyphony Digital, Santa Monica Studio or any equivalent publisher.
The VLC tables of the MPEG decoder are adapted from Play! (BSD 2-clause, notice in runtime/src/ipu/NOTICE). PCSX2, Play! and the ps2sdk headers were the references for much of the hardware and scheme behavior emulated here. wgpu-native and Dawn provision WebGPU natively and in the browser, and Emscripten builds the WebAssembly.
The code in this repository is accessible under the MIT License, see LICENSE. runtime/src/ipu/tables.hpp keeps the BSD 2-clause notice of Play!.