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.Stainless Siege: five angular steel Cybertrucks on jumbo wheels tear around the dusty, crowd-ringed Halcyon Iron Bowl, launching off ramps, bouncing off hills and barriers, and crushing cars while the stands do the wave.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Stainless Siege — Halcyon Iron Bowl</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
<style>
html,body{margin:0;height:100%;overflow:hidden;background:#07080d;font-family:system-ui,-apple-system,"Segoe UI",Arial,sans-serif;color:#eef8ff;user-select:none}
canvas{display:block}
.hud{position:fixed;pointer-events:none;text-shadow:0 2px 6px #000}
#arena{left:18px;top:14px;font-size:18px;font-weight:800;letter-spacing:.14em}
#arena small{display:block;font-size:11px;letter-spacing:.3em;opacity:.75;margin-top:2px}
#clock{left:50%;top:10px;transform:translateX(-50%);font-size:36px;font-weight:800;font-variant-numeric:tabular-nums}
#score{right:18px;top:14px;font-size:26px;font-weight:800;font-variant-numeric:tabular-nums}
#help{left:18px;bottom:14px;font-size:13px;opacity:.9;line-height:1.55}
#hpwrap{right:18px;bottom:18px;width:220px;height:10px;background:rgba(255,255,255,.18);border:1px solid rgba(255,255,255,.35)}
#hp{height:100%;width:100%;background:#3ef0ff}
#pops{left:0;top:0;right:0;bottom:0}
.pop{position:absolute;left:50%;transform:translateX(-50%);font-size:26px;font-weight:800;white-space:nowrap}
.card{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;background:rgba(7,9,16,.87);border:2px solid #3ef0ff;padding:22px 40px;border-radius:6px;pointer-events:none;box-shadow:0 0 40px rgba(62,240,255,.25)}
.card .k{font-size:13px;letter-spacing:.35em;color:#3ef0ff;font-weight:700}
.card .big{font-size:30px;font-weight:800;margin:8px 0;letter-spacing:.08em}
.card .sc{font-size:44px;font-weight:800;font-variant-numeric:tabular-nums;color:#ffcc00}
.card .n{font-size:13px;opacity:.8;margin-top:8px}
</style>
</head>
<body>
<div id="arena" class="hud">HALCYON IRON BOWL<small>STAINLESS SIEGE RALLY</small></div>
<div id="clock" class="hud">1:30</div>
<div id="score" class="hud">SCORE 0</div>
<div id="help" class="hud">DRIVE: ARROWS or WASD · gas ↑/W · brake ↓/S · steer ←→ / A D<br>P = photo mode · R = restart after full time</div>
<div id="hpwrap" class="hud"><div id="hp"></div></div>
<div id="pops" class="hud"></div>
<div id="photo" class="card" style="display:none"><div class="k">PHOTO MODE</div><div class="big" id="ph-arena"></div><div class="sc" id="ph-score"></div><div class="n">Press P to resume</div></div>
<div id="over" class="card" style="display:none"><div class="k">FULL TIME</div><div class="big" id="ov-arena"></div><div class="sc" id="ov-score"></div><div class="n">Press R to run it back</div></div>
<script>
(function(){
'use strict';
if (!window.THREE) {
document.body.insertAdjacentHTML('beforeend','<p style="padding:20px">three.js failed to load from the CDN.</p>');
return;
}
// ---------- constants & layout ----------
const ARENA_NAME = 'HALCYON IRON BOWL';
const RX = 62, RZ = 40; // floor ellipse
const AX = RX - 2.6, AZ = RZ - 2.6; // legal centre range for bodies
const MATCH_TIME = 90;
const DEMO = /#demo$/.test(location.href);
const ACC = 16, REV = 9, BRAKE = 28, MAX_R = 7, GRAV = 24, TURN = 1.6;
const TIERS = 10, TREAD = 0.85, TSTEP = 0.09, PER_TIER = 240;
const HILLS = [
{x:-28,z:14,r:7,a:2.0},
{x:34,z:-20,r:8,a:2.4},
{x:-6,z:-22,r:5,a:1.2}
];
const BARRIERS = [
{x:-8,z:-4,hw:3.2,hd:0.6},
{x:10,z:6,hw:0.6,hd:3.2},
{x:22,z:-14,hw:2.6,hd:0.6},
{x:-22,z:-16,hw:3,hd:0.6},
{x:33,z:8,hw:0.6,hd:3.2},
{x:-40,z:14,hw:0.6,hd:3.2},
{x:14,z:-26,hw:2.2,hd:0.6}
];
const RAMPS = [
{x:-16,z:-6,ang:0.35,L:6,W:4.5,H:1.6},
{x:22,z:12,ang:2.4,L:6,W:4.5,H:1.6}
];
const BARREL_HOME = [[-30,-2],[-30,4],[-4,10],[6,-8],[26,-4],[14,18],[-14,-26],[40,-22]];
const CAR_HOME = [[-2,6],[22,-6],[-34,-4],[12,-18],[38,14],[-18,22]];
const SPAWNS = [
{phi:-1.57, lane:0.62, vmax:21, aggr:0, accent:0x3ef0ff}, // player
{phi:-0.6, lane:0.42, vmax:17, aggr:0.45, accent:0xff3b3b},
{phi:0.7, lane:0.78, vmax:16, aggr:0.25, accent:0xff9a1f},
{phi:2.0, lane:0.52, vmax:18, aggr:0.7, accent:0xa6ff3b},
{phi:3.4, lane:0.7, vmax:15, aggr:0.5, accent:0xb36bff}
];
const hillH = (x,z) => { let h=0; for (const k of HILLS){ const dx=x-k.x, dz=z-k.z; h+=k.a*Math.exp(-(dx*dx+dz*dz)/(k.r*k.r)); } return h; };
const rnd = (a,b) => a + Math.random()*(b-a);
const $ = id => document.getElementById(id);
// ---------- game state (real state; #demo sets it at boot) ----------
let simT = 0;
let clock = DEMO ? 20 : MATCH_TIME;
let score = DEMO ? 5000 : 0;
let over = false, paused = false;
const keys = {};
const pairCool = new Map();
const parts = [];
const popups = [];
// ---------- renderer ----------
const renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setPixelRatio(Math.min(window.devicePixelRatio||1, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0c14);
scene.fog = new THREE.Fog(0x0a0c14, 150, 330);
const camera = new THREE.PerspectiveCamera(58, window.innerWidth/window.innerHeight, 0.1, 700);
scene.add(new THREE.HemisphereLight(0xcfe3ff, 0x3b2c1c, 0.85));
const sun = new THREE.DirectionalLight(0xfff2da, 0.95);
sun.position.set(-50,110,40);
sun.castShadow = true;
sun.shadow.mapSize.set(2048,2048);
Object.assign(sun.shadow.camera, {left:-110,right:110,top:110,bottom:-110,near:10,far:320});
sun.shadow.camera.updateProjectionMatrix();
sun.shadow.bias = -0.0008;
scene.add(sun);
// ---------- arena ----------
const floorGeo = new THREE.PlaneGeometry(260, 200, 130, 100);
floorGeo.rotateX(-Math.PI/2);
{
const p = floorGeo.attributes.position;
for (let i=0;i<p.count;i++) p.setY(i, hillH(p.getX(i), p.getZ(i)));
p.needsUpdate = true;
}
floorGeo.computeVertexNormals();
const floor = new THREE.Mesh(floorGeo, new THREE.MeshStandardMaterial({color:0x6e5a40, roughness:1}));
floor.receiveShadow = true;
scene.add(floor);
const ringGeo = new THREE.RingGeometry(0.955, 0.975, 192);
ringGeo.rotateX(-Math.PI/2);
const ring = new THREE.Mesh(ringGeo, new THREE.MeshBasicMaterial({color:0xffc400}));
ring.scale.set(RX,1,RZ); ring.position.y = 0.04;
scene.add(ring);
// stepped stands: lathe profile scaled into an ellipse
const bowlProfile = [new THREE.Vector2(1.0,0), new THREE.Vector2(1.0,0.6)];
for (let t=0;t<TIERS;t++){
const r = 1 + TSTEP*t, y = 0.6 + TREAD*t;
bowlProfile.push(new THREE.Vector2(r,y), new THREE.Vector2(r+TSTEP,y), new THREE.Vector2(r+TSTEP,y+TREAD));
}
const bowl = new THREE.Mesh(
new THREE.LatheGeometry(bowlProfile, 160),
new THREE.MeshStandardMaterial({color:0x2a2f3d, roughness:0.9, side:THREE.DoubleSide})
);
bowl.scale.set(RX,1,RZ);
bowl.receiveShadow = true;
scene.add(bowl);
// boundary wall ring
const wallMat = new THREE.MeshStandardMaterial({color:0xffc400, roughness:0.6});
const wallGeo = new THREE.BoxGeometry(3.6,1.3,0.5);
for (let i=0;i<96;i++){
const a = i/96*Math.PI*2;
const x = Math.cos(a)*RX, z = Math.sin(a)*RZ;
const tx = -RX*Math.sin(a), tz = RZ*Math.cos(a);
const m = new THREE.Mesh(wallGeo, wallMat);
m.position.set(x,0.65,z);
m.rotation.y = Math.atan2(-tz, tx);
m.castShadow = true;
scene.add(m);
}
// floodlight towers
const poleMat = new THREE.MeshStandardMaterial({color:0x9aa3b5, metalness:0.6, roughness:0.4});
const lampMat = new THREE.MeshBasicMaterial({color:0xfff4d6});
for (const ang of [0.8, 2.36, 3.93, 5.5]){
const x = Math.cos(ang)*RX*1.42, z = Math.sin(ang)*RZ*1.42;
const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.5,0.7,16,8), poleMat);
pole.position.set(x,14,z); scene.add(pole);
const lamp = new THREE.Mesh(new THREE.BoxGeometry(6,1.2,2.5), lampMat);
lamp.position.set(x,22.5,z); lamp.lookAt(0,0,0); scene.add(lamp);
}
// banner on the far stands
const bannerTex = (() => {
const c = document.createElement('canvas'); c.width = 1024; c.height = 160;
const g = c.getContext('2d');
g.fillStyle = '#0a0c14'; g.fillRect(0,0,1024,160);
g.strokeStyle = '#ffc400'; g.lineWidth = 8; g.strokeRect(8,8,1008,144);
g.fillStyle = '#ffc400'; g.font = '900 92px Arial, Helvetica, sans-serif';
g.textAlign = 'center'; g.textBaseline = 'middle';
g.fillText(ARENA_NAME, 512, 84);
return new THREE.CanvasTexture(c);
})();
const banner = new THREE.Mesh(new THREE.PlaneGeometry(50,7.8), new THREE.MeshBasicMaterial({map:bannerTex}));
banner.position.set(0, 13, -RZ*1.72);
scene.add(banner);
// crowd: ~2400 figures in ten tiers, all the way round
const crowd = [];
for (let t=0;t<TIERS;t++){
const r = 1 + TSTEP*t + TSTEP/2, y = 0.6 + TREAD*t;
for (let i=0;i<PER_TIER;i++){
const a = (i + rnd(-0.35,0.35)) / PER_TIER * Math.PI*2;
crowd.push({x: Math.cos(a)*r*RX + rnd(-0.6,0.6), z: Math.sin(a)*r*RZ + rnd(-0.4,0.4), y, a});
}
}
const CN = crowd.length;
const bodyIM = new THREE.InstancedMesh(new THREE.BoxGeometry(0.85,1.1,0.5), new THREE.MeshStandardMaterial({roughness:0.85}), CN);
const headIM = new THREE.InstancedMesh(new THREE.SphereGeometry(0.36,8,6), new THREE.MeshStandardMaterial({roughness:0.8}), CN);
bodyIM.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
headIM.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
const SHIRTS = [0xd33f49,0x2d7dd2,0xf2c94c,0x27ae60,0xffffff,0x1a1a1a,0xff7a1a,0x9b59b6,0x1abc9c,0x3e4a5c,0xe84393];
const SKIN = [0xf1c27d,0xe0ac69,0x8d5524,0xc68642,0xffdbac,0x6b4226];
const tmpCol = new THREE.Color();
for (let i=0;i<CN;i++){
bodyIM.setColorAt(i, tmpCol.setHex(SHIRTS[(Math.random()*SHIRTS.length)|0]));
headIM.setColorAt(i, tmpCol.setHex(SKIN[(Math.random()*SKIN.length)|0]));
}
scene.add(bodyIM);
scene.add(headIM);
const dummy = new THREE.Object3D();
function updateCrowd(){
for (let i=0;i<CN;i++){
const c = crowd[i];
// a Mexican wave travelling round the bowl (driven by sim time, so it freezes in photo mode)
const bump = Math.pow(Math.max(0, Math.sin(c.a*4 - simT*2.6)), 8) * 0.7;
dummy.position.set(c.x, c.y+0.55+bump, c.z); dummy.updateMatrix(); bodyIM.setMatrixAt(i, dummy.matrix);
dummy.position.set(c.x, c.y+1.3+bump, c.z); dummy.updateMatrix(); headIM.setMatrixAt(i, dummy.matrix);
}
bodyIM.instanceMatrix.needsUpdate = true;
headIM.instanceMatrix.needsUpdate = true;
}
// barriers and ramps
const barrierMat = new THREE.MeshStandardMaterial({color:0xb9a46a, roughness:0.9});
for (const B of BARRIERS){
const m = new THREE.Mesh(new THREE.BoxGeometry(B.hw*2,1.5,B.hd*2), barrierMat);
m.position.set(B.x,0.75,B.z);
m.castShadow = m.receiveShadow = true;
scene.add(m);
}
const rampMat = new THREE.MeshStandardMaterial({color:0xffa726, roughness:0.6});
for (const R of RAMPS){
const s = new THREE.Shape();
s.moveTo(-R.L/2,0); s.lineTo(R.L/2,0); s.lineTo(R.L/2,R.H); s.closePath();
const g = new THREE.ExtrudeGeometry(s, {depth:R.W, bevelEnabled:false});
g.translate(0,0,-R.W/2);
const m = new THREE.Mesh(g, rampMat);
m.position.set(R.x,0,R.z); m.rotation.y = R.ang;
m.castShadow = m.receiveShadow = true;
scene.add(m);
}
// ---------- Cybertruck model ----------
const STEEL = new THREE.Color(0xc9ced6);
const SCORCH = new THREE.Color(0x3a2a22);
const tireMat = new THREE.MeshStandardMaterial({color:0x111214, roughness:0.95});
const hubMat = new THREE.MeshStandardMaterial({color:0xb8c0cc, metalness:0.9, roughness:0.3});
const wheelGeo = new THREE.CylinderGeometry(1.1,1.1,0.8,22); wheelGeo.rotateX(Math.PI/2);
const hubGeo = new THREE.CylinderGeometry(0.55,0.55,0.84,14); hubGeo.rotateX(Math.PI/2);
function makeCybertruck(accent){
const root = new THREE.Group(), body = new THREE.Group();
root.add(body);
const shape = new THREE.Shape();
shape.moveTo(-2.8,0.5); shape.lineTo(2.8,0.5); shape.lineTo(2.8,1.0);
shape.lineTo(1.2,1.45); shape.lineTo(-0.6,1.95); shape.lineTo(-2.8,1.95); shape.closePath();
const W = 2.2;
const bodyGeo = new THREE.ExtrudeGeometry(shape, {depth:W, bevelEnabled:true, bevelThickness:0.1, bevelSize:0.1, bevelSegments:2, steps:1, curveSegments:2});
bodyGeo.translate(0,0,-W/2);
const steel = new THREE.MeshStandardMaterial({color:0xc9ced6, metalness:0.85, roughness:0.32});
const shell = new THREE.Mesh(bodyGeo, steel);
shell.castShadow = true;
body.add(shell);
for (const x of [-1.8,1.8]) for (const z of [-1.5,1.5]){
const w = new THREE.Mesh(wheelGeo, tireMat); w.position.set(x,1.1,z); w.castShadow = true; body.add(w);
const h = new THREE.Mesh(hubGeo, hubMat); h.position.set(x,1.1,z); body.add(h);
}
const glow = new THREE.Mesh(new THREE.BoxGeometry(5.2,0.05,1.9), new THREE.MeshBasicMaterial({color:accent}));
glow.position.set(0,0.47,0); body.add(glow);
for (const z of [-1.23,1.23]){
const st = new THREE.Mesh(new THREE.BoxGeometry(4.6,0.07,0.04), new THREE.MeshBasicMaterial({color:accent}));
st.position.set(0,1.05,z); body.add(st);
}
const bar = new THREE.Mesh(new THREE.BoxGeometry(0.08,0.14,2.0), new THREE.MeshBasicMaterial({color:0xfff6e0}));
bar.position.set(2.82,0.85,0); body.add(bar);
scene.add(root);
return {root, body, steel};
}
// ---------- bodies ----------
const trucks = SPAWNS.map((sp,i) => {
const vis = makeCybertruck(sp.accent);
return {
id:i, kind:'truck', isPlayer:i===0, sp, vis,
x:0,y:0,z:0, vx:0,vy:0,vz:0, th:0, r:2.0, invM:1/1.2,
health:100, wrecked:false, wreckT:0, tilt:0,
air:false, airT:0, lastRampH:0,
throttle:0, steer:0,
chase:false, chaseT:rnd(0,3), aggr:sp.aggr||0, lane:sp.lane, vmax:sp.vmax,
hitCD:0, smokeT:0
};
});
const barrelGeo = new THREE.CylinderGeometry(0.9,0.9,1.8,16);
const barrelMat = new THREE.MeshStandardMaterial({color:0xe2561b, roughness:0.55, metalness:0.3});
const barrels = BARREL_HOME.map((p,i) => {
const m = new THREE.Mesh(barrelGeo, barrelMat); m.castShadow = true; scene.add(m);
return {id:100+i, kind:'barrel', hx:p[0], hz:p[1], x:p[0], z:p[1], vx:0, vz:0, y:0.9, r:0.9, invM:1/0.4, dead:false, hitCD:0, vis:m};
});
const carBodyMats = [0xf2f2f2,0xe8c21a,0x3a7bd5,0xd9423a,0x2ecc71].map(c => new THREE.MeshStandardMaterial({color:c, roughness:0.5, metalness:0.2}));
const glassMat = new THREE.MeshStandardMaterial({color:0x162030, roughness:0.2, metalness:0.6});
const carBodyGeo = new THREE.BoxGeometry(4,1,2), carCabGeo = new THREE.BoxGeometry(2.2,0.8,1.8);
const cars = CAR_HOME.map((p,i) => {
const g = new THREE.Group();
const b = new THREE.Mesh(carBodyGeo, carBodyMats[i%5]); b.position.y = 0.8; b.castShadow = true;
const c = new THREE.Mesh(carCabGeo, glassMat); c.position.set(-0.2,1.7,0); c.castShadow = true;
g.add(b, c); scene.add(g);
return {id:200+i, kind:'car', hx:p[0], hz:p[1], x:p[0], z:p[1], vx:0, vz:0, r:1.5, invM:1/0.6, dead:false, crushT:0, hitCD:0, vis:g};
});
// ---------- helpers ----------
function spawnPoint(sp){
const x = Math.cos(sp.phi)*AX*sp.lane, z = Math.sin(sp.phi)*AZ*sp.lane;
const tx = -AX*Math.sin(sp.phi), tz = AZ*Math.cos(sp.phi);
return {x, z, th: Math.atan2(-tz, tx)}; // heading along the lap direction
}
function respawn(t){
const s = spawnPoint(t.sp);
t.x = s.x; t.z = s.z; t.th = s.th;
t.vx = t.vz = 0;
t.y = hillH(t.x,t.z); t.vy = 0; t.air = false; t.airT = 0; t.lastRampH = 0;
t.health = 100; t.wrecked = false; t.wreckT = 0; t.tilt = 0;
t.throttle = 0; t.steer = 0; t.hitCD = 0;
}
function groundAt(x,z){
let rampH = 0;
for (const R of RAMPS){
const dx = x-R.x, dz = z-R.z;
const lx = dx*Math.cos(R.ang) - dz*Math.sin(R.ang);
const lz = dx*Math.sin(R.ang) + dz*Math.cos(R.ang);
if (Math.abs(lx) <= R.L/2 && Math.abs(lz) <= R.W/2){
const rh = R.H*(lx+R.L/2)/R.L;
if (rh > rampH) rampH = rh;
}
}
return {h: Math.max(hillH(x,z), rampH), rampH};
}
const partGeo = new THREE.SphereGeometry(0.5,6,5);
function spawnPart(x,y,z,vx,vy,vz,life,color,size,grow){
if (parts.length > 200) return;
const m = new THREE.MeshBasicMaterial({color, transparent:true, opacity:0.85, depthWrite:false});
const mesh = new THREE.Mesh(partGeo, m);
mesh.position.set(x,y,z); mesh.scale.setScalar(size);
scene.add(mesh);
parts.push({mesh, vx, vy, vz, life, max:life, grow});
}
function spark(x,z,power){
const n = Math.min(10, 4+Math.floor(power));
for (let i=0;i<n;i++) spawnPart(x,0.9,z, rnd(-4,4), rnd(1,4), rnd(-4,4), rnd(0.25,0.5), 0xffc857, 0.22, -0.35);
}
const popsEl = $('pops');
function layoutPops(){ popups.forEach((p,i) => { p.el.style.top = (31 - i*6) + '%'; }); }
function popup(text, color){
const el = document.createElement('div');
el.className = 'pop'; el.textContent = text; el.style.color = color || '#ffffff';
popsEl.appendChild(el);
popups.unshift({el, t:1.6});
while (popups.length > 4){ popups.pop().el.remove(); }
layoutPops();
}
function addScore(n, label, color){
score += n;
if (label) popup(label + ' +' + n, color);
}
// ---------- damage & wrecks ----------
function wreck(t){
t.wrecked = true; t.wreckT = 3.5; t.throttle = 0; t.steer = 0;
for (let i=0;i<14;i++) spawnPart(t.x+rnd(-1,1), t.y+1.5, t.z+rnd(-1,1), rnd(-1.5,1.5), rnd(2,5), rnd(-1.5,1.5), rnd(1.5,2.8), 0x4b505c, rnd(0.8,1.4), rnd(1.2,2));
for (let i=0;i<8;i++) spawnPart(t.x, t.y+1, t.z, rnd(-6,6), rnd(2,6), rnd(-6,6), 0.45, 0xffc857, 0.25, -0.4);
if (t.isPlayer){ score = Math.max(0, score-200); popup('WRECKED -200', '#ff5a5a'); }
}
function hitTruck(t, d){
if (d <= 0 || t.wrecked) return;
t.health -= d;
if (t.health <= 0){ t.health = 0; wreck(t); }
}
function hitBody(b, d, px, pz){
if (b.kind !== 'truck' || d <= 0 || b.hitCD > 0) return;
b.hitCD = 0.3;
hitTruck(b, d);
spark(px, pz, d);
}
function crushCar(c){
c.dead = true; c.crushT = 10; c.vx = c.vz = 0;
c.vis.scale.set(1, 0.22, 1);
spark(c.x, c.z, 6);
}
function dmgOf(imp, other){ return other.kind === 'truck' ? Math.max(0, imp-5)*4 : 0; }
function onContact(a, b, imp, px, pz){
for (const [o, other] of [[a,b],[b,a]]){
if (o.kind === 'car' && !o.dead && imp > 3.2){
crushCar(o);
if (other.isPlayer) addScore(150, 'CRUSHED', '#ffe066');
}
}
const key = a.id*1000 + b.id;
if (pairCool.has(key)) return;
pairCool.set(key, 0.4);
if (imp > 4) spark(px, pz, imp);
if (a.kind === 'truck') hitTruck(a, dmgOf(imp, b));
if (b.kind === 'truck') hitTruck(b, dmgOf(imp, a));
const P = trucks[0];
if (a === P || b === P){
const o = a === P ? b : a;
if (o.kind === 'truck'){ if (imp > 5) addScore(Math.round(imp*18), 'RAM', '#ff8a3d'); }
else if (o.kind === 'barrel' && imp > 2.5) addScore(25, 'BARREL', '#ffb347');
}
}
// ---------- collisions ----------
function wallCollide(b){
const q = (b.x/AX)*(b.x/AX) + (b.z/AZ)*(b.z/AZ);
if (q <= 1) return;
const k = 1/Math.sqrt(q);
b.x *= k; b.z *= k;
let nx = b.x/(AX*AX), nz = b.z/(AZ*AZ);
const nl = Math.hypot(nx,nz); nx /= nl; nz /= nl;
const vn = b.vx*nx + b.vz*nz;
if (vn > 0){
b.vx -= nx*vn*1.5; b.vz -= nz*vn*1.5;
hitBody(b, Math.max(0, vn-6)*3, b.x, b.z);
}
}
function barrierCollide(b){
if (b.air) return;
for (const B of BARRIERS){
const cx = Math.max(B.x-B.hw, Math.min(B.x+B.hw, b.x));
const cz = Math.max(B.z-B.hd, Math.min(B.z+B.hd, b.z));
const dx = b.x-cx, dz = b.z-cz, d2 = dx*dx + dz*dz;
if (d2 >= b.r*b.r) continue;
let nx, nz, pen;
if (d2 > 1e-6){ const d = Math.sqrt(d2); nx = dx/d; nz = dz/d; pen = b.r - d; }
else {
const ox = B.hw - Math.abs(b.x-B.x), oz = B.hd - Math.abs(b.z-B.z);
if (ox < oz){ nx = b.x >= B.x ? 1 : -1; nz = 0; pen = ox + b.r; }
else { nx = 0; nz = b.z >= B.z ? 1 : -1; pen = oz + b.r; }
}
b.x += nx*pen; b.z += nz*pen;
const vn = b.vx*nx + b.vz*nz;
if (vn < 0){
b.vx -= nx*vn*1.45; b.vz -= nz*vn*1.45;
hitBody(b, Math.max(0, -vn-6)*3, b.x, b.z);
}
}
}
function collidePairs(){
const list = [];
for (const t of trucks) list.push(t);
for (const b of barrels) if (!b.dead) list.push(b);
for (const c of cars) if (!c.dead) list.push(c);
for (let i=0;i<list.length;i++){
const a = list[i];
for (let j=i+1;j<list.length;j++){
const b = list[j];
if (a.air || b.air) continue;
const dx = b.x-a.x, dz = b.z-a.z, rr = a.r+b.r, d2 = dx*dx+dz*dz;
if (d2 >= rr*rr) continue;
const d = Math.sqrt(d2);
const nx = d > 1e-4 ? dx/d : 1, nz = d > 1e-4 ? dz/d : 0;
const pen = rr - (d > 1e-4 ? d : 0);
const inv = a.invM + b.invM;
a.x -= nx*pen*a.invM/inv; a.z -= nz*pen*a.invM/inv;
b.x += nx*pen*b.invM/inv; b.z += nz*pen*b.invM/inv;
const rv = (b.vx-a.vx)*nx + (b.vz-a.vz)*nz;
if (rv < 0){
const e = 0.35;
const jImp = -(1+e)*rv/inv;
a.vx -= nx*jImp*a.invM; a.vz -= nz*jImp*a.invM;
b.vx += nx*jImp*b.invM; b.vz += nz*jImp*b.invM;
onContact(a, b, -rv, (a.x+b.x)/2, (a.z+b.z)/2);
}
}
}
}
// ---------- vehicle physics ----------
function updateTruck(t, dt){
if (t.wrecked){ t.throttle = 0; t.steer = 0; }
let th = t.th;
let fx = Math.cos(th), fz = -Math.sin(th), rx = Math.sin(th), rz = Math.cos(th);
let s = t.vx*fx + t.vz*fz, l = t.vx*rx + t.vz*rz;
const hgt = hillH(t.x,t.z);
const vmax = t.vmax * (1 - Math.min(0.35, hgt*0.12));
if (!t.air){
if (t.throttle > 0){ if (s < vmax) s = Math.min(vmax, s + ACC*t.throttle*dt); }
else if (t.throttle < 0){
if (s > 0.5) s = Math.max(0, s - BRAKE*dt);
else s = Math.max(-MAX_R, s - REV*(-t.throttle)*dt);
}
else s *= Math.max(0, 1-0.35*dt);
l *= Math.max(0, 1-6*dt);
th += -t.steer * TURN * Math.min(1, Math.abs(s)/5) * Math.sign(s) * dt;
} else {
l *= Math.max(0, 1-0.3*dt);
}
t.th = th;
fx = Math.cos(th); fz = -Math.sin(th); rx = Math.sin(th); rz = Math.cos(th);
t.vx = fx*s + rx*l; t.vz = fz*s + rz*l;
t.x += t.vx*dt; t.z += t.vz*dt;
const g = groundAt(t.x, t.z);
if (t.air){
t.vy -= GRAV*dt; t.y += t.vy*dt; t.airT += dt;
if (t.vy <= 0 && t.y <= g.h){
t.y = g.h; t.vy = 0; t.air = false;
if (t.airT > 0.7 && t.isPlayer) addScore(300, 'BIG AIR', '#ffe066');
t.airT = 0;
}
} else if (t.y > g.h + 0.3){
t.air = true; t.airT = 0;
t.vy = t.lastRampH > 0 ? Math.min(11, Math.max(0,s)*0.55) : 0; // launch off a ramp lip
} else {
t.y = g.h;
}
t.lastRampH = g.rampH;
}
// ---------- AI ----------
function aiControl(t){
if (t.wrecked){ t.throttle = 0; t.steer = 0; return; }
t.chaseT -= 1/60;
if (t.chaseT <= 0){ t.chaseT = 3 + Math.random()*4; t.chase = Math.random() < t.aggr; }
const P = trucks[0];
let tx, tz;
if (t.chase && !P.wrecked && Math.hypot(P.x-t.x, P.z-t.z) < 42){
tx = P.x + P.vx*0.6; tz = P.z + P.vz*0.6;
} else {
const a = Math.atan2(t.z/AZ, t.x/AX) + 0.55;
tx = Math.cos(a)*AX*t.lane; tz = Math.sin(a)*AZ*t.lane;
}
let rx = 0, rz = 0;
const rep = (ox, oz, R, S) => {
const dx = t.x-ox, dz = t.z-oz, d = Math.hypot(dx,dz);
if (d < R && d > 1e-3){ const w = (R-d)/R*S; rx += dx/d*w; rz += dz/d*w; }
};
for (const o of trucks) if (o !== t) rep(o.x, o.z, 9, 12); // keep rivals apart
for (const b of barrels) if (!b.dead) rep(b.x, b.z, 4.5, 8);
for (const c of cars) if (!c.dead) rep(c.x, c.z, 4.5, 8);
for (const B of BARRIERS){
rep(Math.max(B.x-B.hw, Math.min(B.x+B.hw, t.x)), Math.max(B.z-B.hd, Math.min(B.z+B.hd, t.z)), 4.5, 10);
}
let dx = tx - t.x, dz = tz - t.z;
const n = Math.hypot(dx,dz) || 1;
dx = dx/n*10 + rx; dz = dz/n*10 + rz;
const th = t.th, fx = Math.cos(th), fz = -Math.sin(th), sx = Math.sin(th), sz = Math.cos(th);
const err = Math.atan2(dx*sx + dz*sz, dx*fx + dz*fz);
t.steer = Math.max(-1, Math.min(1, err*2.2));
t.throttle = Math.abs(err) > 1.2 ? 0.25 : 1;
}
// ---------- lifecycle ----------
function clearParts(){
for (const p of parts){ scene.remove(p.mesh); p.mesh.material.dispose(); }
parts.length = 0;
}
function resetWorld(){
for (const t of trucks) respawn(t);
for (const b of barrels){ b.x = b.hx; b.z = b.hz; b.vx = b.vz = 0; b.dead = false; }
for (const c of cars){ c.x = c.hx; c.z = c.hz; c.vx = c.vz = 0; c.dead = false; c.crushT = 0; c.vis.scale.set(1,1,1); }
clearParts();
for (const p of popups) p.el.remove();
popups.length = 0;
pairCool.clear();
}
function syncTrucks(dt){
for (const t of trucks){
const target = t.wrecked ? 1.3 : 0;
t.tilt += (target - t.tilt) * Math.min(1, dt*3);
t.vis.root.position.set(t.x, t.y, t.z);
t.vis.root.rotation.y = t.th;
t.vis.body.rotation.x = t.tilt;
t.vis.body.position.y = 1.4 * (t.tilt/1.3);
t.vis.steel.color.copy(STEEL).lerp(SCORCH, (1 - t.health/100) * 0.7);
}
}
const camPos = new THREE.Vector3(), camLook = new THREE.Vector3();
function followCam(dt, snap){
const P = trucks[0];
const fx = Math.cos(P.th), fz = -Math.sin(P.th);
camPos.set(P.x - fx*12, P.y + 5.5, P.z - fz*12);
if (snap) camera.position.copy(camPos);
else camera.position.lerp(camPos, 1 - Math.exp(-5*dt));
camLook.set(P.x + fx*7, P.y + 1.3, P.z + fz*7);
camera.lookAt(camLook);
}
function step(dt){
simT += dt;
clock -= dt;
if (clock <= 0){ clock = 0; over = true; showOver(); return; }
// controls
const P = trucks[0];
P.throttle = (keys.ArrowUp || keys.KeyW ? 1 : 0) - (keys.ArrowDown || keys.KeyS ? 1 : 0);
P.steer = (keys.ArrowRight || keys.KeyD ? 1 : 0) - (keys.ArrowLeft || keys.KeyA ? 1 : 0);
for (let i=1;i<trucks.length;i++) aiControl(trucks[i]);
// vehicles
for (const t of trucks){ if (t.hitCD > 0) t.hitCD -= dt; updateTruck(t, dt); }
// barrels & crushable cars roll and slow down
for (const b of barrels) if (!b.dead){
b.x += b.vx*dt; b.z += b.vz*dt;
const f = Math.max(0, 1-1.6*dt); b.vx *= f; b.vz *= f;
}
for (const c of cars) if (!c.dead){
c.x += c.vx*dt; c.z += c.vz*dt;
const f = Math.max(0, 1-1.6*dt); c.vx *= f; c.vz *= f;
}
// static boundaries, then body-body contacts
const solids = [...trucks, ...barrels, ...cars];
for (const b of solids){ if (b.dead) continue; wallCollide(b); barrierCollide(b); }
collidePairs();
for (const [k,v] of pairCool){ const nv = v - dt; if (nv <= 0) pairCool.delete(k); else pairCool.set(k, nv); }
// car recovery
for (const c of cars) if (c.dead){
c.crushT -= dt;
if (c.crushT <= 0){ c.dead = false; c.x = c.hx; c.z = c.hz; c.vx = c.vz = 0; c.vis.scale.set(1,1,1); }
}
// wrecks, smoke and respawns
for (const t of trucks){
if (t.wrecked){
t.wreckT -= dt; t.smokeT -= dt;
if (t.smokeT <= 0){ t.smokeT = 0.12; spawnPart(t.x, t.y+1.6, t.z, rnd(-0.6,0.6), rnd(1.5,2.5), rnd(-0.6,0.6), 1.8, 0x555b66, 0.9, 1.8); }
if (t.wreckT <= 0) respawn(t);
} else if (t.health < 35 && Math.random() < dt*2){
spawnPart(t.x, t.y+1.6, t.z, 0, 1.5, 0, 1.2, 0x6a6f7a, 0.6, 1.2);
}
}
// particles and popups
for (let i=parts.length-1;i>=0;i--){
const p = parts[i];
p.life -= dt;
if (p.life <= 0){ scene.remove(p.mesh); p.mesh.material.dispose(); parts.splice(i,1); continue; }
const m = p.mesh;
m.position.x += p.vx*dt; m.position.y += p.vy*dt; m.position.z += p.vz*dt;
m.scale.setScalar(Math.max(0.05, m.scale.x + p.grow*dt));
m.material.opacity = 0.85 * Math.min(1, p.life/p.max);
}
for (let i=popups.length-1;i>=0;i--){
const p = popups[i];
p.t -= dt;
if (p.t <= 0){ p.el.remove(); popups.splice(i,1); layoutPops(); }
else p.el.style.opacity = Math.min(1, p.t/0.5);
}
// visuals
for (const c of cars) c.vis.position.set(c.x, 0, c.z);
for (const b of barrels) b.vis.position.set(b.x, b.y, b.z);
syncTrucks(dt);
updateCrowd();
followCam(dt, false);
}
// ---------- overlays & HUD ----------
const clockEl = $('clock'), scoreEl = $('score'), hpEl = $('hp');
function fmtScore(){ return Math.floor(score).toLocaleString('en-US'); }
function hud(){
scoreEl.textContent = 'SCORE ' + fmtScore();
const c = Math.ceil(clock);
clockEl.textContent = Math.floor(c/60) + ':' + String(c % 60).padStart(2,'0');
const h = trucks[0].health;
hpEl.style.width = Math.max(0,h) + '%';
hpEl.style.background = h > 40 ? '#3ef0ff' : '#ff5a5a';
}
function togglePause(){
if (over) return;
paused = !paused;
const el = $('photo');
if (paused){
$('ph-arena').textContent = ARENA_NAME;
$('ph-score').textContent = 'SCORE ' + fmtScore();
el.style.display = 'block';
} else {
el.style.display = 'none';
}
}
function showOver(){
$('ov-arena').textContent = ARENA_NAME;
$('ov-score').textContent = 'SCORE ' + fmtScore();
$('over').style.display = 'block';
}
function restart(){
over = false; paused = false;
clock = MATCH_TIME; score = 0;
$('over').style.display = 'none';
$('photo').style.display = 'none';
resetWorld();
syncTrucks(0);
followCam(0, true);
}
// ---------- input ----------
addEventListener('keydown', e => {
if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code)) e.preventDefault();
keys[e.code] = true;
if (e.code === 'KeyP' && !e.repeat) togglePause();
if (e.code === 'KeyR' && !e.repeat && over) restart();
});
addEventListener('keyup', e => { keys[e.code] = false; });
addEventListener('blur', () => { for (const k in keys) keys[k] = false; });
addEventListener('resize', () => {
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// ---------- boot ----------
resetWorld();
syncTrucks(0);
followCam(0, true);
updateCrowd();
hud();
let last = performance.now();
function frame(now){
const dt = Math.min(0.05, Math.max(0, (now-last)/1000));
last = now;
if (!paused && !over) step(dt); // photo mode and full time both stop the simulation
renderer.render(scene, camera); // the frame stays drawn while paused
hud();
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
#demo is tested at boot with /#demo$/ and sets clock = 20 and score = 5000, the same variables the countdown, scoring, HUD, and game-over logic read. It is not an overlay. The HUD reads those variables every frame.P sets paused, and the frame loop skips step() while it is set. That stops the physics, AI, match clock, particles, popups, crowd wave, and camera follow. The render loop keeps drawing the frozen frame so the card and the HUD stay responsive. The render itself does not change the simulation.(impact − 5) × 4 damage from another truck, and wall or barrier hits do (speed − 6) × 3, with a 0.3–0.4 s per-pair cooldown. Crushing a car at impact above 3.2 flattens it. Bodies in the air skip contacts, and ramp launches are triggered by the ground height dropping away under a moving truck.