[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-93885":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":16,"stars30d":16,"stars90d":14,"forks30d":14,"starsTrendScore":17,"compositeScore":18,"rankGlobal":9,"rankLanguage":9,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":9,"pushedAt":9,"updatedAt":24,"readmeContent":25,"aiSummary":9,"trendingCount":14,"starSnapshotCount":14,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},93885,"Claude-of-Duty","mshumer\u002FClaude-of-Duty","mshumer","A Call of Duty-quality FPS in Three.js, built from a single prompt.",null,"JavaScript",2313,337,4,0,786,1671,3243,109.49,"MIT License",false,"main",true,[],"2026-07-30 04:02:14","# Claude of Duty\n\nGet updates [here](https:\u002F\u002Fshumer.dev\u002Fnewsletter).\n\nA first-person shooter built in the browser with Three.js r180 and WebGL2. Roughly\n55k lines across 11 subsystems, written by a fleet of AI agents under orchestration.\n\n**There are no art assets.** Every texture, mesh, animation and sound is generated\nprocedurally at load time from code. No models, no HDRIs, no image files, no audio\nfiles. The only runtime dependency is `three`.\n\n```bash\nnpm install\nnpm run dev          # http:\u002F\u002F127.0.0.1:5173\n```\n\nClick the canvas to lock the cursor. WASD move, mouse aim, LMB fire, RMB ADS,\nR reload, Shift sprint, Ctrl crouch, Space jump, Q\u002FE lean, Esc release.\n\n## What's in it\n\n| subsystem | what it does |\n|---|---|\n| `render` | HDR pipeline, cascaded shadow maps in a `sampler2DArray` with texel snapping and PCSS contact hardening, MRT depth\u002Fnormal\u002Fvelocity prepass, GTAO, TAA with YCoCg variance clipping, tile-dilated motion blur, Karis bloom pyramid, GPU EV100 metering, procedural 33³ grade LUT, AgX composite |\n| `materials` | GPU texture forge: 19 procedural surfaces (concrete, brick, plaster, asphalt, sand, rusted\u002Fpainted\u002Fbrushed metal, wood, fabric, burlap, glass…), periodic noise so everything tiles seamlessly, Sobel height→normal, parallax occlusion mapping, triplanar projection, curvature-driven edge wear |\n| `sky` | Atmospheric scattering, time of day, PMREM environment generation, volumetric fog and light shafts |\n| `world` | ~120×120 m market street: modular building kit with real wall thickness, enterable interiors, several hundred instanced props |\n| `physics` | Written from scratch, no library. Binned-SAH BVH (29k tris → 14k nodes in 22 ms, 0.25 µs\u002Fraycast), swept-capsule character controller with a 5-plane crease stack, impulse rigid bodies with CCD, PBD ragdolls, multi-layer bullet penetration |\n| `player` | Movement state machine, slide\u002Fmantle\u002Flean, camera feel |\n| `weapons` | Procedural weapon geometry, viewmodel rig, ADS, spring recoil, procedural reloads, ballistics with travel time and drop |\n| `fx` | GPU particles, decals, tracers, muzzle flash, explosions |\n| `ai` | Skinned soldiers, navmesh pathing, perception, cover behaviour, ragdoll death |\n| `ui` | DOM\u002FCSS HUD: crosshair, hitmarkers, minimap, compass, killfeed |\n| `audio` | Web Audio synthesis — no sound files. Layered weapon fire, convolution reverb, HRTF spatialisation, occlusion |\n\n`ARCHITECTURE.md` is the contract the agents worked against: subsystem interface,\ndirectory ownership, the cross-subsystem event vocabulary, and shared surface types.\n\n## Tooling\n\nThe interesting part of this repo is arguably the harness, not the game.\n\n| tool | purpose |\n|---|---|\n| `tools\u002Fcapture.mjs` | Screenshot one named shot via GPU-backed headless Chromium |\n| `tools\u002Fshotset.mjs` | All 11 shots in one session — fast review set |\n| `tools\u002Fbaseline.mjs` | **Reproducible** capture: each shot in an isolated page, fixed frame budget. Bit-identical across runs |\n| `tools\u002Fimagediff.mjs` | Per-pixel gate. Exits non-zero if any pixel moved |\n| `tools\u002Fprofile.mjs` | Gameplay profiler at real device pixel ratio. Frame-time *distribution* and hitch attribution via per-frame WebGL program counts |\n| `tools\u002Fplaytest.mjs` | Scripted movement\u002Ffire smoke test |\n\nTwo findings worth recording, because both invalidated earlier measurements:\n\n**Median frame time hides the actual problem.** A static-camera benchmark reported\n94 fps while the game was unplayable. Real gameplay at Retina DPR (internal 3.34 MP,\nnot 2.07) ran 12–17 fps with **728–1236 ms stalls** caused by 34+ WebGL programs\ncompiling lazily mid-frame. `profile.mjs` reports p50\u002Fp95\u002Fp99 and attributes each\nhitch, which is what surfaced it.\n\n**Captures were not reproducible.** `shotset.mjs` reuses one page across all 11\nshots, so particle age, decal buffers and exposure state leak forward — two identical\nruns differed on 10 of 11 shots. `baseline.mjs` isolates each shot in a fresh page,\nwhich is bit-identical and is what makes `imagediff.mjs` a usable gate.\n\n## Performance\n\nMeasured on an Apple silicon laptop at 1512×982, DPR 2 (3.34 MP internal), `ultra` preset,\n3 runs, gameplay in motion with AI and firing active:\n\n| | before optimization | after |\n|---|---|---|\n| fps p50 | 12–17 | **28–30** |\n| fps p99 | 4–9 | **14–17** |\n| worst frame | 728–1236 ms | **66–82 ms** |\n| shader compiles during play | 34–35 | **0** |\n| boot | ~9–12 s | **3.7–4.6 s** |\n\nThe optimization pass was constrained to produce **zero visual change**, enforced by\n`imagediff.mjs` rather than by assertion — the shipped build is bit-identical to its\npre-optimization reference across all 11 shots.\n\nShader pre-warm (`src\u002Fcore\u002Fprewarm.js`) is what removed the stalls. Making it\n*provably* pixel-neutral required first fixing subsystems that animated off\n`performance.now()` instead of the engine clock, since any change to boot duration\notherwise shifted output.\n\n## Honest assessment\n\nThe goal was to match a modern Call of Duty. **It does not.**\n\nEleven independent adversarial critics scored the frames against that bar. Scores\nwent 3.59 → 4.14 → 4.05 → **5.05** out of 10. Two shots reached \"CLOSE\"; the rest\nremain \"AMATEUR\". In a blind A\u002FB, **every critic in every round picked the real Call\nof Duty frame.**\n\nWhere it falls short, specifically:\n\n- **Hands.** Blocky finger slabs that don't convincingly grip the weapon.\n- **Material richness.** Surfaces read as procedural noise rather than photographed\n  reality at close range — the ceiling of generating texture from code.\n- **Characters.** Enemies read as mannequins at distance.\n- **Indirect light.** An approximation, not real GI.\n- **Frame rate.** 28–30 fps at Retina. The art passes tripled geometry cost\n  (5.9M → 11.3M triangles) and optimization recovered about half.\n\nA known root cause remains unfixed: the viewmodel light rig in `render\u002Findex.js`\ndelivers roughly 20× the irradiance per unit albedo that the world does — a plain\n*black* material in the view scene renders at L=110 against a background of 91,\npurely from F0=0.04. Every weapon albedo is cheated to a third of physical to\ncompensate, which caps material separation on the most-looked-at object in the game.\n\n## Process note\n\nSequential single-owner passes beat parallel fan-out decisively. Three rounds of six\nagents each owning one directory moved the score +0.46 and left frame-ruining defects\n*higher* than they started (60 → 47 → 66), because tonemapping, sky and indirect light\nare one coupled system and isolated agents kept breaking each other's assumptions.\nOne sequential pass with a single owner per coupled concern moved it +1.00 and cut\ndefects 66 → 26.\n\nThe most valuable single result came from an agent contradicting its own brief. Every\ncritic for three rounds reported the weapon as \"untextured\". It wasn't — it was\nspecular-dominated, with the diffuse term measured at L=26 against a shipped L=67.\nPrior rounds had been crushing albedos to fight bright-part complaints, which killed\ndiffuse and made it worse. The fix was the opposite of what was asked for.\n",2,"2026-07-27 02:30:05","CREATED_QUERY"]