27:18Gemini 4 Hydrogen: Cheaper Tokens, More Expensive Answers?
Nov 13, 2026 · YouTube
18:40ERNIE 5.5 Flash Preview vs GLM 5.3 Flash: Baidu's new model is very impressive!
Nov 11, 2026 · YouTube
36:22Fable 5.2 robbing me blind, plus: Nex AGI's N2.6 vs GLM 5.3 Flash
Nov 11, 2026 · YouTube
14:03Gemini 4 Carbon: Can it handle my challenges? (First look with one-shot coding tests)
Nov 10, 2026 · YouTube
47:51Ranking major U.S. Open-Weights Models. (Where does Ripple 65B fit in?)
Nov 6, 2026 · YouTube
10:57"Crouton Alpha": the latest super-efficient coding model by Meta?
Nov 3, 2026 · YouTube
38:58Testing Particle-1: a post-trained DeepSeek V4.1 Flash specializing in coding and efficiency
Oct 28, 2026 · YouTube
22:14GPT-6.1 Terra Pro vs Terra: How Much Better Is Pro?
Oct 27, 2026 · YouTube
29:55Kimi K3.1 Pro Looked Incredible. Then I Kept Testing.
Oct 26, 2026 · YouTube
31:28Is this company reselling GPT Sol 6.1 at a loss (for non-agentic/text-only)?🤔 Is this sustainable?
Oct 20, 2026 · YouTube
17:58Nemotron 3.5 Super VL vs Opus 5.5: Testing NVIDIA’s new coding juggernaut
Oct 15, 2026 · YouTube
16:04Testing a *massive* new open-weights model 🤯: Muse Spark Open (vs. Nemotron, Opus 5.5, and more)
Oct 15, 2026 · YouTube
16:43Gemini 4 Argon: Great 🐈 Animation, but a broken escalator. (Coding challenge vs Opus 5.5, Sol 6.1)
Oct 13, 2026 · YouTube
16:37Meta's new "Muse Wonder" AI is impressive... but can it compete with Anthropic's models?
Oct 12, 2026 · YouTube
11:12WOW... Haiku 5.5 is very impressive. RIP OpenAI?
Oct 7, 2026 · YouTube
22:47Mistral Large 4 Is Cheap. But Is It Good Enough to Compete?
Oct 6, 2026 · YouTube
22:02Is Pareto 26.10 Preview Just Routing to GPT Sol? I Tested It Against Sol 6.1 Pro
Oct 1, 2026 · YouTube
11:22I Tested Sonnet 5.5 Against Opus 5.5. Is It Actually Cheaper?
Sep 28, 2026 · YouTube
30:00Did Elon Musk's team "train the gay away"... and make Grok 4.7 worse?
Sep 26, 2026 · YouTube
10:58So... how much better is Claude Opus 5.5 vs. GPT-6 Sol, really?
Sep 23, 2026 · YouTube
9:38Claude Opus 5.5... the BEST new model for coding!! 🤩
Sep 22, 2026 · YouTube
29:00Grok 4.7: improved, but GLM 5.3 FlashX beat it anyway (benchmark against Gemini, Sol, etc.)
Sep 22, 2026 · YouTube
16:54Stealth Union Alpha: Is this another cheap GPT model? (better than Luna, cheaper than Terra?)
Sep 16, 2026 · YouTube
16:45DeepSeek V4.1 Flash Is Shockingly Good for the Price
Sep 10, 2026 · YouTube
17:14How much better is GPT-6 Astra over Claude Opus 5? Real Website and Coding Tests
Sep 5, 2026 · YouTube
29:13Implementing a multiplayer game with GPT-6 Astra (+ Gemini 3.8, Opus 5, Sol Pro)
Sep 5, 2026 · YouTube
19:42Meta Muse Spark 1.3 Review: A Huge Leap Forward
Sep 2, 2026 · YouTube
17:31Testing Gemini 3.8 Flash on Real Coding Challenges (vs. other models)
Sep 2, 2026 · YouTube
19:45Fable 5.1 writes an animated novel using Ren'Py... did it do better than Sol Pro 5.6?
Sep 1, 2026 · YouTube
14:09Testing IBM Granite-4.2-8B! Can it name 100 women?
Aug 31, 2026 · YouTube
17:37Tencent Hy4 Preview Tested: Is It Really Better Than Kimi K3 and GLM 5.3?
Aug 28, 2026 · YouTube
23:58Qwen 3.8 Flash Tested: UI, Coding, Physics & more - compared to Opus 5, Sol Pro, Grok
Aug 27, 2026 · YouTube
You are a game developer who ships browser demos that have to work on the first click, in front of an audience, on someone else's laptop.
Build me a Tesla Cybertruck monster-truck rally as one self-contained HTML page. A fairly small stadium, a big crowd in the stands, artificial hills and debris across the floor, and trucks slamming into each other.
Then make it match the spec below exactly. The spec is the challenge; a great-looking rally that ignores it is a miss.
.html page. No build step. Saved to disk and double-clicked, it runs.localStorage, sessionStorage, cookies or indexedDB. It runs in a sandboxed frame where touching them throws.Both are checkable in ten seconds, which is why they are here.
#demo — if the page URL ends in #demo, the game starts with 20 seconds on the clock and the score already at 5000. This must run through the real game state, not a cosmetic overlay. Everything else behaves normally.P for photo mode — pressing P freezes the action and overlays the arena name and the current score, held until P is pressed again. The simulation stops; it does not keep playing behind the card.One sentence: the name of your rally and what it looks like.
Exactly one fenced html block containing the whole page and nothing else.
Exactly four bullets.
#demo sets, and why it is not an overlay.P is pressed — the render loop, the physics step, or both.VOLT DOME — a floodlit night arena where five wedge-nosed Cybertruck monster trucks trade paint over launch ramps, cargo crates and tire stacks while a full 360° bowl of a crowd flickers with camera flashes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>VOLT DOME — Cybertruck Monster Rally</title>
<style>
:root{ --cy:#18e0ff; --or:#ff7a18; }
*{margin:0;padding:0;box-sizing:border-box;}
html,body{height:100%;overflow:hidden;background:#05070c;
font-family:'Segoe UI',system-ui,-apple-system,Arial,sans-serif;
-webkit-user-select:none;user-select:none;}
canvas{position:fixed;inset:0;display:block;z-index:0;}
#hud{position:fixed;top:0;left:0;z-index:10;padding:14px 18px;pointer-events:none;}
#hud .plate{background:rgba(8,12,18,.55);border-left:3px solid var(--cy);
border-radius:8px;padding:10px 16px 12px;box-shadow:0 6px 24px rgba(0,0,0,.5);}
#arena{font-size:24px;font-weight:800;letter-spacing:4px;color:var(--cy);
text-shadow:0 0 12px rgba(24,224,255,.6);line-height:1;}
#arena small{display:block;font-size:10px;letter-spacing:3px;color:#7fb8cc;
font-weight:600;margin-top:4px;}
#stats{display:flex;gap:24px;margin-top:11px;}
#stats .lbl{font-size:10px;letter-spacing:2px;color:#8aa;text-transform:uppercase;}
#stats .val{font-size:26px;font-weight:800;color:#eaf6ff;
font-variant-numeric:tabular-nums;line-height:1;}
#score{color:var(--or);text-shadow:0 0 10px rgba(255,122,24,.5);display:inline-block;}
#score.pop{animation:pop .25s ease;}
@keyframes pop{0%{transform:scale(1);}40%{transform:scale(1.4);}100%{transform:scale(1);}}
#hint{position:fixed;left:18px;bottom:14px;z-index:10;color:#9fb6c4;
font-size:12px;letter-spacing:1px;pointer-events:none;text-shadow:0 1px 4px #000;}
#hint b{color:var(--cy);}
.card{position:fixed;inset:0;z-index:20;display:none;align-items:center;
justify-content:center;background:rgba(4,6,10,.82);color:#eaf6ff;text-align:center;}
.card .box{border:2px solid var(--cy);border-radius:14px;padding:34px 48px;
background:rgba(10,16,24,.85);box-shadow:0 0 60px rgba(24,224,255,.25);}
.card h1{font-size:44px;letter-spacing:6px;color:var(--cy);
text-shadow:0 0 20px rgba(24,224,255,.6);line-height:1.1;}
.card .big{font-size:62px;font-weight:800;color:var(--or);
text-shadow:0 0 22px rgba(255,122,24,.5);line-height:1;}
.card .sub{color:#7fb8cc;font-size:12px;letter-spacing:3px;}
.card p{color:#bcd;letter-spacing:2px;font-size:14px;margin-top:16px;}
</style>
</head>
<body>
<div id="hud">
<div class="plate">
<div id="arena">VOLT DOME<small>CYBERTRUCK MONSTER RALLY</small></div>
<div id="stats">
<div><div class="lbl">Score</div><div class="val" id="score">0</div></div>
<div><div class="lbl">Clock</div><div class="val" id="clock">1:30</div></div>
</div>
</div>
</div>
<div id="hint"><b>WASD</b> / <b>ARROWS</b> drive · <b>P</b> photo ·
<b>M</b> mute · ram rivals & smash crates to score</div>
<div id="photoCard" class="card"><div class="box">
<div class="sub">— PHOTO MODE —</div>
<h1>VOLT DOME</h1>
<div class="sub" style="margin-top:20px">SCORE</div>
<div class="big" style="margin-top:6px"><span id="photoScore">0</span></div>
<p>Press P to resume</p>
</div></div>
<div id="overCard" class="card"><div class="box">
<div class="sub">TIME UP</div>
<h1>VOLT DOME</h1>
<div class="sub" style="margin-top:20px">FINAL SCORE</div>
<div class="big" style="margin-top:6px"><span id="finalScore">0</span></div>
<p>Press R to run it back</p>
</div></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
(function(){
'use strict';
if(typeof THREE==='undefined'){
document.getElementById('hint').textContent='Could not load the 3D engine (three.js CDN blocked).';
return;
}
const PI2=Math.PI*2;
const wrapAngle=a=>Math.atan2(Math.sin(a),Math.cos(a));
/* ---------- tunables ---------- */
const ARENA_NAME='VOLT DOME';
const FLOOR_R=40; // play-space radius (truck centers bounded inside)
const TRUCK_R=2.4; // collision disc radius
const ACCEL=30, MAXF=26, MAXR=12;
const DRAG=0.6, LATDRAG=3.5, TURN=2.1, GRAV=30;
/* ---------- state ---------- */
const state={running:true,paused:false,over:false,score:0,timeLeft:90,demo:false};
if(location.hash==='#demo'){ state.demo=true; state.timeLeft=20; state.score=5000; }
/* ---------- three ---------- */
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x05070c);
scene.fog=new THREE.Fog(0x05070c,70,185);
const camera=new THREE.PerspectiveCamera(60,innerWidth/innerHeight,0.1,500);
const renderer=new THREE.WebGLRenderer({antialias:true});
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.setSize(innerWidth,innerHeight);
document.body.appendChild(renderer.domElement);
scene.add(new THREE.HemisphereLight(0x8899cc,0x0a0a12,0.75));
const dl=new THREE.DirectionalLight(0xffffff,0.85); dl.position.set(30,70,20); scene.add(dl);
const p1=new THREE.PointLight(0x18e0ff,0.5,230); p1.position.set(-45,32,-45); scene.add(p1);
const p2=new THREE.PointLight(0xff7a18,0.5,230); p2.position.set(45,32,45); scene.add(p2);
/* ---------- collections ---------- */
const trucks=[], crates=[], barriers=[], ramps=[], particles=[], flashes=[];
let player=null;
/* ---------- spawn ring ---------- */
const SPAWN_R=15, spawnPoints=[];
for(let i=0;i<5;i++){
const a=Math.PI/2 + i*(PI2/5);
const x=Math.cos(a)*SPAWN_R, z=Math.sin(a)*SPAWN_R;
spawnPoints.push({x,z,heading:Math.atan2(-x,-z)});
}
function farFromSpawns(x,z,m){for(const s of spawnPoints){if((s.x-x)**2+(s.z-z)**2<m*m)return false;}return true;}
function randField(minR,maxR,ms){for(let k=0;k<30;k++){const a=Math.random()*PI2,r=minR+Math.random()*(maxR-minR);const x=Math.cos(a)*r,z=Math.sin(a)*r;if(farFromSpawns(x,z,ms))return{x,z};}return{x:0,z:0};}
/* ---------- arena ---------- */
function buildArena(){
const floor=new THREE.Mesh(new THREE.CircleGeometry(44,64),
new THREE.MeshStandardMaterial({color:0x14161c,roughness:.95}));
floor.rotation.x=-Math.PI/2; scene.add(floor);
const logo=new THREE.Mesh(new THREE.CircleGeometry(6,32),
new THREE.MeshBasicMaterial({color:0x0e1a24}));
logo.rotation.x=-Math.PI/2; logo.position.y=0.01; scene.add(logo);
for(const rr of [10,22,32]){
const m=new THREE.Mesh(new THREE.RingGeometry(rr-0.15,rr+0.15,64),
new THREE.MeshBasicMaterial({color:0x2a3340,side:THREE.DoubleSide}));
m.rotation.x=-Math.PI/2; m.position.y=0.015; scene.add(m);
}
const bound=new THREE.Mesh(new THREE.RingGeometry(FLOOR_R-0.6,FLOOR_R+0.2,72),
new THREE.MeshBasicMaterial({color:0x18e0ff,side:THREE.DoubleSide}));
bound.rotation.x=-Math.PI/2; bound.position.y=0.02; scene.add(bound);
const wall=new THREE.Mesh(new THREE.CylinderGeometry(44,44,3,64,1,true),
new THREE.MeshStandardMaterial({color:0x1b2029,roughness:.9,side:THREE.DoubleSide}));
wall.position.y=1.5; scene.add(wall);
// seating bowl
const bowl=new THREE.Mesh(new THREE.CylinderGeometry(70,46,16,64,1,true),
new THREE.MeshStandardMaterial({color:0x232a35,roughness:.95,side:THREE.DoubleSide}));
bowl.position.y=8; scene.add(bowl);
const rim=new THREE.Mesh(new THREE.TorusGeometry(70,0.5,8,64),
new THREE.MeshStandardMaterial({color:0x18e0ff,emissive:0x0a3a44,roughness:.6}));
rim.rotation.x=Math.PI/2; rim.position.y=16; scene.add(rim);
// outer facade
const facade=new THREE.Mesh(new THREE.CylinderGeometry(72,72,24,64,1,true),
new THREE.MeshStandardMaterial({color:0x0c1017,roughness:1,side:THREE.DoubleSide}));
facade.position.y=11; scene.add(facade);
// crowd (instanced)
const CROWD=4800;
const cGeo=new THREE.BoxGeometry(0.5,0.9,0.5);
const cMat=new THREE.MeshLambertMaterial({color:0xffffff});
const crowd=new THREE.InstancedMesh(cGeo,cMat,CROWD);
const dummy=new THREE.Object3D(), col=new THREE.Color();
for(let i=0;i<CROWD;i++){
const a=Math.random()*PI2, t=Math.pow(Math.random(),0.85);
const rad=46+t*24+(Math.random()-0.5)*1.4;
const y=t*16+0.55+Math.random()*0.3;
dummy.position.set(Math.cos(a)*rad,y,Math.sin(a)*rad);
dummy.rotation.set(0,-a+Math.PI/2,0);
const s=0.8+Math.random()*0.5;
dummy.scale.set(s,s*(0.85+Math.random()*0.4),s);
dummy.updateMatrix();
crowd.setMatrixAt(i,dummy.matrix);
col.setHSL(Math.random(),0.6,0.45+Math.random()*0.35);
crowd.setColorAt(i,col);
}
crowd.instanceMatrix.needsUpdate=true;
if(crowd.instanceColor) crowd.instanceColor.needsUpdate=true;
scene.add(crowd);
// camera flashes in the crowd
for(let i=0;i<48;i++){
const s=new THREE.Sprite(new THREE.SpriteMaterial({color:0xffffff,transparent:true,opacity:.9}));
const a=Math.random()*PI2,t=Math.random();
s.position.set(Math.cos(a)*(46+t*24),t*16+1.5+Math.random()*2,Math.sin(a)*(46+t*24));
s.scale.set(1.3,1.3,1.3); s.visible=false; s.userData.timer=Math.random()*2;
scene.add(s); flashes.push(s);
}
// floodlight towers
for(let i=0;i<4;i++){
const a=Math.PI/4+i*Math.PI/2, x=Math.cos(a)*74, z=Math.sin(a)*74;
const pole=new THREE.Mesh(new THREE.CylinderGeometry(0.5,0.8,30,8),
new THREE.MeshStandardMaterial({color:0x222831,roughness:.8}));
pole.position.set(x,15,z); scene.add(pole);
const panel=new THREE.Mesh(new THREE.BoxGeometry(6,3,1),
new THREE.MeshStandardMaterial({color:0xffffff,emissive:0xffffee,emissiveIntensity:.9}));
panel.position.set(x*0.97,30,z*0.97); panel.lookAt(0,10,0); scene.add(panel);
}
}
/* ---------- obstacles ---------- */
function buildObstacles(){
// ramps
const rShape=new THREE.Shape();
rShape.moveTo(-3,0); rShape.lineTo(3,0); rShape.lineTo(3,2.2); rShape.closePath();
const rGeo=new THREE.ExtrudeGeometry(rShape,{depth:5,bevelEnabled:false}); rGeo.translate(0,0,-2.5);
const rMat=new THREE.MeshStandardMaterial({color:0x2b3340,roughness:.7,metalness:.3});
const rEdge=new THREE.EdgesGeometry(rGeo);
for(let i=0;i<5;i++){
const p=randField(16,32,8), ry=Math.random()*PI2;
const mesh=new THREE.Mesh(rGeo,rMat);
mesh.rotation.y=ry; mesh.position.set(p.x,0,p.z);
mesh.add(new THREE.LineSegments(rEdge,new THREE.LineBasicMaterial({color:0x18e0ff})));
scene.add(mesh);
ramps.push({x:p.x,z:p.z,dx:Math.cos(ry),dz:-Math.sin(ry),trig:3.6});
}
// crates
const cGeo=new THREE.BoxGeometry(1.6,1.6,1.6);
const cMat=new THREE.MeshStandardMaterial({color:0x9c6b34,roughness:.85,metalness:.1});
const cEdge=new THREE.EdgesGeometry(cGeo);
for(let i=0;i<12;i++){
const p=randField(8,34,6), ry=Math.random()*PI2;
const mesh=new THREE.Mesh(cGeo,cMat);
mesh.add(new THREE.LineSegments(cEdge,new THREE.LineBasicMaterial({color:0x2a1a0a})));
mesh.position.set(p.x,0.8,p.z); mesh.rotation.y=ry; scene.add(mesh);
crates.push({mesh,pos:new THREE.Vector3(p.x,0.8,p.z),start:new THREE.Vector3(p.x,0.8,p.z),
vel:new THREE.Vector3(),ang:new THREE.Vector3(),rot:new THREE.Euler(0,ry,0),startRotY:ry,
flying:false,alive:true,cool:0});
}
// tire stacks (solid)
const tGeo=new THREE.TorusGeometry(0.85,0.32,8,16);
const tMat=new THREE.MeshStandardMaterial({color:0x141414,roughness:.95});
for(let i=0;i<9;i++){
const p=randField(10,34,7);
const g=new THREE.Group();
for(let k=0;k<3;k++){const tr=new THREE.Mesh(tGeo,tMat);tr.rotation.x=Math.PI/2;tr.position.y=0.35+k*0.6;g.add(tr);}
g.position.set(p.x,0,p.z); scene.add(g);
barriers.push({x:p.x,z:p.z,r:1.4});
}
// debris (visual clutter)
const dGeo=new THREE.BoxGeometry(1,1,1);
const dMat=new THREE.MeshStandardMaterial({color:0x2a2f38,roughness:1});
for(let i=0;i<30;i++){
const a=Math.random()*PI2,r=Math.random()*36,s=0.3+Math.random()*0.6;
const m=new THREE.Mesh(dGeo,dMat);
m.position.set(Math.cos(a)*r,s/2,Math.sin(a)*r);
m.scale.set(s,s*(0.4+Math.random()),s); m.rotation.y=Math.random()*PI2;
scene.add(m);
}
}
/* ---------- trucks ---------- */
function makeTruck(color){
const group=new THREE.Group();
const bodyPivot=new THREE.Group(); bodyPivot.position.y=0.75; group.add(bodyPivot);
const bodyGroup=new THREE.Group(); bodyGroup.rotation.y=-Math.PI/2; bodyPivot.add(bodyGroup);
const P=[[2.5,0.3],[2.5,0.55],[0.3,1.7],[-2.4,1.05],[-2.4,0.3]];
const shape=new THREE.Shape(); shape.moveTo(P[0][0],P[0][1]);
for(let i=1;i<P.length;i++) shape.lineTo(P[i][0],P[i][1]);
shape.closePath();
const bGeo=new THREE.ExtrudeGeometry(shape,{depth:2,bevelEnabled:false}); bGeo.translate(0,0,-1);
const body=new THREE.Mesh(bGeo,new THREE.MeshStandardMaterial({color,metalness:.55,roughness:.4}));
bodyGroup.add(body);
bodyGroup.add(new THREE.LineSegments(new THREE.EdgesGeometry(bGeo),
new THREE.LineBasicMaterial({color:0x000000,transparent:true,opacity:.35})));
const win=new THREE.Mesh(new THREE.BoxGeometry(2.4,0.55,2.06),
new THREE.MeshStandardMaterial({color:0x0a0f16,metalness:.3,roughness:.15}));
win.position.set(-0.3,1.25,0); bodyGroup.add(win);
const hlMat=new THREE.MeshStandardMaterial({color:0xffffff,emissive:0xffffff,emissiveIntensity:1});
for(const zz of [-0.6,0.6]){const hl=new THREE.Mesh(new THREE.BoxGeometry(0.15,0.2,0.4),hlMat);hl.position.set(2.42,0.7,zz);bodyGroup.add(hl);}
const bar=new THREE.Mesh(new THREE.BoxGeometry(0.3,0.14,1.7),
new THREE.MeshStandardMaterial({color:0xffaa00,emissive:0xffaa00,emissiveIntensity:.8}));
bar.position.set(0.3,1.78,0); bodyGroup.add(bar);
const wGeo=new THREE.CylinderGeometry(0.8,0.8,0.55,16);
const wMat=new THREE.MeshStandardMaterial({color:0x0a0a0a,roughness:.9});
const hMat=new THREE.MeshStandardMaterial({color:0x777777,metalness:.8,roughness:.35});
const wheels=[];
for(const [x,z] of [[1.05,1.55],[-1.05,1.55],[1.05,-1.55],[-1.05,-1.55]]){
const wg=new THREE.Group(); wg.position.set(x,0.8,z);
const spin=new THREE.Group(); wg.add(spin);
const tire=new THREE.Mesh(wGeo,wMat); tire.rotation.z=Math.PI/2; spin.add(tire);
const hub=new THREE.Mesh(new THREE.CylinderGeometry(0.34,0.34,0.6,10),hMat); hub.rotation.z=Math.PI/2; spin.add(hub);
group.add(wg); wheels.push({group:wg,spin,front:z>0});
}
return {group,bodyPivot,wheels};
}
function spawnTrucks(){
const colors=[0x18e0ff,0xff3b30,0xffd60a,0xaf52de,0x30d158];
const modes=['chase','hunt','ram','roam'];
for(let i=0;i<5;i++){
const parts=makeTruck(colors[i]), sp=spawnPoints[i];
const t={
isPlayer:i===0, group:parts.group, bodyPivot:parts.bodyPivot, wheels:parts.wheels,
pos:new THREE.Vector3(sp.x,0,sp.z), vel:new THREE.Vector3(),
heading:sp.heading, y:0, vy:0, grounded:true, speedF:0, cool:0,
wreck:0, spin:0, spawn:{x:sp.x,z:sp.z,heading:sp.heading},
ai:{aggr:0.5+Math.random()*0.5, speedMul:0.85+Math.random()*0.2,
mode:modes[i%4], modeT:Math.random()*2, wanderA:Math.random()*PI2,
wanderR:12+Math.random()*16, phase:Math.random()*PI2, freq:0.6+Math.random()*0.8}
};
const sh=new THREE.Mesh(new THREE.CircleGeometry(2.6,20),
new THREE.MeshBasicMaterial({color:0x000000,transparent:true,opacity:.35}));
sh.rotation.x=-Math.PI/2; sh.position.set(sp.x,0.03,sp.z); scene.add(sh); t.shadow=sh;
if(i===0){
const ring=new THREE.Mesh(new THREE.TorusGeometry(3.2,0.14,8,32),
new THREE.MeshBasicMaterial({color:0x18e0ff}));
ring.rotation.x=Math.PI/2; ring.position.set(sp.x,0.06,sp.z); scene.add(ring); t.ring=ring;
}
t.group.position.set(sp.x,0,sp.z); t.group.rotation.y=sp.heading;
scene.add(t.group); trucks.push(t);
}
}
/* ---------- particles ---------- */
const pGeo=new THREE.SphereGeometry(0.18,6,6);
for(let i=0;i<150;i++){
const m=new THREE.Mesh(pGeo,new THREE.MeshBasicMaterial({color:0xffffff,transparent:true}));
m.visible=false; scene.add(m);
particles.push({mesh:m,vel:new THREE.Vector3(),life:0,max:1,grav:1,grow:0,base:1});
}
let pIdx=0;
function spawnParticle(x,y,z,vx,vy,vz,life,color,size,grav,grow){
const p=particles[pIdx++%particles.length];
p.mesh.position.set(x,y,z); p.mesh.material.color.set(color);
p.base=size; p.mesh.scale.setScalar(size); p.mesh.visible=true;
p.vel.set(vx,vy,vz); p.life=life; p.max=life; p.grav=grav; p.grow=grow;
}
function impactAt(x,y,z,strength,color){
const n=Math.min(10,3+Math.floor(strength));
for(let i=0;i<n;i++){
const a=Math.random()*PI2, sp=2+Math.random()*strength*0.5;
spawnParticle(x,y,z,Math.cos(a)*sp,2+Math.random()*strength*0.4,Math.sin(a)*sp,
0.4+Math.random()*0.3,color,0.5+Math.random()*0.6,1,0);
}
}
function spawnSmoke(t){
spawnParticle(t.pos.x+(Math.random()-0.5)*2,1+Math.random(),t.pos.z+(Math.random()-0.5)*2,
(Math.random()-0.5),2+Math.random()*2,(Math.random()-0.5),
0.8+Math.random()*0.5,0x555555,1.2,-0.2,1.6);
}
function updateParticles(dt){
for(const p of particles){
if(p.life<=0){ if(p.mesh.visible)p.mesh.visible=false; continue; }
p.life-=dt; if(p.life<=0){p.mesh.visible=false;continue;}
p.vel.y-=GRAV*0.5*p.grav*dt;
p.mesh.position.addScaledVector(p.vel,dt);
if(p.mesh.position.y<0.1){p.mesh.position.y=0.1;p.vel.y*=-0.3;p.vel.x*=0.6;p.vel.z*=0.6;}
const k=p.life/p.max;
p.mesh.material.opacity=k;
p.mesh.scale.setScalar(p.base*(1+(1-k)*p.grow));
}
}
/* ---------- audio ---------- */
let AC=null,masterGain,engineGain,engineOsc,noiseBuf,muted=false;
function initAudio(){
if(AC){ if(AC.state==='suspended')AC.resume(); return; }
try{
AC=new (window.AudioContext||window.webkitAudioContext)();
masterGain=AC.createGain(); masterGain.gain.value=0.5; masterGain.connect(AC.destination);
engineGain=AC.createGain(); engineGain.gain.value=0; engineGain.connect(masterGain);
engineOsc=AC.createOscillator(); engineOsc.type='sawtooth'; engineOsc.frequency.value=60;
engineOsc.connect(engineGain); engineOsc.start();
const len=Math.floor(AC.sampleRate*0.3); noiseBuf=AC.createBuffer(1,len,AC.sampleRate);
const d=noiseBuf.getChannelData(0); for(let i=0;i<len;i++)d[i]=Math.random()*2-1;
}catch(e){ AC=null; }
}
function engineSound(spd){
if(!AC)return;
if(muted){engineGain.gain.value=0;return;}
engineOsc.frequency.value=55+Math.min(spd,28)*6;
engineGain.gain.value=0.015+Math.min(spd,28)*0.0012;
}
function impactSound(str){
if(!AC||muted)return;
try{
const src=AC.createBufferSource(); src.buffer=noiseBuf;
const g=AC.createGain(),f=AC.createBiquadFilter(),t=AC.currentTime;
g.gain.setValueAtTime(Math.min(0.5,0.05+str*0.02),t);
g.gain.exponentialRampToValueAtTime(0.001,t+0.25);
f.type='lowpass'; f.frequency.value=900;
src.connect(f); f.connect(g); g.connect(masterGain);
src.start(t); src.stop(t+0.3);
}catch(e){}
}
function toggleMute(){ muted=!muted; if(masterGain)masterGain.gain.value=muted?0:0.5; }
/* ---------- input ---------- */
const keys={};
addEventListener('keydown',e=>{
const k=e.key.toLowerCase();
if(['arrowup','arrowdown','arrowleft','arrowright',' '].includes(k))e.preventDefault();
keys[k]=true; initAudio();
if(k==='p')togglePhoto();
else if(k==='m')toggleMute();
else if(k==='r'&&state.over)restart();
});
addEventListener('keyup',e=>{keys[e.key.toLowerCase()]=false;});
addEventListener('blur',()=>{for(const k in keys)keys[k]=false;});
addEventListener('pointerdown',()=>initAudio());
/* ---------- controls ---------- */
function playerControl(){
let throttle=0,steer=0;
if(keys['w']||keys['arrowup'])throttle+=1;
if(keys['s']||keys['arrowdown'])throttle-=1;
if(keys['d']||keys['arrowright'])steer+=1;
if(keys['a']||keys['arrowleft'])steer-=1;
return {throttle,steer};
}
function aiControl(t,dt){
const ai=t.ai; ai.modeT-=dt;
if(ai.modeT<=0){
const r=Math.random();
if(r<0.15+0.4*ai.aggr)ai.mode='chase';
else if(r<0.7)ai.mode='hunt';
else if(r<0.87)ai.mode='ram';
else ai.mode='roam';
ai.modeT=1.5+Math.random()*2.5;
}
let tx=0,tz=0;
if(ai.mode==='chase'){ tx=player.pos.x; tz=player.pos.z; }
else if(ai.mode==='ram'){
let best=null,bd=1e9;
for(const o of trucks){if(o===t)continue;const d=(o.pos.x-t.pos.x)**2+(o.pos.z-t.pos.z)**2;if(d<bd){bd=d;best=o;}}
if(best){tx=best.pos.x;tz=best.pos.z;}
} else if(ai.mode==='hunt'){
let best=null,bd=1e9;
for(const c of crates){if(!c.alive)continue;const d=(c.pos.x-t.pos.x)**2+(c.pos.z-t.pos.z)**2;if(d<bd){bd=d;best=c;}}
if(best){tx=best.pos.x;tz=best.pos.z;}
} else { ai.wanderA+=dt*0.6; tx=Math.cos(ai.wanderA)*ai.wanderR; tz=Math.sin(ai.wanderA)*ai.wanderR; }
let dx=tx-t.pos.x, dz=tz-t.pos.z; const dl=Math.hypot(dx,dz)||1; dx/=dl; dz/=dl;
// separation (anti-clump)
let sx=0,sz=0;
for(const o of trucks){ if(o===t)continue;
const ox=t.pos.x-o.pos.x,oz=t.pos.z-o.pos.z,d=Math.hypot(ox,oz);
if(d<9&&d>0.001){const w=(9-d)/9; sx+=ox/d*w; sz+=oz/d*w;}
}
dx+=sx*1.6; dz+=sz*1.6;
// wall avoidance
const dc=Math.hypot(t.pos.x,t.pos.z);
if(dc>FLOOR_R-10){const w=(dc-(FLOOR_R-10))/10; dx+=-t.pos.x/dc*w*2.5; dz+=-t.pos.z/dc*w*2.5;}
// wander noise
const nz=Math.sin(performance.now()*0.001*ai.freq+ai.phase)*0.4, ca=Math.cos(nz), sa=Math.sin(nz);
const ndx=dx*ca-dz*sa, ndz=dx*sa+dz*ca; dx=ndx; dz=ndz;
const desired=Math.atan2(dx,dz);
const diff=wrapAngle(desired-t.heading);
const steer=Math.max(-1,Math.min(1,diff*2));
let throttle=Math.abs(diff)>1.3?0.35:1;
throttle*=ai.speedMul;
return {throttle,steer};
}
/* ---------- physics ---------- */
function applyPhysics(t,ctrl,dt){
let fx=Math.sin(t.heading),fz=Math.cos(t.heading);
let rx=fz,rz=-fx;
let vf=t.vel.x*fx+t.vel.z*fz;
let vl=t.vel.x*rx+t.vel.z*rz;
vf+=ctrl.throttle*ACCEL*dt;
vf*=(1-DRAG*dt); vl*=(1-LATDRAG*dt);
if(vf>MAXF)vf=MAXF; if(vf<-MAXR)vf=-MAXR;
const auth=Math.min(Math.abs(vf)/6,1);
t.heading+=ctrl.steer*TURN*dt*auth*(vf<0?-1:1);
fx=Math.sin(t.heading); fz=Math.cos(t.heading); rx=fz; rz=-fx;
t.vel.x=fx*vf+rx*vl; t.vel.z=fz*vf+rz*vl;
t.pos.x+=t.vel.x*dt; t.pos.z+=t.vel.z*dt;
const dcen=Math.hypot(t.pos.x,t.pos.z), maxR=FLOOR_R-TRUCK_R;
if(dcen>maxR){
const nx=t.pos.x/dcen,nz=t.pos.z/dcen;
t.pos.x=nx*maxR; t.pos.z=nz*maxR;
const vn=t.vel.x*nx+t.vel.z*nz;
if(vn>0){ t.vel.x-=nx*vn*1.4; t.vel.z-=nz*vn*1.4; if(t.isPlayer&&vn>6)shakeIt(vn*0.03); }
}
t.vy-=GRAV*dt; t.y+=t.vy*dt;
if(t.y<=0){ t.y=0; if(t.vy<0)t.vy=0; t.grounded=true; } else t.grounded=false;
t.speedF=vf;
t.group.position.set(t.pos.x,t.y,t.pos.z);
t.group.rotation.y=t.heading;
const tr=-vl*0.02-ctrl.steer*0.05*auth, tp=-ctrl.throttle*0.03, kk=Math.min(1,8*dt);
t.bodyPivot.rotation.z+=(tr-t.bodyPivot.rotation.z)*kk;
t.bodyPivot.rotation.x+=(tp-t.bodyPivot.rotation.x)*kk;
for(const w of t.wheels){
w.spin.rotation.x+=(vf*dt)/0.8;
if(w.front)w.group.rotation.y=ctrl.steer*0.45;
}
t.shadow.position.set(t.pos.x,0.03,t.pos.z);
const sh=Math.max(0.4,1-t.y*0.06);
t.shadow.scale.set(sh,sh,sh); t.shadow.material.opacity=0.35*sh;
if(t.ring)t.ring.position.set(t.pos.x,0.06,t.pos.z);
}
/* ---------- collisions ---------- */
function onTruckImpact(a,b,impact,mx,mz){
if(impact<3)return;
impactAt(mx,1.2,mz,impact,0xffee88);
if(a.isPlayer||b.isPlayer){
const rival=a.isPlayer?b:a;
shakeIt(Math.min(0.6,impact*0.04)); impactSound(impact);
addScore(Math.floor(impact*8));
if(impact>8&&rival.wreck<=0){
rival.wreck=1.0+Math.random()*0.6;
rival.spin=(Math.random()<0.5?-1:1)*(6+Math.random()*4);
}
} else if(impact>10&&Math.random()<0.5){
const v=Math.random()<0.5?a:b;
if(v.wreck<=0){ v.wreck=0.8; v.spin=(Math.random()-0.5)*10; }
}
}
function resolveCollisions(){
for(let i=0;i<trucks.length;i++)for(let j=i+1;j<trucks.length;j++){
const a=trucks[i],b=trucks[j];
let dx=a.pos.x-b.pos.x,dz=a.pos.z-b.pos.z,d=Math.hypot(dx,dz); const minD=TRUCK_R*2;
if(d<minD&&d>0.0001){
const nx=dx/d,nz=dz/d,ov=minD-d;
a.pos.x+=nx*ov*0.5; a.pos.z+=nz*ov*0.5;
b.pos.x-=nx*ov*0.5; b.pos.z-=nz*ov*0.5;
const vn=(a.vel.x-b.vel.x)*nx+(a.vel.z-b.vel.z)*nz;
if(vn<0){
const imp=-(1+0.5)*vn*0.5;
a.vel.x+=nx*imp; a.vel.z+=nz*imp;
b.vel.x-=nx*imp; b.vel.z-=nz*imp;
onTruckImpact(a,b,-vn,(a.pos.x+b.pos.x)/2,(a.pos.z+b.pos.z)/2);
}
}
}
for(const t of trucks){
for(const bar of barriers){
let dx=t.pos.x-bar.x,dz=t.pos.z-bar.z,d=Math.hypot(dx,dz),minD=TRUCK_R+bar.r;
if(d<minD&&d>0.0001){
const nx=dx/d,nz=dz/d,ov=minD-d;
t.pos.x+=nx*ov; t.pos.z+=nz*ov;
const vn=t.vel.x*nx+t.vel.z*nz;
if(vn<0){ t.vel.x-=nx*vn*1.5; t.vel.z-=nz*vn*1.5; t.vel.x*=0.7; t.vel.z*=0.7;
if(t.isPlayer&&-vn>6){shakeIt(-vn*0.03);impactAt(t.pos.x,1,t.pos.z,-vn*0.5,0xffcc66);} }
}
}
for(const c of crates){
if(!c.alive)continue;
let dx=t.pos.x-c.pos.x,dz=t.pos.z-c.pos.z,d=Math.hypot(dx,dz),minD=TRUCK_R+1.1;
if(d<minD&&d>0.0001&&t.grounded){
const spd=Math.hypot(t.vel.x,t.vel.z);
if(spd>3&&c.cool<=0){
const nx=dx/d,nz=dz/d;
c.vel.set(t.vel.x*0.7+nx*7,5+spd*0.2,t.vel.z*0.7+nz*7);
c.ang.set((Math.random()-0.5)*10,(Math.random()-0.5)*10,(Math.random()-0.5)*10);
c.flying=true; c.cool=0.4;
if(t.isPlayer){ addScore(40); impactAt(c.pos.x,1,c.pos.z,6,0xffaa33); shakeIt(0.15); }
}
}
}
for(const r of ramps){
const dx=t.pos.x-r.x,dz=t.pos.z-r.z;
if(dx*dx+dz*dz<r.trig*r.trig&&t.grounded){
const spd=Math.hypot(t.vel.x,t.vel.z), dot=t.vel.x*r.dx+t.vel.z*r.dz;
if(spd>7&&dot>0&&t.cool<=0){
t.vy=Math.min(6+spd*0.55,17); t.y=0.2; t.grounded=false; t.cool=0.5;
if(t.isPlayer)addScore(30);
}
}
}
}
}
function updateCrates(dt){
for(const c of crates){
if(c.cool>0)c.cool-=dt;
if(!c.alive)continue;
if(c.flying){
c.vel.y-=GRAV*dt;
c.pos.addScaledVector(c.vel,dt);
c.rot.x+=c.ang.x*dt; c.rot.y+=c.ang.y*dt; c.rot.z+=c.ang.z*dt;
if(c.pos.y<0.8){
c.pos.y=0.8;
if(c.vel.y<0)c.vel.y*=-0.35;
c.vel.x*=0.7; c.vel.z*=0.7; c.ang.multiplyScalar(0.6);
if(c.vel.lengthSq()<1&&Math.abs(c.vel.y)<1){ c.flying=false; c.vel.set(0,0,0); c.ang.set(0,0,0); }
}
const dc=Math.hypot(c.pos.x,c.pos.z);
if(dc>FLOOR_R-1){ c.pos.x*=(FLOOR_R-1)/dc; c.pos.z*=(FLOOR_R-1)/dc; c.vel.x*=-0.4; c.vel.z*=-0.4; }
}
c.mesh.position.copy(c.pos);
c.mesh.rotation.set(c.rot.x,c.rot.y,c.rot.z);
}
}
/* ---------- per-frame ---------- */
function update(dt){
state.timeLeft-=dt;
if(state.timeLeft<=0){ state.timeLeft=0; gameOver(); }
for(const t of trucks){
if(t.cool>0)t.cool-=dt;
let ctrl;
if(t.wreck>0){
t.wreck-=dt; t.heading+=t.spin*dt; t.spin*=(1-1.2*dt);
if(Math.random()<0.6)spawnSmoke(t);
if(t.wreck<=0)t.spin=0;
ctrl={throttle:0,steer:0};
} else if(t.isPlayer) ctrl=playerControl();
else ctrl=aiControl(t,dt);
applyPhysics(t,ctrl,dt);
}
resolveCollisions();
updateCrates(dt);
updateParticles(dt);
engineSound(Math.abs(player.speedF));
}
function updateFlashes(dt){
for(const s of flashes){
s.userData.timer-=dt;
if(s.userData.timer<=0){
if(s.visible){ s.visible=false; s.userData.timer=0.4+Math.random()*2.5; }
else { s.visible=true; s.userData.timer=0.05+Math.random()*0.05; }
}
}
}
/* ---------- camera ---------- */
const camPos=new THREE.Vector3();
let shake=0;
function shakeIt(v){ shake=Math.min(1.2,shake+v); }
function updateCamera(dt){
const p=player, fx=Math.sin(p.heading), fz=Math.cos(p.heading);
const dx=p.pos.x-fx*14, dz=p.pos.z-fz*14, dy=10+p.y*0.5;
const k=1-Math.exp(-5*dt);
camPos.x+=(dx-camPos.x)*k; camPos.y+=(dy-camPos.y)*k; camPos.z+=(dz-camPos.z)*k;
shake*=(1-4*dt); if(shake<0.001)shake=0;
camera.position.set(camPos.x+(Math.random()-0.5)*shake,camPos.y+(Math.random()-0.5)*shake,camPos.z+(Math.random()-0.5)*shake);
camera.lookAt(p.pos.x+fx*4,1.5+p.y*0.6,p.pos.z+fz*4);
}
/* ---------- HUD / flow ---------- */
const scoreEl=document.getElementById('score'), clockEl=document.getElementById('clock');
const photoCard=document.getElementById('photoCard'), photoScore=document.getElementById('photoScore');
const overCard=document.getElementById('overCard'), finalScore=document.getElementById('finalScore');
function fmtTime(t){ t=Math.ceil(t); const m=Math.floor(t/60),s=t%60; return m+':'+(s<10?'0':'')+s; }
function updateHUD(){ scoreEl.textContent=state.score; clockEl.textContent=fmtTime(state.timeLeft); }
function addScore(n){ state.score+=n; scoreEl.classList.remove('pop'); void scoreEl.offsetWidth; scoreEl.classList.add('pop'); }
function togglePhoto(){
if(state.over)return;
state.paused=!state.paused;
photoCard.style.display=state.paused?'flex':'none';
if(state.paused)photoScore.textContent=state.score;
last=performance.now();
}
function gameOver(){ state.over=true; finalScore.textContent=state.score; overCard.style.display='flex'; }
function resetTrucks(){
for(const t of trucks){
t.pos.set(t.spawn.x,0,t.spawn.z); t.vel.set(0,0,0); t.heading=t.spawn.heading;
t.y=0; t.vy=0; t.grounded=true; t.speedF=0; t.cool=0; t.wreck=0; t.spin=0;
t.group.position.set(t.spawn.x,0,t.spawn.z); t.group.rotation.y=t.spawn.heading;
t.bodyPivot.rotation.set(0,0,0);
t.shadow.position.set(t.spawn.x,0.03,t.spawn.z);
if(t.ring)t.ring.position.set(t.spawn.x,0.06,t.spawn.z);
}
}
function resetCrates(){
for(const c of crates){
c.pos.copy(c.start); c.vel.set(0,0,0); c.ang.set(0,0,0);
c.rot.set(0,c.startRotY,0); c.flying=false; c.alive=true; c.cool=0;
c.mesh.position.copy(c.pos); c.mesh.rotation.set(0,c.startRotY,0);
}
}
function restart(){
state.over=false; state.paused=false;
overCard.style.display='none'; photoCard.style.display='none';
state.score=state.demo?5000:0; state.timeLeft=state.demo?20:90;
resetTrucks(); resetCrates(); updateHUD(); last=performance.now();
}
/* ---------- loop ---------- */
let last=performance.now();
function loop(now){
requestAnimationFrame(loop);
let dt=(now-last)/1000; last=now;
if(dt>0.05)dt=0.05;
if(state.paused)return; // freeze: no physics, no render
if(state.running&&!state.over) update(dt);
else updateParticles(dt);
updateFlashes(dt);
updateCamera(dt);
renderer.render(scene,camera);
}
/* ---------- boot ---------- */
buildArena();
buildObstacles();
spawnTrucks();
player=trucks[0];
camPos.set(player.pos.x-Math.sin(player.heading)*14,10,player.pos.z-Math.cos(player.heading)*14);
updateHUD();
addEventListener('resize',()=>{
camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix();
renderer.setSize(innerWidth,innerHeight);
if(state.paused)renderer.render(scene,camera);
});
requestAnimationFrame(loop);
})();
</script>
</body>
</html>
if(location.hash==='#demo'){ state.demo=true; state.timeLeft=20; state.score=5000; } writes the actual game-state fields. The clock counts down from 20 and the score climbs from 5000 through the same addScore/timer code as normal play — it's the real simulation state, not a painted-on number.P flips state.paused, and the loop returns before both the physics/AI update() and the renderer.render() call — so the last frame stays on the canvas, the clock stops, and nothing moves behind the card until P is pressed again (a resize still triggers one repaint so the frozen frame survives).