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.GRAVELGRIND COLISEUM — a brutalist concrete bowl where five angular Cybertrucks hurl themselves over ramps, boulders, and each other under flickering floodlights while a roaring crowd of thousands watches from every stand.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GRAVELGRIND COLISEUM — Cybertruck Monster Rally</title>
<style>
html, body { margin: 0; padding: 0; overflow: hidden; background: #0a0a0c; }
canvas { display: block; }
#hud {
position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
font-family: "Courier New", monospace; font-weight: bold; color: #e8e8e8;
background: rgba(10,10,14,0.72); border: 2px solid #ff3b1f;
padding: 10px 22px; letter-spacing: 2px; font-size: 15px;
text-shadow: 0 0 6px rgba(255,59,31,0.8); pointer-events: none;
white-space: pre; text-align: center; line-height: 1.5;
}
#hud .arena { color: #ff3b1f; font-size: 18px; letter-spacing: 4px; }
#photo {
position: fixed; inset: 0; display: none; align-items: center; justify-content: center;
background: rgba(4,4,8,0.82); pointer-events: none;
}
#photo .card {
font-family: "Courier New", monospace; text-align: center; color: #fff;
border: 4px solid #ff3b1f; padding: 48px 72px; background: rgba(12,12,18,0.9);
box-shadow: 0 0 60px rgba(255,59,31,0.5);
}
#photo .arena { font-size: 44px; letter-spacing: 10px; color: #ff3b1f; margin-bottom: 24px; text-shadow: 0 0 18px rgba(255,59,31,0.9); }
#photo .score { font-size: 30px; letter-spacing: 6px; }
#photo .hint { margin-top: 28px; font-size: 14px; color: #999; letter-spacing: 3px; }
#start {
position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;
background: rgba(6,6,10,0.94); font-family: "Courier New", monospace; color: #eee;
cursor: pointer; z-index: 10;
}
#start .box { text-align: center; border: 3px solid #ff3b1f; padding: 40px 60px; }
#start h1 { color: #ff3b1f; letter-spacing: 8px; margin: 0 0 18px 0; font-size: 34px; text-shadow: 0 0 14px rgba(255,59,31,.8); }
#start p { letter-spacing: 2px; line-height: 1.8; margin: 6px 0; }
</style>
</head>
<body>
<div id="hud"><span class="arena">GRAVELGRIND COLISEUM</span>
SCORE 0000000 TIME 03:00 [WASD/ARROWS drive · P photo]</div>
<div id="photo"><div class="card">
<div class="arena">GRAVELGRIND COLISEUM</div>
<div class="score" id="photoScore">SCORE 0000000</div>
<div class="hint">PRESS P TO RESUME</div>
</div></div>
<div id="start"><div class="box">
<h1>GRAVELGRIND COLISEUM</h1>
<p>CYBERTRUCK MONSTER RALLY — 5 TRUCKS, ONE SURVIVOR WORTHY</p>
<p>DRIVE: WASD or ARROW KEYS</p>
<p>RAM RIVALS TO SCORE · P FOR PHOTO MODE</p>
<p style="color:#ff3b1f">▶ CLICK TO START</p>
</div></div>
<script type="importmap">
{ "imports": { "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js" } }
</script>
<script type="module">
import * as THREE from 'three';
const ARENA_NAME = 'GRAVELGRIND COLISEUM';
const hud = document.getElementById('hud');
const photoEl = document.getElementById('photo');
const photoScore = document.getElementById('photoScore');
const startEl = document.getElementById('start');
let score = 0;
let timeLeft = 180; // 3:00 clock
let photoMode = false;
let started = false;
let gameOver = false;
// ---- DEMO MODE: real game state, not an overlay ----
if (location.hash === '#demo') {
timeLeft = 20;
score = 5000;
}
// ---------- RENDERER / SCENE ----------
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, 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(0x0b0d12);
scene.fog = new THREE.Fog(0x0b0d12, 90, 220);
const camera = new THREE.PerspectiveCamera(62, innerWidth / innerHeight, 0.1, 400);
let camMode = 0; // 0 chase, 1 orbit
const hemi = new THREE.HemisphereLight(0x8899bb, 0x22140c, 0.55);
scene.add(hemi);
const sun = new THREE.DirectionalLight(0xfff2dd, 1.6);
sun.position.set(60, 90, 30);
sun.castShadow = true;
sun.shadow.mapSize.set(2048, 2048);
sun.shadow.camera.left = -90; sun.shadow.camera.right = 90;
sun.shadow.camera.top = 90; sun.shadow.camera.bottom = -90;
sun.shadow.camera.far = 300;
scene.add(sun);
// ---------- ARENA: bowl + stands + crowd ----------
const ARENA_R = 78; // floor radius
const WALL_H = 14;
const STAND_R0 = ARENA_R + 6;
const STAND_R1 = ARENA_R + 34;
// concrete floor
const floor = new THREE.Mesh(
new THREE.CylinderGeometry(ARENA_R, ARENA_R + 4, 2, 64),
new THREE.MeshStandardMaterial({ color: 0x3a3a40, roughness: 0.95 })
);
floor.position.y = -1;
floor.receiveShadow = true;
scene.add(floor);
// dirt patches
for (let i = 0; i < 26; i++) {
const a = Math.random() * Math.PI * 2, r = Math.random() * (ARENA_R - 14);
const m = new THREE.Mesh(
new THREE.CircleGeometry(3 + Math.random() * 6, 18),
new THREE.MeshStandardMaterial({ color: 0x4a3826, roughness: 1 })
);
m.rotation.x = -Math.PI / 2;
m.position.set(Math.cos(a) * r, 0.02, Math.sin(a) * r);
scene.add(m);
}
// stands: stepped rings all the way round
const standMat = new THREE.MeshStandardMaterial({ color: 0x565660, roughness: 0.9 });
const STEP_H = 1.5, STEP_D = 3.2, STEPS = 9;
for (let s = 0; s < STEPS; s++) {
const rIn = STAND_R0 + s * STEP_D;
const ring = new THREE.Mesh(
new THREE.CylinderGeometry(rIn + STEP_D, rIn + STEP_D, STEP_H, 72, 1, true),
standMat
);
ring.position.y = WALL_H / 2 + s * STEP_H + STEP_H / 2;
scene.add(ring);
// vomitory stripes
if (s % 2 === 0) {
const stripe = new THREE.Mesh(
new THREE.CylinderGeometry(rIn + STEP_D + 0.02, rIn + STEP_D + 0.02, STEP_H * 0.9, 72, 1, true),
new THREE.MeshStandardMaterial({ color: 0x8a2a1a, roughness: 1 })
);
stripe.position.y = ring.position.y;
scene.add(stripe);
}
}
// outer wall
const wall = new THREE.Mesh(
new THREE.CylinderGeometry(STAND_R1 + 2, STAND_R1 + 2, WALL_H * 2.2, 72, 1, true),
new THREE.MeshStandardMaterial({ color: 0x2c2c34, roughness: 1 })
);
wall.position.y = WALL_H * 1.1;
scene.add(wall);
// arena wall (keeps trucks in)
const barrier = new THREE.Mesh(
new THREE.CylinderGeometry(ARENA_R, ARENA_R - 1.2, 3.4, 64, 1, true),
new THREE.MeshStandardMaterial({ color: 0x777788, roughness: 0.6, metalness: 0.3, side: THREE.DoubleSide })
);
barrier.position.y = 1.7;
scene.add(barrier);
// ad boards on the wall
const boardMat = new THREE.MeshStandardMaterial({ color: 0xff3b1f, emissive: 0xff3b1f, emissiveIntensity: 0.55 });
for (let i = 0; i < 24; i++) {
const a = (i / 24) * Math.PI * 2;
const board = new THREE.Mesh(new THREE.PlaneGeometry(8, 1.6), boardMat);
board.position.set(Math.cos(a) * (ARENA_R - 0.6), 2.6, Math.sin(a) * (ARENA_R - 0.6));
board.lookAt(0, 2.6, 0);
scene.add(board);
}
// ---- CROWD: instanced, all the way round ----
const CROWD_ROWS = 9, CROWD_PER_ROW = 220;
const crowdGeo = new THREE.SphereGeometry(0.32, 6, 5);
const crowdMat = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 1 });
const crowd = new THREE.InstancedMesh(crowdGeo, crowdMat, CROWD_ROWS * CROWD_PER_ROW);
{
const dummy = new THREE.Object3D();
const color = new THREE.Color();
let idx = 0;
for (let s = 0; s < CROWD_ROWS; s++) {
const r = STAND_R0 + s * STEP_D + STEP_D * 0.5;
const y = WALL_H / 2 + s * STEP_H + STEP_H * 0.55;
for (let i = 0; i < CROWD_PER_ROW; i++) {
const a = (i / CROWD_PER_ROW) * Math.PI * 2 + s * 0.013;
dummy.position.set(Math.cos(a) * r, y, Math.sin(a) * r);
dummy.rotation.set(0, -a + Math.PI / 2, 0);
const h = 0.75 + Math.random() * 0.5;
dummy.scale.set(h, h, h);
dummy.updateMatrix();
crowd.setMatrixAt(idx, dummy.matrix);
// varied shirt colors
color.setHSL(Math.random(), 0.55 + Math.random() * 0.4, 0.35 + Math.random() * 0.3);
crowd.setColorAt(idx, color);
idx++;
}
}
crowd.instanceMatrix.needsUpdate = true;
if (crowd.instanceColor) crowd.instanceColor.needsUpdate = true;
scene.add(crowd);
}
// crowd "wave" — a few rows flash brighter over time
const flashMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
const flashGeo = new THREE.SphereGeometry(0.34, 6, 5);
const flashes = [];
for (let i = 0; i < 40; i++) {
const f = new THREE.Mesh(flashGeo, flashMat);
f.visible = false;
scene.add(f);
flashes.push(f);
}
// floodlights
const lightMat = new THREE.MeshStandardMaterial({ color: 0xddddff, emissive: 0xbbccff, emissiveIntensity: 2.2 });
for (let i = 0; i < 8; i++) {
const a = (i / 8) * Math.PI * 2;
const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.4, 26), new THREE.MeshStandardMaterial({ color: 0x333340 }));
pole.position.set(Math.cos(a) * (STAND_R1 - 4), WALL_H * 2.2 + 13, Math.sin(a) * (STAND_R1 - 4));
scene.add(pole);
const rig = new THREE.Mesh(new THREE.BoxGeometry(5, 0.5, 2.4), new THREE.MeshStandardMaterial({ color: 0x222228 }));
rig.position.set(Math.cos(a) * (STAND_R1 - 4), WALL_H * 2.2 + 26, Math.sin(a) * (STAND_R1 - 4));
rig.lookAt(0, 0, 0);
scene.add(rig);
for (let k = -2; k <= 2; k++) {
const lamp = new THREE.Mesh(new THREE.CylinderGeometry(0.55, 0.7, 0.5, 12), lightMat);
lamp.position.copy(rig.position);
lamp.position.x += Math.cos(a + Math.PI / 2) * k * 1.1;
lamp.position.z += Math.sin(a + Math.PI / 2) * k * 1.1;
lamp.rotation.x = Math.PI / 2;
scene.add(lamp);
const spot = new THREE.SpotLight(0xffffff, 260, 160, 0.55, 0.6);
spot.position.copy(lamp.position);
spot.target.position.set(0, 0, 0);
spot.castShadow = false;
scene.add(spot); scene.add(spot.target);
}
}
// ---------- OBSTACLES (3+ distinct types) ----------
const obstacles = []; // {mesh, type, radius, height, pos}
// Type 1: concrete ramps (kickers)
const rampMat = new THREE.MeshStandardMaterial({ color: 0x6a6a72, roughness: 0.95 });
for (let i = 0; i < 7; i++) {
const a = Math.random() * Math.PI * 2, r = 14 + Math.random() * (ARENA_R - 24);
const g = new THREE.BoxGeometry(9, 0.7, 5);
const m = new THREE.Mesh(g, rampMat);
m.position.set(Math.cos(a) * r, 0.35, Math.sin(a) * r);
m.rotation.y = Math.random() * Math.PI;
m.rotation.z = -0.42; // tilted kicker
m.castShadow = m.receiveShadow = true;
scene.add(m);
obstacles.push({ mesh: m, type: 'ramp', radius: 5.5, height: 1.6, pos: m.position });
}
// Type 2: boulders
const rockMat = new THREE.MeshStandardMaterial({ color: 0x55524c, roughness: 1, flatShading: true });
for (let i = 0; i < 14; i++) {
const a = Math.random() * Math.PI * 2, r = 10 + Math.random() * (ARENA_R - 20);
const s = 0.8 + Math.random() * 1.6;
const m = new THREE.Mesh(new THREE.DodecahedronGeometry(s), rockMat);
m.position.set(Math.cos(a) * r, s * 0.7, Math.sin(a) * r);
m.rotation.set(Math.random(), Math.random(), Math.random());
m.castShadow = m.receiveShadow = true;
scene.add(m);
obstacles.push({ mesh: m, type: 'rock', radius: s * 1.1, height: s * 1.6, pos: m.position });
}
// Type 3: junk cars (crushable debris)
const junkColors = [0x8a2a1a, 0x2a4a8a, 0x3a7a3a, 0x7a6a2a, 0x6a3a7a];
for (let i = 0; i < 10; i++) {
const a = Math.random() * Math.PI * 2, r = 12 + Math.random() * (ARENA_R - 22);
const g = new THREE.Group();
const body = new THREE.Mesh(new THREE.BoxGeometry(3.4, 1.1, 1.8),
new THREE.MeshStandardMaterial({ color: junkColors[i % junkColors.length], roughness: 0.9 }));
body.position.y = 0.75; body.castShadow = true;
const top = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.7, 1.6),
new THREE.MeshStandardMaterial({ color: 0x222228, roughness: 0.9 }));
top.position.y = 1.5;
g.add(body, top);
g.position.set(Math.cos(a) * r, 0, Math.sin(a) * r);
g.rotation.y = Math.random() * Math.PI;
scene.add(g);
obstacles.push({ mesh: g, type: 'junk', radius: 2.4, height: 2.2, pos: g.position, group: g, crushed: 0 });
}
// ---------- TRUCKS ----------
// Angular Cybertruck-ish wedge body
function makeTruck(colorHex, name) {
const g = new THREE.Group();
const mat = new THREE.MeshStandardMaterial({ color: colorHex, roughness: 0.55, metalness: 0.75, flatShading: true });
const dark = new THREE.MeshStandardMaterial({ color: 0x14161a, roughness: 0.4, metalness: 0.6 });
// main wedge body
const body = new THREE.Mesh(new THREE.BoxGeometry(4.6, 1.15, 2.3), mat);
body.position.y = 1.15;
// cab wedge (Cybertruck triangle profile)
const cabGeo = new THREE.BufferGeometry();
cabGeo.setAttribute('position', new THREE.Float32BufferAttribute([
-1.6, 0.55, -1.15, 1.9, 0.55, -1.15, 1.9, 1.75, -1.15,
-1.6, 0.55, 1.15, 1.9, 0.55, 1.15, 1.9, 1.75, 1.15,
-1.6, 0.55, -1.15, -1.6, 0.55, 1.15, 1.9, 1.75, 1.15,
-1.6, 0.55, -1.15, 1.9, 1.75, 1.15, 1.9, 1.75, -1.15,
-1.6, 0.55, -1.15, 1.9, 0.55, -1.15, 1.9, 1.75, -1.15,
-1.6, 0.55, 1.15, 1.9, 1.75, 1.15, 1.9, 0.55, 1.15
]));
cabGeo.computeVertexNormals();
const cab = new THREE.Mesh(cabGeo, mat);
cab.position.set(0.35, 1.15, 0);
// window band
const win = new THREE.Mesh(new THREE.BoxGeometry(2.6, 0.42, 2.06), dark);
win.position.set(0.55, 1.95, 0);
// light bar
const bar = new THREE.Mesh(new THREE.BoxGeometry(0.12, 0.1, 2.2),
new THREE.MeshStandardMaterial({ color: 0xffffee, emissive: 0xffeebb, emissiveIntensity: 2.5 }));
bar.position.set(1.95, 1.62, 0);
g.add(body, cab, win, bar);
// monster wheels
const wheelGeo = new THREE.CylinderGeometry(0.62, 0.62, 0.5, 18);
const wheelMat = new THREE.MeshStandardMaterial({ color: 0x0c0c0e, roughness: 1 });
const hubMat = new THREE.MeshStandardMaterial({ color: 0x888892, metalness: 0.9, roughness: 0.3 });
const hubGeo = new THREE.CylinderGeometry(0.3, 0.3, 0.52, 12);
const wheelPositions = [[-1.7, 0.62, 1.25], [-1.7, 0.62, -1.25], [1.7, 0.62, 1.25], [1.7, 0.62, -1.25]];
const wheels = [];
wheelPositions.forEach(p => {
const w = new THREE.Mesh(wheelGeo, wheelMat);
w.rotation.z = Math.PI / 2;
w.position.set(...p);
const h = new THREE.Mesh(hubGeo, hubMat);
h.rotation.z = Math.PI / 2;
h.position.set(...p);
g.add(w, h);
wheels.push(w);
});
// headlights
const hlGeo = new THREE.BoxGeometry(0.08, 0.22, 0.5);
const hlMat = new THREE.MeshStandardMaterial({ color: 0xffffee, emissive: 0xffeecc, emissiveIntensity: 3 });
[-1, 1].forEach(s => {
const hl = new THREE.Mesh(hlGeo, hlMat);
hl.position.set(-2.32, 1.2, s * 0.85);
g.add(hl);
});
// taillights
const tlMat = new THREE.MeshStandardMaterial({ color: 0xff2211, emissive: 0xff2211, emissiveIntensity: 2.5 });
[-1, 1].forEach(s => {
const tl = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.2, 0.4), tlMat);
tl.position.set(2.32, 1.1, s * 0.85);
g.add(tl);
});
// name flag
const canvas = document.createElement('canvas');
canvas.width = 256; canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#111'; ctx.fillRect(0, 0, 256, 64);
ctx.fillStyle = '#fff'; ctx.font = 'bold 34px monospace';
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
ctx.fillText(name, 128, 34);
const tex = new THREE.CanvasTexture(canvas);
const flag = new THREE.Mesh(new THREE.PlaneGeometry(1.6, 0.4), new THREE.MeshBasicMaterial({ map: tex }));
flag.position.set(0, 2.75, 0);
g.add(flag);
g.traverse(o => { if (o.isMesh) { o.castShadow = true; } });
scene.add(g);
return { group: g, wheels, name, colorHex };
}
const truckDefs = [
{ name: 'PLAYER', color: 0xd8d8de },
{ name: 'RUSTY', color: 0xb03a1e },
{ name: 'VIPER', color: 0x1e7a3a },
{ name: 'BOLT', color: 0x2a4fc0 },
{ name: 'MUDDY', color: 0x8a6a2a }
];
const trucks = truckDefs.map((d, i) => {
const t = makeTruck(d.color, d.name);
const a = (i / 5) * Math.PI * 2 + 0.6;
t.group.position.set(Math.cos(a) * 30, 0.6, Math.sin(a) * 30);
t.group.rotation.y = -a + Math.PI / 2;
return {
...t,
isPlayer: i === 0,
// physics state
vel: new THREE.Vector3(),
speed: 0,
heading: t.group.rotation.y,
tilt: 0, roll: 0,
airborne: false,
onGroundY: 0.6,
// ai state
aiTarget: new THREE.Vector3(),
aiTimer: 1 + Math.random() * 2,
aiAggro: 0.5 + Math.random() * 0.5, // how hard they chase
aiWander: Math.random() * Math.PI * 2,
aiOffset: (Math.random() - 0.5) * 14, // personal lane offset -> anti-clumping
// damage
damage: 0,
wrecked: false,
scoreFlash: 0,
spin: 0
};
});
// ---------- INPUT ----------
const keys = {};
addEventListener('keydown', e => {
keys[e.code] = true;
if (e.code === 'KeyP') togglePhoto();
if (e.code === 'KeyC') camMode = (camMode + 1) % 2;
});
addEventListener('keyup', e => { keys[e.code] = false; });
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
// ---------- AUDIO (Web Audio, generated) ----------
let actx = null;
function ensureAudio() {
if (!actx) actx = new (window.AudioContext || window.webkitAudioContext)();
if (actx.state === 'suspended') actx.resume();
return actx;
}
function engineHum(truck) {
if (!actx || photoMode) return;
const t = actx.currentTime;
const src = actx.createOscillator();
src.type = 'sawtooth';
src.frequency.value = 40 + Math.abs(truck.speed) * 1.6;
const f = actx.createBiquadFilter();
f.type = 'lowpass'; f.frequency.value = 180 + Math.abs(truck.speed) * 9;
const g = actx.createGain();
g.gain.value = truck.isPlayer ? 0.05 : 0.018;
src.connect(f); f.connect(g); g.connect(actx.destination);
src.start(t); src.stop(t + 0.12);
}
function crashSound(power) {
if (!actx) return;
const t = actx.currentTime;
const len = actx.sampleRate * 0.35;
const buf = actx.createBuffer(1, len, actx.sampleRate);
const d = buf.getChannelData(0);
for (let i = 0; i < len; i++) d[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / len, 2);
const src = actx.createBufferSource(); src.buffer = buf;
const f = actx.createBiquadFilter(); f.type = 'lowpass'; f.frequency.value = 500 + power * 900;
const g = actx.createGain(); g.gain.value = Math.min(0.5, 0.08 + power * 0.09);
src.connect(f); f.connect(g); g.connect(actx.destination);
src.start(t);
}
function scoreChime() {
if (!actx) return;
const t = actx.currentTime;
[880, 1320].forEach((fr, i) => {
const o = actx.createOscillator(); o.type = 'square'; o.frequency.value = fr;
const g = actx.createGain();
g.gain.setValueAtTime(0.12, t + i * 0.09);
g.gain.exponentialRampToValueAtTime(0.001, t + i * 0.09 + 0.35);
o.connect(g); g.connect(actx.destination);
o.start(t + i * 0.09); o.stop(t + i * 0.09 + 0.4);
});
}
// ---------- PHOTO MODE ----------
function togglePhoto() {
photoMode = !photoMode;
photoEl.style.display = photoMode ? 'flex' : 'none';
if (photoMode) photoScore.textContent = 'SCORE ' + String(Math.floor(score)).padStart(7, '0');
}
// ---------- GAME LOGIC ----------
const tmpV = new THREE.Vector3(), tmpV2 = new THREE.Vector3();
const clock = new THREE.Clock();
function spawnParticles(pos, color, count) {
for (let i = 0; i < count; i++) {
const p = new THREE.Mesh(
new THREE.BoxGeometry(0.14, 0.14, 0.14),
new THREE.MeshStandardMaterial({ color, roughness: 1 })
);
p.position.copy(pos);
p.userData.v = new THREE.Vector3((Math.random() - 0.5) * 14, 4 + Math.random() * 10, (Math.random() - 0.5) * 14);
p.userData.life = 0.7 + Math.random() * 0.6;
scene.add(p);
setTimeout(() => scene.remove(p), 1400);
}
}
function updateTruckPhysics(truck, dt, isPlayer) {
const g = truck.group;
// steering
let steer = 0, throttle = 0;
if (isPlayer && !truck.wrecked) {
if (keys['KeyW'] || keys['ArrowUp']) throttle += 1;
if (keys['KeyS'] || keys['ArrowDown']) throttle -= 1;
if (keys['KeyA'] || keys['ArrowLeft']) steer += 1;
if (keys['KeyD'] || keys['ArrowRight']) steer -= 1;
}
const maxSpeed = truck.wrecked ? 2 : (isPlayer ? 34 : 26 + truck.aiAggro * 8);
const accel = throttle * 42 * dt;
truck.speed += accel;
// drag + rolling resistance
truck.speed -= truck.speed * (1.2 + Math.abs(truck.speed) * 0.012) * dt;
if (Math.abs(truck.speed) < 0.05 && throttle === 0) truck.speed = 0;
truck.speed = THREE.MathUtils.clamp(truck.speed, -maxSpeed * 0.5, maxSpeed);
if (isPlayer || !truck.wrecked) {
const steerEffect = THREE.MathUtils.clamp(truck.speed / maxSpeed, -1, 1);
truck.heading -= steer * 2.4 * dt * (0.35 + Math.abs(steerEffect) * 0.85);
}
// velocity from heading
truck.vel.set(Math.sin(truck.heading), 0, Math.cos(truck.heading)).multiplyScalar(truck.speed);
// gravity
if (!truck.airborne) truck.vel.y -= 30 * dt;
const newPos = g.position.clone().addScaledVector(truck.vel, dt);
// arena wall clamp
const dist = Math.hypot(newPos.x, newPos.z);
if (dist > ARENA_R - 3.2) {
const ang = Math.atan2(newPos.z, newPos.x);
newPos.x = Math.cos(ang) * (ARENA_R - 3.2);
newPos.z = Math.sin(ang) * (ARENA_R - 3.2);
const radial = new THREE.Vector3(Math.cos(ang), 0, Math.sin(ang));
const intoWall = truck.vel.dot(radial);
if (intoWall > 0) {
truck.vel.addScaledVector(radial, -intoWall * 1.6);
truck.speed *= 0.6;
if (intoWall > 8) { crashSound(intoWall / 20); spawnParticles(g.position, 0x999999, 8); }
}
}
// obstacle collisions
for (const ob of obstacles) {
const dx = newPos.x - ob.pos.x, dz = newPos.z - ob.pos.z;
const d = Math.hypot(dx, dz);
const minD = ob.radius + 2.1;
if (d < minD) {
const nx = dx / (d || 1), nz = dz / (d || 1);
newPos.x = ob.pos.x + nx * minD;
newPos.z = ob.pos.z + nz * minD;
const vn = truck.vel.x * nx + truck.vel.z * nz;
if (vn < 0) {
truck.vel.x -= nx * vn * 1.5;
truck.vel.z -= nz * vn * 1.5;
truck.speed *= 0.72;
const impact = -vn;
if (ob.type === 'ramp') {
// kicker launch
truck.vel.y = Math.max(truck.vel.y, 9 + impact * 0.35);
truck.airborne = true;
if (impact > 6) spawnParticles(g.position, 0x6a6a72, 6);
} else {
if (impact > 7) {
crashSound(impact / 18);
spawnParticles(g.position, ob.type === 'junk' ? 0xcc4422 : 0x777777, 10);
if (ob.type === 'junk' && ob.crushed < 2) {
ob.crushed++;
ob.group.scale.y = 1 - ob.crushed * 0.35;
ob.group.rotation.z += (Math.random() - 0.5) * 0.5;
if (isPlayer) { score += 150; scoreFlash(truck); }
}
if (isPlayer && impact > 10) { truck.damage += impact * 0.012; }
}
}
}
}
}
// ground / airborne
if (truck.airborne) {
if (newPos.y + truck.vel.y * dt <= truck.onGroundY && truck.vel.y <= 0) {
newPos.y = truck.onGroundY;
truck.vel.y = 0;
truck.airborne = false;
truck.tilt = Math.min(0.5, truck.tilt + Math.abs(truck.vel.x + truck.vel.z) * 0.004);
if (Math.abs(truck.vel.lengthSq()) > 400) spawnParticles(newPos, 0x8a7a5a, 8);
} else {
newPos.y += truck.vel.y * dt;
}
} else {
newPos.y = truck.onGroundY;
}
g.position.copy(newPos);
// visual orientation: heading + tilt/roll spring
const fwdSpeed = truck.vel.length();
truck.tilt *= (1 - 4 * dt);
truck.roll *= (1 - 4 * dt);
if (truck.airborne) {
truck.tilt += dt * 2.2; // nose up in air
}
g.rotation.set(truck.tilt, truck.heading, truck.roll);
g.rotation.y = truck.heading;
// wheels spin
const spin = truck.speed * 0.09;
truck.wheels.forEach(w => w.rotation.x += spin);
// wreck state
if (truck.damage >= 1 && !truck.wrecked) {
truck.wrecked = true;
truck.spin = 6 + Math.random() * 4;
spawnParticles(g.position, 0x333333, 24);
crashSound(1.4);
}
if (truck.wrecked) {
truck.spin *= (1 - 1.2 * dt);
g.rotation.y += truck.spin * dt;
truck.speed *= (1 - 1.5 * dt);
// smoke
if (Math.random() < dt * 6) {
const p = new THREE.Mesh(new THREE.SphereGeometry(0.5, 6, 5),
new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 1 }));
p.position.copy(g.position).add(new THREE.Vector3(0, 2.2, 0));
p.userData.v = new THREE.Vector3((Math.random() - 0.5), 0.8, (Math.random() - 0.5));
p.userData.life = 1.6;
scene.add(p);
setTimeout(() => scene.remove(p), 1700);
}
}
}
function scoreFlash(truck) {
truck.scoreFlash = 1;
scoreChime();
}
// ---------- AI (spread-out hunters) ----------
function updateAI(truck, dt, all) {
if (truck.wrecked) return;
truck.aiTimer -= dt;
const g = truck.group;
if (truck.aiTimer <= 0) {
truck.aiTimer = 0.5 + Math.random() * 1.1;
// pick target: nearest healthy truck, biased by aggro
let best = null, bestD = 1e9;
for (const o of all) {
if (o === truck || o.wrecked) continue;
const d = g.position.distanceTo(o.group.position);
const bias = o.isPlayer ? (2.2 - truck.aiAggro * 1.4) : 1.0; // rivals gang up on player
const dd = d * bias;
if (dd < bestD) { bestD = dd; best = o; }
}
if (best) truck.aiTarget.copy(best.group.position);
// personal orbit offset keeps them from clumping into one blob
truck.aiOffset = (Math.random() - 0.5) * 20;
}
// steer toward target + offset point
const aim = tmpV.copy(truck.aiTarget).add(new THREE.Vector3(
Math.cos(truck.aiWander) * truck.aiOffset * 0.4, 0, Math.sin(truck.aiWander) * truck.aiOffset * 0.4));
truck.aiWander += dt * 0.35;
const to = tmpV2.subVectors(aim, g.position).setY(0);
if (to.lengthSq() > 1) {
const want = Math.atan2(to.x, to.z);
let diff = want - truck.heading;
while (diff > Math.PI) diff -= Math.PI * 2;
while (diff < -Math.PI) diff += Math.PI * 2;
const steer = THREE.MathUtils.clamp(diff * 2.2, -1, 1);
truck.heading += steer * 2.2 * dt;
// throttle: full if roughly facing target, slow in turns
const facing = Math.abs(diff) < 0.7 ? 1 : 0.35;
truck.speed += (26 + truck.aiAggro * 8) * facing * 2.2 * dt;
}
truck.speed -= truck.speed * 1.4 * dt;
}
// ---------- TRUCK vs TRUCK COLLISIONS ----------
function collideTrucks(all, dt) {
for (let i = 0; i < all.length; i++) {
for (let j = i + 1; j < all.length; j++) {
const A = all[i], B = all[j];
const pa = A.group.position, pb = B.group.position;
const d = pa.distanceTo(pb);
const minD = 4.6;
if (d < minD && d > 0.001) {
const nx = (pb.x - pa.x) / d, nz = (pb.z - pa.z) / d;
// separate
const push = (minD - d) * 0.5;
pa.x -= nx * push; pa.z -= nz * push;
pb.x += nx * push; pb.z += nz * push;
// relative velocity along normal
const rvx = B.vel.x - A.vel.x, rvz = B.vel.z - A.vel.z;
const vn = rvx * nx + rvz * nz;
if (vn < 0) {
const imp = -vn * 0.55;
A.vel.x -= nx * imp; A.vel.z -= nz * imp;
B.vel.x += nx * imp; B.vel.z += nz * imp;
A.speed = A.vel.length(); B.speed = B.vel.length();
const power = -vn;
// launch + tilt
A.vel.y += power * 0.06; B.vel.y += power * 0.06;
A.airborne = B.airborne = true;
A.roll += (Math.random() - 0.5) * power * 0.02;
B.roll += (Math.random() - 0.5) * power * 0.02;
if (power > 9) {
crashSound(power / 22);
spawnParticles(pa.clone().lerp(pb, 0.5), 0xff8844, 14);
}
// scoring: player ramming rivals
if (A.isPlayer && power > 7 && !B.wrecked) {
score += Math.round(100 + power * 12);
B.damage += power * 0.02;
scoreFlash(A);
spawnParticles(pb.clone(), 0xffdd66, 10);
} else if (B.isPlayer && power > 7 && !A.wrecked) {
score = Math.max(0, score - Math.round(power * 4));
A.damage += power * 0.02;
spawnParticles(pa.clone(), 0xff4444, 10);
} else if (!A.isPlayer && !B.isPlayer && power > 9) {
// rival-vs-rival: small spectacle bonus to player score
score += 25;
}
}
}
}
}
}
// ---------- HUD ----------
let hudTimer = 0;
function updateHUD() {
const m = Math.floor(timeLeft / 60), s = Math.floor(timeLeft % 60);
hud.innerHTML =
`<span class="arena">${ARENA_NAME}</span>\n` +
`SCORE ${String(Math.floor(score)).padStart(7, '0')} TIME ${m}:${String(s).padStart(2, '0')} [WASD/ARROWS drive · P photo]`;
}
// ---------- MAIN LOOP ----------
let last = 0;
function frame(tms) {
requestAnimationFrame(frame);
const dt = Math.min(clock.getDelta(), 0.05);
if (!started || photoMode) {
// photo mode: freeze EVERYTHING — no physics, no timers; still render so the card sits on a live frame
renderer.render(scene, camera);
return;
}
// clock
timeLeft -= dt;
if (timeLeft <= 0 && !gameOver) {
gameOver = true;
timeLeft = 0;
}
// AI
for (const tr of trucks) if (!tr.isPlayer) updateAI(tr, dt, trucks);
// physics
for (const tr of trucks) updateTruckPhysics(tr, dt, tr.isPlayer);
// truck-vs-truck
collideTrucks(trucks, dt);
// engine hum (throttled)
humAcc += dt;
if (humAcc > 0.12) {
humAcc = 0;
const p = trucks[0];
if (!p.wrecked) engineHum(p);
}
// crowd wave flashes
waveT += dt;
if (waveT > 0.18) {
waveT = 0;
const row = Math.floor(Math.random() * CROWD_ROWS);
const r = STAND_R0 + row * STEP_D + STEP_D * 0.5;
const y = WALL_H / 2 + row * STEP_H + STEP_H * 0.55;
for (const f of flashes) {
if (!f.visible) {
const a = Math.random() * Math.PI * 2;
f.position.set(Math.cos(a) * r, y, Math.sin(a) * r);
f.visible = true;
setTimeout(() => f.visible = false, 260);
break;
}
}
}
// camera: chase player
const p = trucks[0];
if (camMode === 0) {
const back = new THREE.Vector3(Math.sin(p.heading), 0, Math.cos(p.heading)).multiplyScalar(-11);
const target = p.group.position.clone().add(back).add(new THREE.Vector3(0, 6.5, 0));
camera.position.lerp(target, 1 - Math.pow(0.001, dt));
const look = p.group.position.clone().add(new THREE.Vector3(0, 1.5, 0));
camera.lookAt(look);
} else {
orbitT += dt * 0.25;
camera.position.set(Math.cos(orbitT) * 95, 55, Math.sin(orbitT) * 95);
camera.lookAt(0, 5, 0);
}
// HUD throttle
hudTimer += dt;
if (hudTimer > 0.1) { hudTimer = 0; updateHUD(); }
renderer.render(scene, camera);
}
let humAcc = 0, waveT = 0, orbitT = 0;
requestAnimationFrame(frame);
// ---------- START ----------
startEl.addEventListener('click', () => {
ensureAudio();
started = true;
startEl.style.display = 'none';
updateHUD();
});
</script>
</body>
</html>
Five trucks: each AI rival gets a persistent personal aiOffset (a randomized lateral orbit bias around its target) plus a per-truck aiAggro that scales both speed and how strongly it prioritizes the player over other rivals, and retargeting happens on randomized 0.5–1.6s timers rather than every frame — so the four rivals orbit, peel off, and re-engage at different rhythms instead of converging into one blob. Exactly five trucks are spawned from a fixed five-entry definition list; there is no spawn/despawn path that could change the count.
Demo mode: when location.hash === '#demo', the page sets the real game variables timeLeft = 20 and score = 5000 before the loop starts — the same variables the HUD renders, the clock counts down, ramming adds to, and photo mode snapshots. It is not an overlay: the countdown, scoring, collisions, and wreck states all run on those values, and the HUD (which reads the live variables) shows 0:20 / 5000000 on the first frame.
Photo mode: pressing P sets a photoMode flag that the main loop checks before any simulation — the clock, AI steering, physics integration, collision resolution, and particle updates are all skipped, so the physics step and the game timers both stop; the render loop keeps running (a single renderer.render per frame) so the frozen scene stays on screen behind the card, and the card itself reads the frozen score value. The simulation does not advance behind the overlay.
Collisions: the model is impulse-based sphere/circle resolution in the horizontal plane. Truck-vs-truck computes the relative velocity along the contact normal, applies a symmetric impulse (-vn * 0.55) to both velocity vectors, physically separates the two positions by half the penetration each, adds a small vertical launch and random roll scaled by impact power, and converts impact power into score (player ramming a rival: 100 + power*12 plus power*0.02 damage to the victim; player getting rammed: score penalty), particle bursts, and crash sounds. Truck-vs-obstacle resolves penetration against each obstacle's bounding radius, reflects the inward velocity component with restitution, and gives ramps a special kicker branch that converts horizontal impact into vertical launch velocity; junk cars additionally accumulate crush state (scale/rotation deformation) and award flat score for being crushed. Arena walls clamp position radially and reflect the radial velocity component. Wreck state triggers when accumulated damage reaches 1, adding spin decay, smoke particles, and disabling control.