Four 3D Games in 3.82MB: Inside Claude's One-Shot Build Pipeline
AI Field Test · Engineering Teardown
~15 min read. Requires some Three.js basics; code snippets are reconstructions based on public descriptions, not repo originals. Focus on the approach.
Look at four things first.
A 1.42MB FPS that calls itself "Domestic 3A", with a title screen, mission briefing, three relays, mechanical enemies, action rating, and results panel.
An 862KB CF transport ship, 6v6, four guns, four difficulty levels, two lighting sets for day and dusk.
A 718KB QQ Speed, four tracks, drift charge, mini boost, double boost, start boost, landing boost — all present.
An 818KB pelican cycling game, procedural modeling, two-bone IK, Verlet scarf, Gerstner ocean, day/night cycle, and 14 achievements.
All four together are 3.82MB. No texture packs, no glTF, no MP3, no backend. Each is an HTML file you open and play.
Three online demos come from the GitHub repo riba2534/claude-opus-5-5-demo. The author's stated setup: Claude Opus 5.5, Claude Code CLI, single session, zero manual code edits. The fourth, "Ji Mao" (Cat), is also a single file, title screen says "developed with Fable5.2 proprietary". It has a public link but no attribution. It's not in that repo, so don't attribute it to the same author.
This post doesn't discuss "AI is amazing". It only breaks down four things: how one-sentence prompts become engineering constraints, what exactly is inside 3.82MB, which pitfalls only appear after generation, and what this method can't do yet.
1. The results: technical baseline of four demos
| Demo | Type | Size | Code organization | Key techniques |
|---|---|---|---|---|
| Ji Mao | FPS | 1.42MB | Single file | Three.js, Bloom, FXAA, ShadowMap, InstancedMesh, WebAudio |
| Transport Ship | FPS | 862KB | 18 modules | Three.js, procedural weapons and map, AI bots |
| QQ Speed | Racing | 718KB | 15 modules | Three.js, track generation script, drift state machine |
| Pelican Cycling | Casual | 818KB | 11 modules | Two-bone IK, Verlet fabric, Gerstner ocean, day/night cycle |
A few easily overlooked numbers:
- Transport Ship: 18 JS modules, 862KB; QQ Speed: 15 modules, 718KB; Pelican: 11 modules, 818KB. Module counts come from the repo README; sizes are by actual file bytes.
- Ji Mao has no module split — just one HTML, 1,422,128 bytes.
- None have external images, audio, or model files. Models are composed from geometry, textures are drawn on Canvas, sounds are synthesized with WebAudio.
"Single file" and "small size" are not the same thing. A single file can be large; small size can still be split into many requests. These four demos achieve both, not through compression but by switching to a different asset production method.
2. The truth about one-shot: four layers of constraints behind one sentence
First, put out the three original prompts.
Pelican:
Generate a 3D page of a pelican riding a bicycle, use all your abilities as much as possible.
Then upload to CDN and give me the access link.
Transport Ship:
As realistically as possible, recreate the transport ship map from CrossFire. I need a real shooting game, generate a 3D page, use all your abilities as much as possible. After finishing, upload to CDN and send me the link.
QQ Speed:
As realistically as possible, recreate the game map from QQ Speed. I need a real QQ Speed game, including all key binds and drift mechanics, generate a 3D page, use all your abilities as much as possible. After finishing, upload to CDN and send me the link.
All three are short, but information-dense. Breaking them down reveals four layers of constraints:
| Words in prompt | Corresponding engineering action |
|---|---|
| "As realistically as possible" | First search the web for official map layout, key binds, rules, then start |
| "Include all key binds and drift mechanics" | Input system, vehicle physics, drift/nitro state machine — not just visuals |
| "Generate a 3D page" | Runs directly in browser, single-file delivery, no local environment dependencies |
| "Upload to CDN, send me the link" | Build, deploy, cache purge, domain and cert — full chain |
| "Use all your abilities as much as possible" | Let the model choose tech stack, asset strategy, testing approach |
The last one is the most valuable. It hands "what to do, how to do it, how to verify" all to the model.
So one-shot is not "one API call produces a page". Its precise meaning is: complete the loop from research, design, coding, testing, to deployment within one session. There are still failures, fixes, and re-runs in between — just that the model strings these steps together itself, without a human taking over piece by piece.
The repo also has several hard rules:
- Model: Claude Opus 5.5, run in Claude Code CLI, 1M context, xhigh reasoning.
- Rendering: Three.js r186, build with esbuild, finally inlined to one HTML.
- Models, textures, animations, audio all procedurally generated; no external assets.
- Deploy to Cloudflare Pages; even the README was written by the model.
- Source is exactly what the model output; no manual edits.
These rules determine every subsequent technical choice. No external assets means procedural; single file means inlining; one-sentence delivery means testing and deployment also go to the model.
3. How 3.82MB fits four games
A conventional 3D web game asset directory looks something like: glTF models, PNG or KTX2 textures, MP3 audio, animation clips, fonts. Four games would easily be tens of MB.
These four demos have an empty asset directory.
3.1 Geometry: parametric generation, not modeling export
Containers are boxes, guardrails are cylinders, tires are toruses, trees are cones plus cylinders. These shapes stack from Three.js primitives. Parameterizing them allows batch generation of variants in different sizes and colors.
Repeated objects suit InstancedMesh. Containers in the port, trees along the track, chairs in the stands — drawing the same geometry many times doesn't make draw calls grow linearly with count. That's one key reason a single file can still hold frame rate.
3.2 Textures: drawn with Canvas 2D, not PNG
Container numbering, weight limits, inspection dates — these details are not PNGs. They're drawn directly on Canvas. After drawing, convert to CanvasTexture and cache on demand, avoiding re-creation every frame.
// Procedural texture cache: draw only once per key
const cache = new Map();
function canvasTexture(key, draw, size = 512) {
if (cache.has(key)) return cache.get(key);
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
draw(ctx, size);
const tex = new THREE.CanvasTexture(canvas);
tex.colorSpace = THREE.SRGBColorSpace; // color space since r152+
tex.anisotropy = 4; // don't blur at oblique angles
cache.set(key, tex);
return tex;
}
The benefit is not saving a few images; it's "mutability". The same container function, given different numbers, weights, colors, generates an entire yard. With PNGs, you'd need to make dozens of images first.
Cost: first frame has to draw. So real projects pre-generate common textures during loading, not when entering the game.
3.3 Audio: oscillators and noise, not MP3
Gunshots, engines, waves, bird calls — all synthesized with WebAudio. Noise plus filters for gunshots, low-frequency oscillators for engines, multiple sine waves stacked for music. Saves size, saves copyright.
Cost: "how close it sounds" depends entirely on parameter tuning. Synthesizing a convincing gunshot is no easier than finding a sound effect.
3.4 Bundling: esbuild inlines into one HTML
// build.mjs — inline JS into HTML, producing a double-clickable file
import { build } from 'esbuild';
import fs from 'node:fs';
const template = fs.readFileSync('index.html', 'utf8');
await build({
entryPoints: ['src/main.js'],
bundle: true,
minify: true,
format: 'iife',
target: ['es2020'],
write: false,
plugins: [{
name: 'inline-html',
setup(b) {
b.onEnd((result) => {
const js = result.outputFiles[0].text;
const html = template.replace('<!--APP-->', `<script>${js}</script>`);
fs.writeFileSync('dist/index.html', html);
});
},
}],
});
write: false keeps the output in memory; the plugin replaces a template placeholder with the string, and writes once at the end. No separate JS/CSS requests.
Single file is not a belief; it's a delivery constraint. It has clear pros and cons:
| Dimension | Single file | Conventional split |
|---|---|---|
| Deploy | One file, zero config | Needs static server, MIME, cache policy |
| First package | One request, everything at once | Can split by route/scene |
| Cache | Change one character, whole file invalid | Third-party libs can be cached long-term |
| Debug | Sourcemap requires extra handling | Clear module boundaries |
| Review | A several-thousand-line file | Review per module |
| Suitable for | Demo, vertical slice, teaching, portfolio | Long-term operation, large worlds, multiplayer |
These four demos are all "vertical slices": complete gameplay loop, limited content volume. Single file is a fit.
4. Four demos, four engineering challenges
4.1 Ji Mao: 1.42MB of "3A syntax"
The most 3A-like thing about Ji Mao isn't the visuals; it's the flow.
Title screen has mission code B4, squad name, time 05:42; entering the game gives a mission briefing: "Harbor defense system failed. Cut three relays, break the blockade, and extract by boat." The three relays are in the yard, warehouse, and dock; enemies are mechanical seagulls, mechanical hounds, mechanical guards; the extraction boat is at the south dock gangway.
After finishing, an action rating: perfect assault, precise decisive, steady, perilous. Results panel statistics: time, hit rate, mechanicals destroyed, retries. Death screen too — survival time and relays cut.
This is nothing new; 3A FPS has used it for years. The new part is it's packed into 1.42MB.
| Subsystem | Implementation |
|---|---|
| Rendering | Three.js + Bloom + FXAA + ShadowMap |
| Level | Harbor single level, three relays, linear progression |
| Combat | Rifle, melee, hit feedback and directional damage indicator |
| Enemies | Mechanical seagull, mechanical hound, mechanical guard |
| UI | Title, briefing, pause, settings, results, death |
| Input | Keyboard/mouse, gamepad, touch — three mapping sets |
| Audio | WebAudio synthesized ambient, gunshots, radio |
Technically it's standard pipeline: Bloom for neon and signal lights, FXAA for anti-aliasing, ShadowMap for harbor shadows, InstancedMesh for repeated containers and guardrails. Container numbers, weight limits, inspection dates — all Canvas-drawn.
It also has clear boundaries: single level, three enemy types, no multiplayer, no cinematic set pieces. The project intro says it plainly: "Currently the project is still under continuous development and optimization; there is still a gap from international top-tier 3A standards."
That sentence is more honest than any technical metric. It's not 3A; it's a development experience version using 3A's syntax.
4.2 Transport Ship: search guides first, then build
CF's transport ship is a map with strong memory. The model didn't build from memory; it searched official layout and guides before starting.
The structure that ends up in code:
| Area | Role | Cost |
|---|---|---|
| Bow/stern cabins | Both spawns | Few exits, easy to camp |
| Mid V-shaped slant container | Main combat zone | Crossfire, long exposure |
| L-shaped box stacks on both sides | Flank advance | Narrow view, many corners |
| Left/right one-way container tunnels | Flanking behind | Single entrance, hard to retreat |
| Second floor above tunnels | Sniper points | Fixed position, easy to pre-aim |
These boxes aren't decoration; they're map design language. Every lane gives you one advantage while charging a cost.
Parameters are generous: 4v4, 6v6, 8v8; target kills 30, 50, 100; difficulty easy, normal, hard, hell; primary weapons AK-47, M4A1, AWM, MP5; lighting day, dusk.
18 modules and 862KB must fit the map, guns, ballistics, recoil, aiming, reloading, weapon switch, melee, throwables, AI, HUD, scoreboard. Module splitting is necessary here; one file would become unmanageable later.
Its hardest part isn't modeling; it's "likeness". Gun proportions, aiming FOV, recoil rhythm, AI push routes — any off, and players of CF spot it instantly.
4.3 QQ Speed: Fingertip feel is a state machine, not effects
QQ Speed's key binds and mechanics are explicitly named in the prompt. This part best demonstrates engineering ability.
| Mechanic | Trigger | Effect |
|---|---|---|
| Drift | Shift + direction | Charge boost, change car posture |
| Nitro | Charge full | Max two stored, gives acceleration |
| Mini boost | Tap up at drift exit | Short acceleration |
| Double boost | Mini + nitro | Stacked acceleration |
| Start boost | Tap up at countdown GO | Start acceleration |
| Landing boost | Tap up at landing from jump | Landing compensation |
| Speed strip | Blue arrow | Direct acceleration |
| Reset | R | Return to track |
Writing these as code, the core is a state machine:
// Pseudo-code: only state transitions, not full vehicle physics
const S = { IDLE: 0, DRIFT: 1 };
let state = S.IDLE;
let charge = 0;
let nitro = 0;
function onFrame(dt, input) {
// 1. Must meet speed condition to start drift
if (state === S.IDLE && input.shift && input.speed > 40) {
state = S.DRIFT;
charge = 0;
}
// 2. Drift charges; releasing Shift settles nitro
if (state === S.DRIFT) {
charge = Math.min(1, charge + dt * 0.9);
if (!input.shift) {
state = S.IDLE;
if (charge >= 0.85) nitro = Math.min(2, nitro + 1);
}
}
// 3. Mini boost at steering-back; holding nitro stacks as double
if (state === S.IDLE && input.justReleasedShift && input.up) {
const double = input.nitro && nitro > 0;
if (double) nitro -= 1;
boost(double ? 1.6 : 1.0);
}
}
Beyond the state machine, two details:
First, fixed timestep. Drift timing, nitro duration, collision detection must not follow frame rate, otherwise 120Hz and 60Hz feel different.
const STEP = 1 / 120;
let acc = 0;
let last = performance.now();
function loop(now) {
const dt = Math.min(0.1, (now - last) / 1000);
last = now;
acc += dt;
while (acc >= STEP) {
update(STEP);
acc -= STEP;
}
render(acc / STEP); // interpolate, avoid visual jitter
requestAnimationFrame(loop);
}
Second, input buffering. If the player taps up 100ms before exiting a corner, that press must not be swallowed because the car hasn't steered back. Keep a small input queue and consume it on the steering-back frame.
Track isn't hand-placed either. The author first generated a top-down view with a script, checked corner angles and intersections, then built the road in 3D. Four tracks: 11 City, Aegean Love, Pharaoh Pyramid, Snow Adventure; eight car colors; three AI levels; 1/2/3/5 laps; race and item modes. 718KB, the smallest of the four.
4.4 Pelican: the hardest part is there's no IP to copy
CF and QQ Speed have tons of public material: maps, key binds, guides, videos. The model's job is "recreation".
The pelican has none. A white pelican with helmet and sunglasses rides a vintage bike, a wicker basket on the front, chasing fish along a coastal road. This image has no existing reference; every choice must be made by itself.
| Technique | Approach |
|---|---|
| Pelican | Procedural modeling, no external model |
| Pedaling | Two-bone IK, foot follows pedal |
| Scarf | Verlet cloth, affected by speed and wind |
| Ocean | Gerstner waves, multiple layers |
| Sky | Day/night cycle, dusk to starry |
| Music | Real-time browser synthesis, rhythm follows cadence |
| Content | 14 achievements, 5 camera modes, fish-catching mini game, bell |
Two-bone IK is the most typical "looks simple, writes full of constraints" thing here. Known hip joint, pedal position, thigh and shin lengths — find knee position:
// Two-bone IK: intersection of two circles, pick the forward one
function solveKnee(hip, pedal, thigh, shin, bendDir) {
const d = hip.distanceTo(pedal);
// Law of cosines: projection length of knee on hip-pedal line
const a = (thigh * thigh - shin * shin + d * d) / (2 * d);
const h = Math.sqrt(Math.max(0, thigh * thigh - a * a));
const base = hip.clone().lerp(pedal, a / d);
const axis = new THREE.Vector3().subVectors(pedal, hip).normalize();
// Project bendDir perpendicular to the bone axis; knee bends that way
const bend = bendDir.clone().addScaledVector(axis, -bendDir.dot(axis)).normalize();
return base.addScaledVector(bend, h);
}
The formula is just the first step. The real difficulty is constraints: pedal trajectory is determined by crank angle, foot must stay on the pedal, knee must not hyperextend, body must gently bob with cadence. Any one of these off, and viewers instantly feel "fake".
The ocean uses Gerstner waves; the vertex shader stacks several layers of waves with different directions and wavelengths:
// Vertex shader: single Gerstner wave; actual stacks 3–4 layers
vec3 gerstner(vec3 p, vec2 dir, float steepness, float wavelength, float speed, float t) {
float k = 6.28318 / wavelength;
float c = sqrt(9.8 / k); // deep-water wave speed
vec2 d = normalize(dir);
float f = k * (dot(d, p.xz) - c * speed * t);
float a = steepness / k;
return vec3(d.x * a * cos(f), a * sin(f), d.y * a * cos(f));
}
The scarf uses Verlet: each particle keeps current and previous position, maintains length via constraint iterations, then adds wind. No physics engine needed; a few dozen lines make it flutter.
Background music is synthesized in real time, tempo follows cadence. That's clever: the on-screen pelican pedals faster, music speeds up, and viewers think the whole scene is "alive".
5. Three pitfalls: problems that only appear after generation
The most valuable part of these four demos is the problems they expose; what they did right is less important.
5.1 320-second soak test
The pelican ran a 320-second soak test before launch, spanning a full day/night cycle. Check items aren't frame rate: geometry count, texture count, and JS heap are the focus.
- Geometry and texture counts stayed constant; no leakage over time.
- JS heap stable at 16–26MB.
- Crossing the 36km distance modulo boundary caused no numeric jumps.
"One run is fine" and "one day is fine" are two different things. The most common 3D bug is creating a geometry or material per frame; memory explodes in minutes. Soak testing catches this.
5.2 Intermittent black screen after release
More troublesome: intermittent black screens that only appear after going live.
The author's approach was to inject NaN to reproduce, eventually pinpointing Bloom: invalid pixels spread during the bloom pass, turning the whole screen black. Fix in two steps:
- Add a cleanup pass before bloom that clamps NaN/Inf pixels.
- Fix the Fresnel calculation on the ocean, removing the source of invalid values.
// Cleanup before bloom: NaN via x != x, Inf via upper clamp
vec4 c = texture2D(tDiffuse, vUv);
if (any(notEqual(c, c))) {
c = vec4(0.0); // NaN
}
c = min(c, vec4(64.0)); // Inf clamp, 64 far above normal HDR range
gl_FragColor = c;
This kind of bug is hard to find by "reading code". It needs real execution, reproduction conditions, and knowledge of HDR pipeline edge cases.
5.3 12 issues before delivery
The repo also mentions a detail: before delivery, a sub-agent independently reviewed and found 12 issues, all fixed.
This shows one-shot's "one" means session count, not attempt count. The model still iterates, just within the same session: it looks up information itself, writes tests itself, runs headless browser itself, fixes bugs itself, redeploys itself.
Human work becomes defining acceptance criteria, reviewing results, making decisions.
6. Capability boundary: what can be copied, what can't
Looking at the four demos together, the boundary is clear.
| Can do | Cannot do |
|---|---|
| Genres with abundant public references: CF maps, QQ keys, 3A FPS camera | Original gameplay, emotion, and feel with no reference |
| Combination of mature tech: Three.js + IK + cloth + ocean | Animation, level design, balance needing a decade of accumulation |
| Single-player vertical slices: one level, one map, one track | Long-term content, multiplayer sync, anti-cheat, operations |
| Procedural assets: geometry, Canvas textures, synthesized audio | 3A motion capture, voice acting, scanning, art consistency |
| Rapid validation: one session to get a playable link | Large-scale team collaboration and project management |
The threshold for 3A has never been visuals; it's money, people, and time. A team of hundreds, three to five years, hundreds of millions in budget, motion capture, scanning, voice, level design, balance, optimization, QA, publishing, operations.
A 1.42MB HTML, however similar, is only "3A visual syntax", not 3A's production system.
But it's moving another cake: the demo layer.
Previously, a game idea had to survive by writing a PPT, drawing concept art, making a gray-box, then finding money to hire a team to build a playable version. Now, a designer, a frontend engineer, or even a non-coding designer can get a running, viewable, linkable vertical slice in one session.
Investors and bosses no longer have to imagine; they click the link.
Once this threshold collapses, the division of labor in the game industry gets rearranged. People making concept demos become fewer; those who can judge "what's worth making" become more expensive; execution value goes down, aesthetics, design, and project management go up.
For frontend engineers, the impact is more direct: being able to only call Three.js APIs becomes less valuable; what's valuable is knowing which effects are worth building, how to make them look real, and where the performance budget goes.
7. To reproduce: a prompt and an acceptance checklist
If you want to run it yourself, don't start with "write a function for me". Give a complete delivery goal.
Make a directly openable 3D <type>, theme is <theme>.
Requirements:
1. Single-file HTML, Three.js, no external images/models/audio;
2. Recreate <specific map/gameplay/key binds>, search official material first;
3. Include menu, settings, results, mobile touch;
4. Run one match with headless browser, check memory, frame rate, boundaries;
5. Deploy to <platform>, send me the link.
This is longer than the original three-sentence prompts, but better for engineering delivery. The original prompts test the model's capability ceiling; this one sets acceptance criteria.
I generally check ten items:
| Check | Pass standard |
|---|---|
| First screen | Interactive picture appears within 3s, not white screen |
| Controls | At least keyboard/mouse and touch; key hints |
| Gameplay | Has start, goal, results, restart |
| Visuals | No obvious clipping, flicker, Z-fighting |
| Performance | 30fps+ on mid-range phone, 60fps on desktop |
| Memory | Play 10 minutes, heap does not grow |
| Boundaries | Long time, long distance, repeated restarts no errors |
| Audio | Only plays after first interaction, no autoplay |
| Deploy | Link accessible, refresh no 404 |
| Copyright | Fan/original clearly marked, no stolen assets |
A few easy-to-flip places:
| Problem | Cause | Handle |
|---|---|---|
| First package too large | Single file stuffs libraries and assets | Compress, delay generation, lower asset precision |
| Mobile black screen | WebGL context limits / VRAM | Lower resolution, lower quality, detect context loss |
| Audio silent | Browser autoplay policy | Resume AudioContext after first click/touch |
| Touch unresponsive | Only keyboard listeners | Unified input layer, map touch to actions |
| Old version after deploy | CDN/browser cache | Hash file names or add version query parameter |
| Fan copyright violation | Using original textures/models | Only recreate gameplay and layout; all assets self-made |
8. Quick reference
| Task | Tool |
|---|---|
| Single-file 3D delivery | Three.js + esbuild inline |
| No art assets | Procedural geometry + CanvasTexture + WebAudio |
| Recreate a map | Search official layout, build gray-box, add details |
| Racing feel | State machine + fixed timestep + input buffering |
| Character pedaling | Two-bone IK + pedal trajectory constraint |
| Ocean | Gerstner wave multi-layer stacking |
| Scarf/flag | Verlet cloth |
| Long-run stability | Soak test + geometry/texture/heap monitoring |
| HDR black block | NaN/Inf cleanup pass + root cause |
| Delivery acceptance | Headless browser one match + accessible link |
A single sentence can generate a game, but cannot generate a "game worth playing".
What the model is best at are genres with abundant public references: CF maps, QQ Speed key binds, 3A FPS camera language. The internet is full of tutorials and screenshots; it only needs to reorganize them into code. What's truly hard is things without reference — a new mechanic, an emotion no one has seen, a feel that takes a decade to refine.
So the pelican is the most precious. It has no IP to copy; every choice must be made on its own.
All four games are playable online now:
- Pelican cycling: https://claude-opus-5-5.riba2534.cn/
- CrossFire · Transport Ship: https://claude-opus-5-5-cf-transport-ship.pages.dev
- QQ Speed: https://claude-opus-5-5-qqfeiche3d.pages.dev/
- Ji Mao: https://jimiao-3a-v1.pages.dev/ , 1.42MB, development experience version
Source: https://github.com/riba2534/claude-opus-5-5-demo
This is post 1 in the "AI Field Test" series. Next: breaking this one-shot flow into a directly copiable prompt template.
Note: CF and QQ Speed related demos are fan tribute works; copyright belongs to the original studios; Ji Mao is a development experience version, not an actual commercial 3A project.