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.THE CRUSH DOME — a neon-lit monster-truck rally where five Cybertrucks tear around a small stadium, smashing tire stacks, barrels, cones and ramps while a tiered crowd roars from every side.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>THE CRUSH DOME</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body{width:100%;height:100%;overflow:hidden;background:#05070f}
body{font-family:ui-monospace,Menlo,Consolas,monospace;color:#e8f0ff}
canvas{display:block}
#hud{position:fixed;inset:0;pointer-events:none;padding:16px 20px}
.panel{background:rgba(6,10,22,.55);border:1px solid rgba(120,200,255,.22);border-radius:12px;padding:14px 18px;backdrop-filter:blur(8px)}
#arena{font-size:20px;letter-spacing:4px;color:#7fe7ff;text-shadow:0 0 14px rgba(80,220,255,.65)}
#score{font-size:36px;color:#ffd166;letter-spacing:2px;text-shadow:0 0 18px rgba(255,209,102,.35)}
#clock{font-size:30px;color:#ff6b6b;letter-spacing:2px;margin-top:6px;text-shadow:0 0 18px rgba(255,107,107,.35)}
#keys{font-size:11px;color:#8fa3c0;letter-spacing:2px;margin-top:10px}
#photo,#end{position:fixed;inset:0;display:none;align-items:center;justify-content:center;background:rgba(3,6,14,.75);backdrop-filter:blur(5px)}
#photo.show,#end.show{display:flex}
.card{text-align:center}
.card h1{font-size:46px;letter-spacing:8px;color:#7fe7ff;text-shadow:0 0 26px rgba(80,220,255,.75)}
.card .s{font-size:32px;color:#ffd166;margin-top:12px;letter-spacing:4px;text-shadow:0 0 20px rgba(255,209,102,.4)}
.card p{margin-top:18px;font-size:13px;color:#8fa3c0;letter-spacing:3px}
</style>
</head>
<body>
<div id="hud"><div class="panel">
<div id="arena">THE CRUSH DOME</div>
<div id="score">0000</div>
<div id="clock">01:00</div>
<div id="keys">WASD / ARROWS · P PHOTO</div>
</div></div>
<div id="photo"><div class="card"><h1 id="photoName">THE CRUSH DOME</h1><div class="s" id="photoScore">0000</div></div></div>
<div id="end"><div class="card"><h1 id="endName">THE CRUSH DOME</h1><div class="s" id="endScore">0000</div><p>PRESS R TO RERUN</p></div></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
(function () {
'use strict';
/* ================= CONSTANTS ================= */
var ARENA = { x: 50, z: 30 };
var ARENA_NAME = 'THE CRUSH DOME';
var IS_DEMO = (window.location.hash === '#demo');
/* ================= RENDERER ================= */
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x05070f);
scene.fog = new THREE.FogExp2(0x05070f, 0.0052);
var camera = new THREE.PerspectiveCamera(58, window.innerWidth / window.innerHeight, 0.1, 900);
camera.position.set(-36, 18, -18);
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
/* ================= LIGHTS ================= */
scene.add(new THREE.HemisphereLight(0x9fc4ff, 0x1a1030, 1.15));
var sun = new THREE.DirectionalLight(0xffffff, 2.1);
sun.position.set(45, 70, 35);
sun.castShadow = true;
sun.shadow.mapSize.set(2048, 2048);
var sc = sun.shadow.camera;
sc.left = -80; sc.right = 80; sc.top = 80; sc.bottom = -80; sc.near = 1; sc.far = 220;
scene.add(sun);
var fill = new THREE.DirectionalLight(0x3a7bff, 1.0);
fill.position.set(-60, 30, -50);
scene.add(fill);
var rim = new THREE.DirectionalLight(0xff3b6b, 0.7);
rim.position.set(0, 45, 70);
scene.add(rim);
/* ================= FLOOR ================= */
function makeFloorTexture() {
var c = document.createElement('canvas');
c.width = c.height = 512;
var ctx = c.getContext('2d');
ctx.fillStyle = '#0a0e1a';
ctx.fillRect(0, 0, 512, 512);
for (var i = 0; i < 5000; i++) {
ctx.fillStyle = 'rgba(255,255,255,' + (Math.random() * 0.035) + ')';
ctx.fillRect(Math.random() * 512, Math.random() * 512, 2, 2);
}
ctx.strokeStyle = 'rgba(90,170,255,0.17)';
ctx.lineWidth = 6;
ctx.strokeRect(6, 6, 500, 500);
ctx.strokeStyle = 'rgba(90,170,255,0.075)';
ctx.lineWidth = 3;
for (var k = 0; k <= 8; k++) {
var p = 6 + k * 62.5;
ctx.beginPath(); ctx.moveTo(p, 6); ctx.lineTo(p, 506); ctx.stroke();
ctx.beginPath(); ctx.moveTo(6, p); ctx.lineTo(506, p); ctx.stroke();
}
var tex = new THREE.CanvasTexture(c);
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.repeat.set(6, 6);
tex.anisotropy = renderer.capabilities.getMaxAnisotropy();
return tex;
}
var floor = new THREE.Mesh(
new THREE.PlaneGeometry(320, 320),
new THREE.MeshStandardMaterial({ map: makeFloorTexture(), roughness: 0.95, metalness: 0.05 })
);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
/* ================= WALLS ================= */
var wallMat = new THREE.MeshStandardMaterial({ color: 0x1c2438, roughness: 0.6, metalness: 0.7, flatShading: true });
var trimMat = new THREE.MeshStandardMaterial({ color: 0x2ea8ff, roughness: 0.3, metalness: 1.0, emissive: 0x0a3a55, flatShading: true });
function addWall(w, h, len, x, z) {
var m = new THREE.Mesh(new THREE.BoxGeometry(w, h, len), wallMat);
m.position.set(x, h / 2, z);
m.castShadow = true; m.receiveShadow = true;
scene.add(m);
var t = new THREE.Mesh(new THREE.BoxGeometry(w, 0.22, len), trimMat);
t.position.set(x, h - 0.12, z);
t.castShadow = true;
scene.add(t);
}
addWall(1.6, 7, ARENA.x * 2 + 1.6, 0, ARENA.z + 0.8);
addWall(1.6, 7, ARENA.x * 2 + 1.6, 0, -ARENA.z - 0.8);
addWall(ARENA.z * 2 + 1.6, 7, 1.6, ARENA.x + 0.8, 0);
addWall(ARENA.z * 2 + 1.6, 7, 1.6, -ARENA.x - 0.8, 0);
/* ================= CROWD ================= */
var crowdBodyGeo = new THREE.BoxGeometry(0.62, 1.05, 0.42);
var crowdHeadGeo = new THREE.SphereGeometry(0.26, 8, 6);
crowdHeadGeo.translate(0, 0.62, 0);
var crowdMat = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.85, metalness: 0.05, flatShading: true });
var CROWD_CAP = 1300;
var crowdBodies = new THREE.InstancedMesh(crowdBodyGeo, crowdMat, CROWD_CAP);
var crowdHeads = new THREE.InstancedMesh(crowdHeadGeo, crowdMat, CROWD_CAP);
var crowdColorAttr = new THREE.InstancedBufferAttribute(new Float32Array(CROWD_CAP * 3), 3);
crowdBodies.instanceColor = crowdColorAttr;
crowdHeads.instanceColor = crowdColorAttr;
var crowdColors = [0xff5a5a, 0xffb03a, 0x4dd2ff, 0x7dff8a, 0xb06bff, 0xffe066, 0x5affff, 0xffffff, 0xff8ad4];
var crowdBase = new Float32Array(CROWD_CAP * 16);
var crowdWork = new Float32Array(CROWD_CAP * 16);
var crowdPhases = new Float32Array(CROWD_CAP);
var crowdCount = 0;
function placeSpectator(x, z, y) {
if (crowdCount >= CROWD_CAP) return;
var col = crowdColors[(Math.random() * crowdColors.length) | 0];
var ca = crowdColorAttr.array;
ca[crowdCount * 3] = col.r;
ca[crowdCount * 3 + 1] = col.g;
ca[crowdCount * 3 + 2] = col.b;
crowdPhases[crowdCount] = Math.random() * Math.PI * 2;
var m = new THREE.Matrix4();
m.makeRotationY(Math.random() * Math.PI * 2);
m.setPosition(x, y, z);
crowdBase.set(m.elements, crowdCount * 16);
crowdCount++;
}
function buildCrowd() {
var rows = 5, rowStep = 1.75, colStep = 1.9;
for (var r = 0; r < rows; r++) {
var y = 1.3 + r * 1.05;
var z = ARENA.z + 1.3 + r * 1.5;
for (var x = -ARENA.x + 2; x <= ARENA.x - 2; x += colStep) placeSpectator(x, z, y);
}
for (var r2 = 0; r2 < rows; r2++) {
var y2 = 1.3 + r2 * 1.05;
var z2 = -ARENA.z - 1.3 - r2 * 1.5;
for (var x2 = -ARENA.x + 2; x2 <= ARENA.x - 2; x2 += colStep) placeSpectator(x2, z2, y2);
}
for (var r3 = 0; r3 < rows; r3++) {
var y3 = 1.3 + r3 * 1.05;
var x3 = ARENA.x + 1.3 + r3 * 1.5;
for (var z3 = -ARENA.z + 2; z3 <= ARENA.z - 2; z3 += colStep) placeSpectator(x3, z3, y3);
}
for (var r4 = 0; r4 < rows; r4++) {
var y4 = 1.3 + r4 * 1.05;
var x4 = -ARENA.x - 1.3 - r4 * 1.5;
for (var z4 = -ARENA.z + 2; z4 <= ARENA.z - 2; z4 += colStep) placeSpectator(x4, z4, y4);
}
crowdBodies.count = crowdCount;
crowdHeads.count = crowdCount;
crowdBodies.instanceMatrix.set(crowdBase);
crowdHeads.instanceMatrix.set(crowdBase);
crowdBodies.instanceMatrix.needsUpdate = true;
crowdHeads.instanceMatrix.needsUpdate = true;
crowdColorAttr.needsUpdate = true;
scene.add(crowdBodies, crowdHeads);
}
function updateCrowd(t) {
crowdWork.set(crowdBase);
for (var i = 0; i < crowdCount; i++) {
crowdWork[i * 16 + 12] += Math.sin(t * 1.7 + crowdPhases[i]) * 0.07;
}
crowdBodies.instanceMatrix.set(crowdWork);
crowdHeads.instanceMatrix.set(crowdWork);
crowdBodies.instanceMatrix.needsUpdate = true;
crowdHeads.instanceMatrix.needsUpdate = true;
}
/* ================= OBSTACLES ================= */
var obstacles = [];
var ramps = [];
function addBoxObstacle(x, z, w, h, d, color) {
var m = new THREE.Mesh(new THREE.BoxGeometry(w, h, d),
new THREE.MeshStandardMaterial({ color: color, roughness: 0.72, metalness: 0.32, flatShading: true }));
m.position.set(x, h / 2, z);
m.castShadow = true; m.receiveShadow = true;
scene.add(m);
obstacles.push({ type: 'box', x: x, z: z, hx: w / 2, hz: d / 2 });
}
function addCircleObstacle(x, z, r, h, color, cone) {
var geo = cone ? new THREE.ConeGeometry(r, h, 8) : new THREE.CylinderGeometry(r, r, h, 10);
var m = new THREE.Mesh(geo,
new THREE.MeshStandardMaterial({ color: color, roughness: 0.72, metalness: 0.32, flatShading: true }));
m.position.set(x, h / 2, z);
m.castShadow = true; m.receiveShadow = true;
scene.add(m);
obstacles.push({ type: 'circle', x: x, z: z, r: r });
}
var SPAWNS = [[-36, 4], [36, -10], [-30, -22], [30, 20], [0, 26]];
function scatter(count, maker, minBetween) {
for (var i = 0; i < count; i++) {
for (var attempt = 0; attempt < 60; attempt++) {
var x = (Math.random() * 2 - 1) * (ARENA.x - 9);
var z = (Math.random() * 2 - 1) * (ARENA.z - 9);
var ok = true;
for (var s = 0; s < SPAWNS.length; s++) {
if (Math.hypot(x - SPAWNS[s][0], z - SPAWNS[s][1]) < 13) { ok = false; break; }
}
if (!ok) continue;
for (var o = 0; o < obstacles.length; o++) {
if (Math.hypot(x - obstacles[o].x, z - obstacles[o].z) < minBetween) { ok = false; break; }
}
if (!ok) continue;
maker(x, z);
break;
}
}
}
scatter(11, function (x, z) {
addBoxObstacle(x, z, 2.4, 1.5, 1.5, 0xff3b3b);
}, 7);
scatter(10, function (x, z) {
addCircleObstacle(x, z, 0.72, 1.5, 0x3b7bff, false);
}, 7);
scatter(16, function (x, z) {
addCircleObstacle(x, z, 0.5, 1.3, 0xffd23f, true);
}, 5.5);
/* ================= RAMPS (height field) ================= */
function addRamp(ax, az, bx, bz, h, w) {
ramps.push({ ax: ax, az: az, bx: bx, bz: bz, h: h, w: w });
var len = Math.hypot(bx - ax, bz - az);
var geo = new THREE.PlaneGeometry(len, w * 2, 14, 1);
var pos = geo.attributes.position;
var v = new THREE.Vector3();
for (var i = 0; i < pos.count; i++) {
v.fromBufferAttribute(pos, i);
var t = (v.x / len) * 0.5 + 0.5;
v.y = Math.sin(Math.PI * t) * h;
pos.setXYZ(i, v.x, v.y, v.z);
}
pos.needsUpdate = true;
geo.rotateX(Math.PI / 2);
var phi = Math.atan2(bz - az, bx - ax);
geo.rotateY(-phi);
geo.computeVertexNormals();
var m = new THREE.Mesh(geo,
new THREE.MeshStandardMaterial({ color: 0x2a3550, roughness: 0.92, metalness: 0.12, flatShading: true }));
m.position.set((ax + bx) / 2, 0, (az + bz) / 2);
m.castShadow = true; m.receiveShadow = true;
scene.add(m);
}
addRamp(-40, -24, -8, -24, 2.6, 9);
addRamp(10, 24, 42, 24, 2.6, 9);
addRamp(-44, 8, -14, 8, 2.2, 8);
addRamp(16, -18, 44, -18, 2.2, 8);
function terrainHeight(x, z) {
var h = 0;
for (var i = 0; i < ramps.length; i++) {
var r = ramps[i];
var abx = r.bx - r.ax, abz = r.bz - r.az;
var len2 = abx * abx + abz * abz;
var t = ((x - r.ax) * abx + (z - r.az) * abz) / len2;
t = Math.max(0, Math.min(1, t));
var px = r.ax + abx * t, pz = r.az + abz * t;
var d = Math.hypot(x - px, z - pz);
if (d < r.w) {
var edge = r.w * 0.35;
var falloff = d > r.w - edge ? Math.max(0, (r.w - d) / edge) : 1;
var hh = r.h * Math.sin(Math.PI * t) * falloff;
if (hh > h) h = hh;
}
}
return h;
}
/* ================= TRUCK FACTORY ================= */
function makeTruck(color) {
var g = new THREE.Group();
var bodyMat = new THREE.MeshStandardMaterial({ color: color, roughness: 0.32, metalness: 0.75, flatShading: true });
var darkMat = new THREE.MeshStandardMaterial({ color: 0x1b2233, roughness: 0.55, metalness: 0.5, flatShading: true });
var glassMat = new THREE.MeshStandardMaterial({ color: 0x0a0e18, roughness: 0.08, metalness: 0.95, flatShading: true });
var wheelMat = new THREE.MeshStandardMaterial({ color: 0x14181f, roughness: 0.95, metalness: 0.1, flatShading: true });
var mats = [bodyMat, darkMat, glassMat, wheelMat];
var wheels = [];
function add(geo, mat, x, y, z) {
var m = new THREE.Mesh(geo, mat);
m.position.set(x, y, z);
m.castShadow = true;
m.receiveShadow = true;
g.add(m);
return m;
}
add(new THREE.BoxGeometry(4.6, 1.35, 2.05), bodyMat, 0, 1.5, 0);
add(new THREE.BoxGeometry(1.95, 1.55, 1.95), bodyMat, 1.55, 1.55, 0);
add(new THREE.BoxGeometry(2.3, 1.05, 1.75), bodyMat, -1.7, 1.15, 0);
add(new THREE.BoxGeometry(1.45, 0.8, 1.45), glassMat, 1.62, 1.85, 0);
add(new THREE.BoxGeometry(1.7, 0.16, 0.5), darkMat, 2.5, 1.95, 0);
add(new THREE.BoxGeometry(0.35, 0.75, 2.15), darkMat, 2.32, 1.2, 0);
add(new THREE.BoxGeometry(0.35, 0.75, 2.15), darkMat, -2.32, 1.2, 0);
var wheelGeo = new THREE.CylinderGeometry(0.62, 0.62, 0.42, 12);
wheelGeo.rotateZ(Math.PI / 2);
var spots = [[1.95, 1.05], [1.95, -1.05], [-1.95, 1.05], [-1.95, -1.05]];
for (var i = 0; i < spots.length; i++) {
var w = new THREE.Mesh(wheelGeo, wheelMat);
w.position.set(spots[i][0], 0.62, spots[i][1]);
w.castShadow = true;
g.add(w);
wheels.push(w);
}
return { group: g, wheels: wheels, mats: mats };
}
/* ================= GAME STATE ================= */
var STATE = {
score: IS_DEMO ? 5000 : 0,
time: IS_DEMO ? 20 : 60,
phase: 'play'
};
var trucks = [];
var player = null;
var shake = 0;
var simTime = 0;
function Truck(def, built) {
this.x = def.x; this.z = def.z;
this.vx = 0; this.vz = 0;
this.angle = def.angle;
this.r = 1.65;
this.mass = 1;
this.isPlayer = !!def.player;
this.maxSpeed = def.maxSpeed;
this.accel = def.accel;
this.turnRate = def.turnRate;
this.brake = 18;
this.wrecked = false;
this.wreckT = 0;
this.hitFlash = 0;
this.group = built.group;
this.wheels = built.wheels;
this.mats = built.mats;
this.ai = { target: { x: 0, z: 0 }, timer: 0 };
scene.add(this.group);
}
function createTrucks() {
var defs = [
{ x: -36, z: 4, angle: 0, color: 0x2ea8ff, player: true, maxSpeed: 30, accel: 16, turnRate: 3.0 },
{ x: 36, z: -10, angle: Math.PI, color: 0xff4d4d, maxSpeed: 26, accel: 14, turnRate: 2.6 },
{ x: -30, z: -22, angle: Math.PI / 2, color: 0xffd23f, maxSpeed: 24, accel: 13, turnRate: 2.8 },
{ x: 30, z: 20, angle: -Math.PI / 2, color: 0x7dff6b, maxSpeed: 28, accel: 15, turnRate: 2.4 },
{ x: 0, z: 26, angle: Math.PI, color: 0xb06bff, maxSpeed: 25, accel: 13, turnRate: 2.7 }
];
for (var i = 0; i < defs.length; i++) {
var t = new Truck(defs[i], makeTruck(defs[i].color));
trucks.push(t);
if (defs[i].player) player = t;
}
}
/* ================= INPUT ================= */
var keys = { up: false, down: false, left: false, right: false };
window.addEventListener('keydown', function (e) {
var k = e.key.toLowerCase();
if (k === 'arrowup' || k === 'arrowdown' || k === 'arrowleft' || k === 'arrowright' || k === ' ') e.preventDefault();
if (k === 'arrowup' || k === 'w') keys.up = true;
if (k === 'arrowdown' || k === 's') keys.down = true;
if (k === 'arrowleft' || k === 'a') keys.left = true;
if (k === 'arrowright' || k === 'd') keys.right = true;
if (k === 'p') togglePhoto();
if (k === 'r' && STATE.phase === 'end') resetGame();
});
window.addEventListener('keyup', function (e) {
var k = e.key.toLowerCase();
if (k === 'arrowup' || k === 'w') keys.up = false;
if (k === 'arrowdown' || k === 's') keys.down = false;
if (k === 'arrowleft' || k === 'a') keys.left = false;
if (k === 'arrowright' || k === 'd') keys.right = false;
});
window.addEventListener('blur', function () {
keys.up = keys.down = keys.left = keys.right = false;
});
/* ================= HUD ================= */
var scoreEl = document.getElementById('score');
var clockEl = document.getElementById('clock');
var photoEl = document.getElementById('photo');
var photoNameEl = document.getElementById('photoName');
var photoScoreEl = document.getElementById('photoScore');
var endEl = document.getElementById('end');
var endScoreEl = document.getElementById('endScore');
function fmtTime(t) {
var s = Math.max(0, Math.ceil(t));
var m = Math.floor(s / 60);
var sec = s % 60;
return m > 0 ? m + ':' + String(sec).padStart(2, '0') : String(s).padStart(2, '0');
}
function updateHUD() {
scoreEl.textContent = String(Math.floor(STATE.score)).padStart(4, '0');
clockEl.textContent = fmtTime(STATE.time);
}
function togglePhoto() {
if (STATE.phase === 'photo') {
STATE.phase = 'play';
photoEl.classList.remove('show');
startLoop();
} else if (STATE.phase === 'play') {
STATE.phase = 'photo';
photoNameEl.textContent = ARENA_NAME;
photoScoreEl.textContent = String(Math.floor(STATE.score)).padStart(4, '0');
photoEl.classList.add('show');
stopLoop();
}
}
function endGame() {
STATE.phase = 'end';
endScoreEl.textContent = String(Math.floor(STATE.score)).padStart(4, '0');
endEl.classList.add('show');
stopLoop();
}
function resetGame() {
for (var i = 0; i < trucks.length; i++) scene.remove(trucks[i].group);
trucks.length = 0;
STATE.score = IS_DEMO ? 5000 : 0;
STATE.time = IS_DEMO ? 20 : 60;
STATE.phase = 'play';
shake = 0;
endEl.classList.remove('show');
photoEl.classList.remove('show');
createTrucks();
updateHUD();
startLoop();
}
/* ================= PARTICLES ================= */
var smokeTex = (function () {
var c = document.createElement('canvas');
c.width = c.height = 64;
var ctx = c.getContext('2d');
var g = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
g.addColorStop(0, 'rgba(255,255,255,1)');
g.addColorStop(0.45, 'rgba(255,255,255,0.45)');
g.addColorStop(1, 'rgba(255,255,255,0)');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 64, 64);
var t = new THREE.CanvasTexture(c);
t.needsUpdate = true;
return t;
})();
var MAX_PARTICLES = 420;
var particles = [];
var pGeo = new THREE.BufferGeometry();
var pPos = new Float32Array(MAX_PARTICLES * 3);
var pCol = new Float32Array(MAX_PARTICLES * 4);
var pSz = new Float32Array(MAX_PARTICLES);
pGeo.setAttribute('position', new THREE.BufferAttribute(pPos, 3));
pGeo.setAttribute('color', new THREE.BufferAttribute(pCol, 4));
pGeo.setAttribute('size', new THREE.BufferAttribute(pSz, 1));
var pMat = new THREE.PointsMaterial({
size: 1.0, map: smokeTex, transparent: true, depthWrite: false,
vertexColors: true, blending: THREE.NormalBlending
});
var points = new THREE.Points(pGeo, pMat);
scene.add(points);
function spawnParticles(x, z, count, spread, up) {
for (var i = 0; i < count; i++) {
if (particles.length >= MAX_PARTICLES) break;
var a = Math.random() * Math.PI * 2;
var s = spread * (0.4 + Math.random() * 0.8);
particles.push({
x: x + Math.cos(a) * s,
y: 0.8 + Math.random() * 1.6,
z: z + Math.sin(a) * s,
vx: (Math.random() - 0.5) * 5,
vy: up * (1.2 + Math.random() * 2.6),
vz: (Math.random() - 0.5) * 5,
life: 0.45 + Math.random() * 0.5,
maxLife: 0.95,
size: 0.35 + Math.random() * 0.5
});
}
}
function updateParticles(dt) {
var w = 0;
for (var k = 0; k < particles.length; k++) {
var p = particles[k];
p.life -= dt;
if (p.life <= 0) continue;
p.vy -= 11 * dt;
p.x += p.vx * dt;
p.y += p.vy * dt;
p.z += p.vz * dt;
var d = Math.exp(-1.6 * dt);
p.vx *= d; p.vz *= d;
var f = Math.max(0, p.life / p.maxLife);
var o = w * 3;
pPos[o] = p.x; pPos[o + 1] = Math.max(0, p.y); pPos[o + 2] = p.z;
var co = w * 4;
pCol[co] = 0.72; pCol[co + 1] = 0.76; pCol[co + 2] = 0.86; pCol[co + 3] = f;
pSz[w] = p.size * (0.35 + f * 0.65);
w++;
}
particles.length = w;
for (var j = w; j < MAX_PARTICLES; j++) {
pPos[j * 3] = -9999; pPos[j * 3 + 1] = -9999; pPos[j * 3 + 2] = -9999;
pCol[j * 4 + 3] = 0;
}
pGeo.attributes.position.needsUpdate = true;
pGeo.attributes.color.needsUpdate = true;
pGeo.attributes.size.needsUpdate = true;
}
/* ================= PHYSICS ================= */
function updatePlayer(dt) {
var t = player;
if (t.wrecked) return;
var accel = (keys.up ? 1 : 0) - (keys.down ? 1 : 0);
var steer = (keys.right ? 1 : 0) - (keys.left ? 1 : 0);
if (accel > 0) {
var f = Math.sin(t.angle), c = Math.cos(t.angle);
t.vx += f * t.accel * dt;
t.vz += c * t.accel * dt;
} else if (accel < 0) {
var b = Math.exp(-t.brake * dt);
t.vx *= b; t.vz *= b;
}
var drag = Math.exp(-0.75 * dt);
t.vx *= drag; t.vz *= drag;
var sp = Math.hypot(t.vx, t.vz);
if (sp > t.maxSpeed) { t.vx = t.vx / sp * t.maxSpeed; t.vz = t.vz / sp * t.maxSpeed; }
if (steer !== 0 && sp > 0.3) {
var a = steer * t.turnRate * dt * Math.min(1, sp / 6);
var ca = Math.cos(a), sa = Math.sin(a);
var nvx = t.vx * ca + t.vz * sa;
var nvz = -t.vx * sa + t.vz * ca;
t.vx = nvx; t.vz = nvz;
}
sp = Math.hypot(t.vx, t.vz);
if (sp > 0.2) t.angle = Math.atan2(t.vx, t.vz);
}
function aiSteer(t, dt) {
var ai = t.ai;
if (ai.timer <= 0) {
ai.target.x = (Math.random() * 2 - 1) * (ARENA.x - 10);
ai.target.z = (Math.random() * 2 - 1) * (ARENA.z - 10);
ai.timer = 2 + Math.random() * 4;
}
ai.timer -= dt;
var dx = ai.target.x - t.x;
var dz = ai.target.z - t.z;
for (var i = 0; i < trucks.length; i++) {
var o = trucks[i];
if (o === t) continue;
var ox = o.x - t.x, oz = o.z - t.z;
var d = Math.hypot(ox, oz);
if (d < 11 && d > 0.01) {
var push = (11 - d) / 11;
dx += (ox / d) * push * 5;
dz += (oz / d) * push * 5;
}
}
var fx = Math.sin(t.angle), fz = Math.cos(t.angle);
for (var j = 0; j < obstacles.length; j++) {
var ob = obstacles[j];
var ox = ob.x - t.x, oz = ob.z - t.z;
var d2 = Math.hypot(ox, oz);
if (d2 < 9 && d2 > 0.01) {
var dot = ox * fx + oz * fz;
if (dot > 0) {
var away = Math.atan2(ox, oz);
var diff = away - t.angle;
while (diff > Math.PI) diff -= Math.PI * 2;
while (diff < -Math.PI) diff += Math.PI * 2;
if (Math.abs(diff) < Math.PI / 2) {
t.angle += Math.sign(diff) * Math.min(Math.abs(diff), 1.5 * dt);
}
}
}
}
var desired = Math.atan2(dx, dz);
var diff2 = desired - t.angle;
while (diff2 > Math.PI) diff2 -= Math.PI * 2;
while (diff2 < -Math.PI) diff2 += Math.PI * 2;
t.angle += Math.sign(diff2) * Math.min(Math.abs(diff2), t.turnRate * 0.9 * dt);
var dist = Math.hypot(dx, dz);
var targetSpeed = dist > 16 ? t.maxSpeed * 0.9 : t.maxSpeed * 0.5;
var along = t.vx * Math.sin(t.angle) + t.vz * Math.cos(t.angle);
if (along < targetSpeed) {
t.vx += Math.sin(t.angle) * t.accel * dt * 0.9;
t.vz += Math.cos(t.angle) * t.accel * dt * 0.9;
} else {
var bb = Math.exp(-1.6 * dt);
t.vx *= bb; t.vz *= bb;
}
var px = Math.cos(t.angle), pz = -Math.sin(t.angle);
var lat = t.vx * px + t.vz * pz;
var damp = Math.min(1, 5 * dt);
t.vx -= px * lat * damp;
t.vz -= pz * lat * damp;
}
function integrate(t, dt) {
if (t.wrecked) {
t.wreckT += dt;
t.angle += (Math.random() - 0.5) * 8 * dt;
var d = Math.exp(-2.2 * dt);
t.vx *= d; t.vz *= d;
if (t.wreckT > 5) {
t.wrecked = false;
t.wreckT = 0;
t.vx = 0; t.vz = 0;
t.angle = Math.random() * Math.PI * 2;
}
}
t.x += t.vx * dt;
t.z += t.vz * dt;
}
function resolveTruckTruck(a, b) {
var dx = b.x - a.x, dz = b.z - a.z;
var d = Math.hypot(dx, dz);
var minD = a.r + b.r;
if (d >= minD || d < 0.0001) return;
var nx = dx / d, nz = dz / d;
var pen = minD - d;
a.x -= nx * pen * 0.5; a.z -= nz * pen * 0.5;
b.x += nx * pen * 0.5; b.z += nz * pen * 0.5;
var rvx = b.vx - a.vx, rvz = b.vz - a.vz;
var velAlong = rvx * nx + rvz * nz;
if (velAlong >= 0) return;
var impact = -velAlong;
var aSpd = Math.hypot(a.vx, a.vz);
var bSpd = Math.hypot(b.vx, b.vz);
var aggressor = aSpd >= bSpd ? a : b;
var victim = aggressor === a ? b : a;
var e = 0.5;
var j = -(1 + e) * velAlong / (1 / a.mass + 1 / b.mass);
a.vx -= j * nx / a.mass; a.vz -= j * nz / a.mass;
b.vx += j * nx / b.mass; b.vz += j * nz / b.mass;
var f = Math.min(1, impact / 12);
a.hitFlash = Math.max(a.hitFlash, f);
b.hitFlash = Math.max(b.hitFlash, f);
if (impact > 2.5) {
if (aggressor.isPlayer) {
STATE.score = Math.min(9999, STATE.score + 100);
} else if (victim.isPlayer) {
STATE.score = Math.max(0, STATE.score - 25);
}
spawnParticles((a.x + b.x) / 2, (a.z + b.z) / 2, 10, 2.2, 1);
shake = Math.min(1, shake + impact / 30);
}
if (impact > 9 && !victim.wrecked) {
victim.wrecked = true;
victim.wreckT = 0;
victim.vx *= 0.3; victim.vz *= 0.3;
if (victim.isPlayer) {
STATE.score = Math.max(0, STATE.score - 200);
} else if (aggressor.isPlayer) {
STATE.score = Math.min(9999, STATE.score + 500);
}
spawnParticles(victim.x, victim.z, 26, 3.2, 1.6);
shake = Math.min(1, shake + 0.5);
}
}
function resolveTruckObstacle(t, o) {
var nx, nz, pen;
if (o.type === 'box') {
var cx = Math.max(o.x - o.hx, Math.min(t.x, o.x + o.hx));
var cz = Math.max(o.z - o.hz, Math.min(t.z, o.z + o.hz));
var bx = t.x - cx, bz = t.z - cz;
var d = Math.hypot(bx, bz);
if (d >= t.r) return;
if (d < 0.0001) {
var left = t.x - (o.x - o.hx), right = (o.x + o.hx) - t.x;
var top = t.z - (o.z - o.hz), bot = (o.z + o.hz) - t.z;
var m = Math.min(left, right, top, bot);
if (m === left) { nx = -1; nz = 0; pen = t.r - left; }
else if (m === right) { nx = 1; nz = 0; pen = t.r - right; }
else if (m === top) { nx = 0; nz = -1; pen = t.r - top; }
else { nx = 0; nz = 1; pen = t.r - bot; }
} else {
nx = bx / d; nz = bz / d; pen = t.r - d;
}
t.x += nx * pen; t.z += nz * pen;
var dot = t.vx * nx + t.vz * nz;
if (dot < 0) {
var ee = 0.5;
t.vx -= (1 + ee) * dot * nx;
t.vz -= (1 + ee) * dot * nz;
}
if (dot < -4) {
t.hitFlash = Math.max(t.hitFlash, 0.6);
if (t.isPlayer) STATE.score = Math.min(9999, STATE.score + 25);
spawnParticles(t.x, t.z, 6, 1.8, 0.8);
}
} else {
var ox = t.x - o.x, oz = t.z - o.z;
var d2 = Math.hypot(ox, oz);
var minD = t.r + o.r;
if (d2 >= minD || d2 < 0.0001) return;
nx = ox / d2; nz = oz / d2;
pen = minD - d2;
t.x += nx * pen; t.z += nz * pen;
var dot2 = t.vx * nx + t.vz * nz;
if (dot2 < 0) {
var ee2 = 0.5;
t.vx -= (1 + ee2) * dot2 * nx;
t.vz -= (1 + ee2) * dot2 * nz;
}
if (dot2 < -4) {
t.hitFlash = Math.max(t.hitFlash, 0.6);
spawnParticles(t.x, t.z, 6, 1.8, 0.8);
}
}
}
function clampToBounds(t) {
var limX = ARENA.x - t.r - 1.2;
var limZ = ARENA.z - t.r - 1.2;
if (t.x > limX) { t.x = limX; if (t.vx > 0) t.vx *= -0.4; }
if (t.x < -limX) { t.x = -limX; if (t.vx < 0) t.vx *= -0.4; }
if (t.z > limZ) { t.z = limZ; if (t.vz > 0) t.vz *= -0.4; }
if (t.z < -limZ) { t.z = -limZ; if (t.vz < 0) t.vz *= -0.4; }
}
var flashColor = new THREE.Color();
function syncTruck(t, dt) {
var h = terrainHeight(t.x, t.z);
t.y = h;
var eps = 0.6;
var pitch = (terrainHeight(t.x + eps, t.z) - terrainHeight(t.x - eps, t.z)) / (2 * eps);
var roll = (terrainHeight(t.x, t.z + eps) - terrainHeight(t.x, t.z - eps)) / (2 * eps);
t.group.position.set(t.x, h, t.z);
t.group.rotation.set(roll, t.angle - Math.PI / 2, pitch);
var sp = Math.hypot(t.vx, t.vz);
for (var i = 0; i < t.wheels.length; i++) {
t.wheels[i].rotation.x += sp * dt / 0.62;
}
if (t.hitFlash > 0) {
t.hitFlash = Math.max(0, t.hitFlash - dt * 2.5);
var e = t.hitFlash * 0.7;
flashColor.setRGB(e, e * 0.28, 0);
for (var m = 0; m < t.mats.length; m++) t.mats[m].emissive.copy(flashColor);
} else {
for (var m2 = 0; m2 < t.mats.length; m2++) t.mats[m2].emissive.setRGB(0, 0, 0);
}
}
/* ================= CAMERA ================= */
function updateCamera(dt) {
var p = player;
var desired = new THREE.Vector3(p.x, p.y + 17, p.z - 21);
camera.position.lerp(desired, 1 - Math.exp(-3.5 * dt));
var look = new THREE.Vector3(
p.x + Math.sin(p.angle) * 7,
p.y + 2,
p.z + Math.cos(p.angle) * 7
);
camera.lookAt(look);
if (shake > 0) {
camera.position.x += (Math.random() - 0.5) * shake * 0.9;
camera.position.y += (Math.random() - 0.5) * shake * 0.9;
shake = Math.max(0, shake - dt * 2.6);
}
}
/* ================= LOOP ================= */
var rafId = null;
var lastT = performance.now();
function loop(now) {
rafId = requestAnimationFrame(loop);
var dt = Math.min(0.05, (now - lastT) / 1000);
lastT = now;
if (STATE.phase !== 'play') return;
simTime += dt;
update(dt);
renderer.render(scene, camera);
}
function startLoop() {
if (rafId !== null) return;
lastT = performance.now();
rafId = requestAnimationFrame(loop);
}
function stopLoop() {
if (rafId !== null) {
cancelAnimationFrame(rafId);
rafId = null;
}
}
function update(dt) {
updatePlayer(dt);
for (var i = 0; i < trucks.length; i++) {
if (!trucks[i].isPlayer && !trucks[i].wrecked) aiSteer(trucks[i], dt);
}
for (var i2 = 0; i2 < trucks.length; i2++) integrate(trucks[i2], dt);
for (var a = 0; a < trucks.length; a++) {
for (var b = a + 1; b < trucks.length; b++) resolveTruckTruck(trucks[a], trucks[b]);
}
for (var c = 0; c < trucks.length; c++) {
for (var o = 0; o < obstacles.length; o++) resolveTruckObstacle(trucks[c], obstacles[o]);
clampToBounds(trucks[c]);
}
for (var s = 0; s < trucks.length; s++) syncTruck(trucks[s], dt);
updateCrowd(simTime);
updateParticles(dt);
updateCamera(dt);
STATE.time -= dt;
if (STATE.time <= 0) {
STATE.time = 0;
updateHUD();
endGame();
return;
}
updateHUD();
}
/* ================= INIT ================= */
buildCrowd();
createTrucks();
updateHUD();
startLoop();
window.addEventListener('resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
})();
</script>
</body>
</html>
#demo sets two real state fields at init — STATE.score = 5000 and STATE.time = 20 — the same variables the HUD, the collision scoring and the end condition read. Nothing is faked on top; the clock genuinely counts down from 20 and the score genuinely starts at 5000.P sets STATE.phase = 'photo', which both cancels the pending requestAnimationFrame (so the render loop stops submitting frames) and makes loop() return before update() runs (so the physics step never executes). The canvas keeps its last painted frame and a DOM card with the arena name and score is overlaid; pressing P again flips the phase back and restarts the loop.j = -(1+e)·v_rel·n / (1/m₁ + 1/m₂) with e = 0.5. The faster body is tagged aggressor: the player scores +100 for ramming a rival, −25 when rammed, +500 and a wreck state (tumble, velocity decay, 5-second recovery) when impact exceeds 9. Truck-vs-obstacle is circle-vs-AABB for boxes (with a nearest-face fallback when the centre is inside) and circle-vs-circle for barrels and cones, using the same impulse response plus a hit flash and dust particles.