A first-person shooter that runs in a browser tab and is trying very hard not to look like it. There are no asset files. Every texture, mesh, sound and skybox in these screenshots is generated by code at load time — nothing is downloaded, nothing is authored in a DCC tool.
Real captures from the running game on an Apple M4, taken by the project's own screenshot harness — not mock-ups.
The hard problem here isn't writing an ambient-occlusion pass. It's that you stop being able to see your own work — you look at a frame for the thirtieth time and it looks fine, because you've been staring at it while it got worse.
So the quality bar is enforced by tooling instead of opinion. A capture harness drives real Chrome on the GPU, steps the simulation by hand so temporal effects converge identically every run, and refuses to produce a misleading frame: it reads Vite's error-overlay shadow DOM so a broken build can never be graded as a bad-looking one, and it raycasts around the camera and fails rather than shooting from inside a wall.
An analyser then scores twelve measurable ways a hobby frame differs from a shipped one — flat untextured area, Laplacian detail density, shadow detail retention, highlight clipping, gradient banding, edge aliasing, hue entropy. Every threshold is calibrated against frames judged by eye, in both directions: it has to pass frames that look good and still fail one that looks bad, or it isn't measuring anything.
It caught its own author being wrong. When five scenarios failed on saturation at once, the metric turned out to be the problem, not the renderer — plain HSV saturation is unstable in shadow, so frames with deep shade scored as garish while looking muted. It's luminance-weighted now.
A hand-written hybrid-forward HDR frame graph. No post-processing library.
| Stage | Technique |
|---|---|
| G-buffer | MRT prepass: view normals + roughness, screen velocity, linear depth |
| GTAO | Horizon-based, cosine-weighted, spatially denoised and temporally accumulated |
| SSR | Hi-Z min-depth pyramid march, roughness-aware cone tracing, IBL fallback on miss |
| TAA | Halton(2,3) jitter, YCoCg variance clipping, closest-depth velocity dilation |
| Motion blur / DOF | Tile-max dilation; physical circle-of-confusion, separate near and far fields |
| Bloom | Six-mip Karis-average dual filter, thresholded scene-referred |
| Composite | AgX (ACES and Khronos-neutral selectable), ASC-CDL, split tone, chroma rolloff |
| Final | Edge-only chromatic aberration, CAS sharpen, luminance-dependent grain, dither |
Ambient occlusion and reflections are not pasted over the finished image. A material injector patches every lit material at Three's shader-chunk include markers, so GTAO multiplies indirect diffuse and specular occlusion only, and SSR replaces the image-based radiance term by confidence rather than adding on top of it.
Physically-based scattering to a PMREM cube, raymarched clouds at half resolution with mip-aware sampling and temporal accumulation, froxel volumetrics with blue-noise jitter, four cascaded shadow maps with texel-snapped stable projections.
Baked on the GPU at boot: domain-warped FBM, Worley aggregate, cavity-driven grime, edge wear, and stochastic sampling to kill visible tiling. Full PBR set per material, normals from a real height field.
Rapier at a fixed 120 Hz with interpolated visuals. Articulated ragdolls with realistic joint limits, Voronoi glass shatter, blast-impulse prop displacement. Ground is a heightfield, not a 180k-triangle trimesh.
Real projectiles, not hitscan — muzzle velocity, drop, drag and travel time, material penetration with energy loss, ricochet at shallow angles. Deterministic per-shot recoil patterns, so they're learnable.
Every sound synthesised in the WebAudio graph. No files. Layered gunshot transients, procedurally generated convolution impulse responses per environment, distance-modelled air absorption.
Acceleration/friction movement with slide, mantle and vault. Gait-cycle weapon bob with footplants synced to audio, trauma-squared camera shake driven by noise, critically damped springs throughout.
| Move · sprint | WASD · Shift |
| Crouch · prone · jump | Ctrl/C · Z · Space |
| Fire · aim | Mouse 1 · Mouse 2 |
| Reload · swap · weapons | R · Q · 1 2 |
| Grenade · melee · use | G · V · F |
| Scoreboard · pause | Tab · Esc |
Any setting works as a URL parameter: ?preset=ultra, ?renderScale=0.75, ?hud=0. A buffer inspector is built in for when something looks wrong and you need to know why: ?rpdebug=ao|normals|velocity|ssr|bloom.
Built by parallel agents against a frozen interface contract. The three rendering-foundation subsystems completed their self-critique loops; the eight gameplay subsystems were cut short partway through, so their code is first-draft and has never been through visual iteration. It renders correctly and you can walk around it — weapons, AI and audio are largely unverified in actual play.
Where it stands against the real thing: materials, tonal response and atmosphere are genuinely close. The weapon viewmodel still reads closer to plastic than machined aluminium, ground cover is sparse, and there's no character work in frame. Nobody would mistake a still from this for Modern Warfare — but it's no longer obvious in 200 milliseconds that it's a browser tab.