Rally
GPT 6 Sol · full
◈Preview unavailable — try playing the result
Browser check: Preview unavailable or timed out. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 36,926 bytes · SHA-256 72711d38245c
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>DUSTLINE — Rally Sprint</title><script src="https://cdn.jsdelivr.net/npm/three@0.160.1/build/three.min.js"></script><style>
:root{--lime:#d8f35d;--cream:#f7f4e8;--ink:#17211c;--glass:rgba(14,25,23,.75)}*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#101e20;color:var(--cream);font-family:Inter,system-ui,-apple-system,Segoe UI,sans-serif;user-select:none;touch-action:none}#game{position:fixed;inset:0;width:100%;height:100%;display:block}#vignette{position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at 50% 46%,transparent 42%,rgba(6,20,18,.28) 100%)}.hidden{display:none!important}.hud{position:fixed;inset:0;pointer-events:none}#topbar{position:absolute;top:24px;left:28px;right:28px;display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.brand{font-size:14px;font-weight:950;letter-spacing:.23em;font-style:italic;text-shadow:0 2px 12px #14221c}.brand b{color:var(--lime)}.raceStats{display:flex;gap:24px;padding:12px 19px;background:var(--glass);backdrop-filter:blur(12px);border:1px solid #ffffff29;border-radius:13px;box-shadow:0 8px 35px #0003}.stat{min-width:75px}.stat small,.meterLabel{display:block;color:#b8c8bb;text-transform:uppercase;letter-spacing:.17em;font-size:9px;font-weight:800;margin-bottom:5px}.stat strong{display:block;font-size:21px;font-weight:800;font-variant-numeric:tabular-nums;line-height:1}.stat strong em{font-size:12px;font-style:normal;color:var(--lime)}#bottom{position:absolute;left:28px;right:28px;bottom:25px;display:flex;align-items:flex-end;justify-content:space-between}.speedpanel{background:var(--glass);border:1px solid #ffffff29;border-radius:16px;padding:14px 18px 15px;width:206px;backdrop-filter:blur(12px)}.speedrow{display:flex;align-items:baseline;justify-content:space-between}.speednum{font-size:55px;line-height:.96;font-weight:900;letter-spacing:-.065em;font-variant-numeric:tabular-nums}.speedunit{color:var(--lime);font-weight:900;font-size:12px;letter-spacing:.13em}.gear{background:var(--lime);color:#18251c;border-radius:5px;font-size:16px;font-weight:950;padding:2px 8px;margin-left:10px}.rpmbar{height:5px;margin-top:13px;border-radius:10px;background:#ffffff30;overflow:hidden}.rpmfill{height:100%;width:0;background:linear-gradient(90deg,#d8f35d,#ffb84f,#ff604e);transition:width .12s}.speedfoot{display:flex;justify-content:space-between;margin-top:7px;color:#afc1b1;font-size:9px;letter-spacing:.12em;font-weight:750}.rightpanel{display:flex;align-items:flex-end;gap:12px}#minimapWrap{background:var(--glass);border:1px solid #ffffff30;border-radius:15px;padding:11px 12px 8px;backdrop-filter:blur(12px)}#minimapWrap .meterLabel{margin:0 0 5px 3px}#minimap{display:block;width:178px;height:134px}#surface{position:absolute;top:96px;left:28px;border-radius:7px;background:#1b2925c7;border:1px solid #ffffff24;padding:7px 10px;color:#d8f35d;font-size:10px;font-weight:900;letter-spacing:.17em}.hint{color:#e7eddfa8;font-size:10px;letter-spacing:.09em;text-align:right;margin-bottom:5px;text-shadow:0 2px 7px #000}.overlay{position:fixed;inset:0;z-index:5;display:flex;align-items:center;justify-content:center;background:linear-gradient(90deg,rgba(8,25,23,.83),rgba(8,25,23,.39) 65%,rgba(8,25,23,.25));backdrop-filter:blur(5px)}.menu{width:min(570px,calc(100vw - 42px));padding:28px 0}.eyebrow{display:inline-block;color:var(--lime);font-weight:900;font-size:12px;letter-spacing:.28em;margin-bottom:15px}.menu h1{font-size:clamp(66px,11vw,132px);letter-spacing:-.095em;font-weight:1000;font-style:italic;line-height:.82;margin:0 0 16px;text-shadow:0 8px 25px #07151266}.menu h1 span{color:var(--lime)}.menu h2{font-size:clamp(46px,7vw,78px);letter-spacing:-.07em;font-weight:950;font-style:italic;line-height:1;margin:0 0 17px}.menu p{color:#e2eaddc7;font-size:15px;line-height:1.6;max-width:440px;margin:18px 0 26px}.btn{pointer-events:auto;appearance:none;background:var(--lime);color:#18251c;border:0;border-radius:8px;padding:16px 27px;font:900 13px Inter,system-ui,sans-serif;letter-spacing:.17em;cursor:pointer;box-shadow:0 7px 0 #96a943,0 17px 32px #0005;transition:transform .15s,box-shadow .15s}.btn:hover{transform:translateY(-3px);box-shadow:0 10px 0 #96a943,0 20px 32px #0005}.btn:active{transform:translateY(5px);box-shadow:0 2px 0 #96a943}.btn.secondary{background:#e7eee2;box-shadow:0 7px 0 #a8b0a6;margin-left:10px}.menuFoot{margin-top:35px;display:flex;gap:20px;flex-wrap:wrap;color:#d5e2d0ad;font-size:11px;font-weight:750;letter-spacing:.08em}.menuFoot b{color:var(--lime)}#countdown{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:4;font-weight:1000;font-style:italic;font-size:min(28vw,230px);letter-spacing:-.09em;color:var(--lime);text-shadow:0 12px 0 #15271b,0 23px 60px #07120fa8;pointer-events:none;animation:pulse .55s ease-out}#countdown.go{font-size:min(24vw,195px)}@keyframes pulse{from{transform:scale(1.5);opacity:0}to{transform:scale(1);opacity:1}}.finishGrid{display:flex;gap:12px;margin:24px 0}.finishGrid div{background:#ffffff1c;border:1px solid #ffffff29;padding:15px 20px;border-radius:10px;min-width:145px}.finishGrid small{display:block;color:#afc3b3;font-size:10px;letter-spacing:.12em;font-weight:900;margin-bottom:6px}.finishGrid strong{font-size:24px;font-variant-numeric:tabular-nums}#touch{position:fixed;inset:0;pointer-events:none;z-index:3;display:none}.touchCluster{position:absolute;bottom:18px;display:flex;align-items:flex-end;gap:10px;pointer-events:auto}.touchCluster.left{left:16px}.touchCluster.right{right:16px}.touchBtn{border:1px solid #ffffff77;color:white;background:#10221cb3;border-radius:16px;width:68px;height:68px;font-size:24px;font-weight:900;backdrop-filter:blur(7px);touch-action:none}.touchBtn:active,.touchBtn.pressed{background:#d8f35d;color:#18251c}.touchBtn.gas{height:99px;background:#c8e555d9;color:#14221b;font-size:13px;letter-spacing:.08em}.touchBtn.brake,.touchBtn.hand{font-size:11px;letter-spacing:.06em}.touchBtn.hand{height:52px}#toast{position:fixed;top:148px;left:50%;transform:translateX(-50%);border-radius:8px;background:#18251cde;color:var(--lime);font-weight:900;letter-spacing:.15em;padding:11px 18px;font-size:12px;pointer-events:none;opacity:0;transition:opacity .2s;white-space:nowrap}#toast.show{opacity:1}@media(max-width:700px){#topbar{top:12px;left:12px;right:12px}.brand{font-size:11px}.raceStats{gap:10px;padding:9px 10px}.stat{min-width:50px}.stat small{font-size:7px}.stat strong{font-size:14px}#surface{left:12px;top:75px;font-size:8px}#bottom{left:12px;right:12px;bottom:100px}.speedpanel{width:136px;padding:9px 11px}.speednum{font-size:36px}.gear{font-size:12px}.hint{display:none}#minimapWrap{padding:7px}#minimap{width:120px;height:92px}#minimapWrap .meterLabel{font-size:8px}.menu h1{font-size:clamp(58px,14vw,100px)}.menuFoot{gap:10px}#touch{display:block}}@media(pointer:coarse){#touch{display:block}}@media(max-width:700px){.touchBtn{width:61px;height:61px}.touchBtn.gas{height:91px}.touchBtn.hand{height:49px}.touchCluster{gap:7px}.touchCluster.left{left:11px}.touchCluster.right{right:11px}#bottom{bottom:115px}.brand{max-width:94px;line-height:1.25}.raceStats{gap:8px}.stat{min-width:46px}}
</style></head><body><canvas id="game"></canvas><div id="vignette"></div><div class="hud"><div id="topbar"><div class="brand">DUSTLINE <b>/</b> RALLY SPRINT</div><div class="raceStats"><div class="stat"><small>LAP</small><strong id="lap">1 <em>/ 3</em></strong></div><div class="stat"><small>TIME</small><strong id="time">00:00.00</strong></div><div class="stat"><small>LAST LAP</small><strong id="lastLap">--:--.--</strong></div><div class="stat"><small>BEST LAP</small><strong id="best">--:--.--</strong></div></div></div><div id="surface">DIRT · GOOD GRIP</div><div id="bottom"><div class="speedpanel"><span class="meterLabel">SPEED</span><div class="speedrow"><div><span class="speednum" id="speed">000</span> <span class="speedunit">KM/H</span></div><span class="gear" id="gear">N</span></div><div class="rpmbar"><div class="rpmfill" id="rpm"></div></div><div class="speedfoot"><span>RPM</span><span id="traction">TRACTION 100%</span></div></div><div class="rightpanel"><span class="hint">WASD / ARROWS DRIVE · SPACE DRIFT<br>C CAMERA · P PAUSE · R RESTART</span><div id="minimapWrap"><span class="meterLabel">STAGE MAP</span><canvas id="minimap" width="356" height="268"></canvas></div></div></div></div><div id="toast"></div><div id="countdown" class="hidden">3</div><div id="startOverlay" class="overlay"><div class="menu"><span class="eyebrow">01 / THE WILDERNESS CIRCUIT</span><h1>DUST<span>LINE.</span></h1><p>Three laps. One winding mountain road. Find your rhythm, kick up the dust, and beat the clock.</p><button class="btn" id="startBtn">START YOUR ENGINE →</button><div class="menuFoot"><span><b>↑ / W</b> ACCELERATE</span><span><b>← → / A D</b> STEER</span><span><b>SPACE</b> HANDBRAKE</span><span><b>P</b> PAUSE</span></div></div></div><div id="pauseOverlay" class="overlay hidden"><div class="menu"><span class="eyebrow">RACE INTERRUPTED</span><h2>TAKE A BREATH.</h2><p>The clock is stopped. The dirt isn't going anywhere.</p><button class="btn" id="resumeBtn">RESUME RACE →</button><button class="btn secondary" id="pauseRestartBtn">RESTART</button></div></div><div id="finishOverlay" class="overlay hidden"><div class="menu"><span class="eyebrow">CHECKERED FLAG / STAGE COMPLETE</span><h2>WELL DRIVEN.</h2><p>Three laps in the books. Think you can go faster?</p><div class="finishGrid"><div><small>TOTAL TIME</small><strong id="finalTime">--</strong></div><div><small>BEST LAP</small><strong id="finalBest">--</strong></div></div><button class="btn" id="finishRestartBtn">RACE AGAIN →</button></div></div><div id="touch"><div class="touchCluster left"><button class="touchBtn" data-control="left">◀</button><button class="touchBtn" data-control="right">▶</button></div><div class="touchCluster right"><button class="touchBtn hand" data-control="hand">DRIFT</button><button class="touchBtn brake" data-control="brake">BRAKE</button><button class="touchBtn gas" data-control="gas">GAS</button></div></div><script>
(()=>{'use strict';
if(!window.THREE){document.querySelector('#startOverlay p').textContent='Unable to load the 3D engine. Please check your internet connection and refresh.';document.querySelector('#startBtn').disabled=true;return;}
const T=THREE, $=s=>document.querySelector(s), clamp=(v,a,b)=>Math.max(a,Math.min(b,v)), lerp=(a,b,t)=>a+(b-a)*t, TAU=Math.PI*2;
const UI={lap:$('#lap'),time:$('#time'),best:$('#best'),last:$('#lastLap'),speed:$('#speed'),gear:$('#gear'),rpm:$('#rpm'),traction:$('#traction'),surface:$('#surface'),count:$('#countdown'),toast:$('#toast')};
const app={scene:null,camera:null,renderer:null,track:[],trackMesh:null,car:null,wheels:[],cameraMode:0,obstacles:[],particles:[],marks:[],keys:{},touch:{},state:'menu',clock:0,lapStart:0,lap:1,best:Infinity,lapTimes:[],gate:1,lastGate:0,carX:0,carZ:0,yaw:0,speed:0,slide:0,steerVis:0,near:0,off:0,terrainY:0,lastTime:0,lastParticle:0,lastMark:0,countVal:3,countTimer:0,shake:0,audio:null,minimap:null,toastTimer:null};
const control=(name)=>!!(app.touch[name]||app.keys[name]);
function fmt(t){if(!Number.isFinite(t))return '--:--.--';let m=Math.floor(t/60),s=Math.floor(t%60),c=Math.floor((t%1)*100);return String(m).padStart(2,'0')+':'+String(s).padStart(2,'0')+'.'+String(c).padStart(2,'0')}
function terrainRaw(x,z){return -2.6+1.25*Math.sin(x*.015)*Math.cos(z*.013)+.75*Math.sin(z*.034+x*.009)}
// Track samples: closed spline, with elevation baked into the road and surrounding terrain.
function makeTrack(){const pts=[[-12,20,1],[-18,98,3],[8,181,6],[83,220,9],[168,200,6],[229,151,2],[233,78,0],[188,21,2],[199,-59,5],[156,-129,8],[65,-165,4],[-30,-151,1],[-103,-183,2],[-186,-157,5],[-229,-92,7],[-209,-17,5],[-151,30,2],[-104,99,4],[-79,40,1],[-56,-24,0]];
const curve=new T.CatmullRomCurve3(pts.map(p=>new T.Vector3(p[0],p[2],p[1])),true,'catmullrom',.45),N=420;
for(let i=0;i<N;i++){let t=i/N,p=curve.getPointAt(t),d=curve.getTangentAt(t).normalize();app.track.push({x:p.x,z:p.z,y:p.y,tx:d.x,tz:d.z,nx:d.z,nz:-d.x,width:12.8,type:((i>80&&i<100)||(i>263&&i<280))?'mud':((i<46)||(i>190&&i<218))?'asphalt':'dirt'})}app.yaw=Math.atan2(app.track[8].tx,app.track[8].tz);app.carX=app.track[8].x;app.carZ=app.track[8].z;
}
function nearest(x,z){let idx=0,dist=Infinity;for(let i=0;i<app.track.length;i++){let p=app.track[i],dx=x-p.x,dz=z-p.z,d=dx*dx+dz*dz;if(d<dist){dist=d;idx=i}}return {i:idx,d:Math.sqrt(dist),p:app.track[idx]}}
function heightAt(x,z,n){n=n||nearest(x,z);let blend=clamp((n.d-7)/20,0,1);return lerp(n.p.y-.18,terrainRaw(x,z),blend)}
function mat(color,rough=1){return new T.MeshStandardMaterial({color,roughness:rough,flatShading:true})}
function box(parent,w,h,d,material,x,y,z){let m=new T.Mesh(new T.BoxGeometry(w,h,d),material);m.position.set(x,y,z);m.castShadow=true;m.receiveShadow=true;parent.add(m);return m}
function buildWorld(){app.scene=new T.Scene();app.scene.background=new T.Color(0x9ac6c4);app.scene.fog=new T.FogExp2(0x9ac6c4,.0028);app.camera=new T.PerspectiveCamera(69,innerWidth/innerHeight,.1,1000);app.renderer=new T.WebGLRenderer({canvas:$('#game'),antialias:true,powerPreference:'high-performance'});app.renderer.setPixelRatio(Math.min(devicePixelRatio,1.7));app.renderer.setSize(innerWidth,innerHeight);app.renderer.shadowMap.enabled=true;app.renderer.shadowMap.type=T.PCFSoftShadowMap;app.renderer.outputColorSpace=T.SRGBColorSpace;app.renderer.toneMapping=T.ACESFilmicToneMapping;app.renderer.toneMappingExposure=1.35;
app.scene.add(new T.HemisphereLight(0xdff5ee,0x657d55,2.05));let sun=new T.DirectionalLight(0xffe5ae,2.35);sun.position.set(-95,150,-110);sun.castShadow=true;sun.shadow.mapSize.set(2048,2048);sun.shadow.camera.left=-240;sun.shadow.camera.right=240;sun.shadow.camera.top=240;sun.shadow.camera.bottom=-240;sun.shadow.camera.near=1;sun.shadow.camera.far=460;sun.shadow.bias=-.00025;app.scene.add(sun);app.sun=sun;
// Vertex-colored rolling ground blends up toward road height so hills feel integrated.
let geo=new T.PlaneGeometry(1450,1450,110,110);geo.rotateX(-Math.PI/2);let pos=geo.attributes.position,colors=[];let col=new T.Color();for(let i=0;i<pos.count;i++){let x=pos.getX(i),z=pos.getZ(i),n=nearest(x,z),h=heightAt(x,z,n)-2.6*(1-clamp((n.d-10)/32,0,1));pos.setY(i,h);let v=.5+.24*Math.sin(x*.08+z*.04)+.15*Math.sin(z*.13);col.setRGB(.22+v*.09,.35+v*.14,.20+v*.07);colors.push(col.r,col.g,col.b)}geo.setAttribute('color',new T.Float32BufferAttribute(colors,3));geo.computeVertexNormals();let ground=new T.Mesh(geo,new T.MeshStandardMaterial({vertexColors:true,roughness:1,flatShading:true}));ground.receiveShadow=true;app.scene.add(ground);
function ribbon(extra,yoff,material,vertexColors=false){let vertices=[],cols=[],indices=[],N=app.track.length;for(let i=0;i<N;i++){let p=app.track[i],w=p.width/2+extra;for(let s of [-1,1]){vertices.push(p.x+p.nx*w*s,p.y+yoff,p.z+p.nz*w*s);if(vertexColors){let base=p.type==='mud'?[.17,.13,.105]:p.type==='asphalt'?[.11,.145,.14]:[.29,.175,.095],shade=1+.045*Math.sin(i*.83);cols.push(...base.map(c=>c*shade))}}let j=i*2,k=((i+1)%N)*2;indices.push(j,k,j+1,j+1,k,k+1)}let g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(vertices,3));if(vertexColors)g.setAttribute('color',new T.Float32BufferAttribute(cols,3));g.setIndex(indices);g.computeVertexNormals();let mesh=new T.Mesh(g,material);mesh.receiveShadow=true;app.scene.add(mesh);return mesh}
ribbon(2,-.14,mat(0x76644a));ribbon(0,.12,new T.MeshStandardMaterial({vertexColors:true,roughness:1,side:T.DoubleSide}),true);
// Edge posts and dashed center line communicate the path through blind bends.
let postMat=mat(0xece8ce),stripeMat=mat(0xe5e0bb),red=mat(0xe65238);for(let i=0;i<app.track.length;i+=8){let p=app.track[i];for(let side of [-1,1]){let post=box(app.scene,.28,1.12,.28,(Math.floor(i/8)%3===0)?red:postMat,p.x+p.nx*side*8.2,p.y+.45,p.z+p.nz*side*8.2);post.rotation.y=Math.atan2(p.tx,p.tz)}}for(let i=5;i<app.track.length;i+=7){let p=app.track[i];if(p.type==='asphalt'){let line=box(app.scene,.17,.025,3.2,stripeMat,p.x,p.y+.045,p.z);line.rotation.y=Math.atan2(p.tx,p.tz)}}
// Finish/start marking and a simple overhead rally gate.
let p=app.track[0],yaw=Math.atan2(p.tx,p.tz),arch=new T.Group();arch.position.set(p.x,p.y,p.z);arch.rotation.y=yaw;app.scene.add(arch);let dark=mat(0x1b332d),lime=mat(0xd9f35c);box(arch,.45,7,.5,dark,-8,3.5,0);box(arch,.45,7,.5,dark,8,3.5,0);box(arch,16.5,1.18,.7,lime,0,7,0);let banner=box(arch,8,.53,.73,dark,0,7,0);banner.position.z=.39;for(let j=0;j<12;j++)box(arch,1.32,.045,.8,j%2?postMat:dark,-7.25+j*1.32,.09,0);
let signCanvas=document.createElement('canvas');signCanvas.width=512;signCanvas.height=100;let ctx=signCanvas.getContext('2d');ctx.fillStyle='#d9f35c';ctx.fillRect(0,0,512,100);ctx.fillStyle='#172b23';ctx.font='italic 900 58px Arial';ctx.textAlign='center';ctx.fillText('DUSTLINE / START',256,69);let sign=new T.Mesh(new T.PlaneGeometry(12,2.34),new T.MeshBasicMaterial({map:new T.CanvasTexture(signCanvas),side:T.DoubleSide}));sign.position.set(0,7,0);arch.add(sign);
}
function buildScenery(){let trunk=mat(0x70523a),leafMats=[mat(0x2c6145),mat(0x3c7650),mat(0x456e40),mat(0x275947)],rockMats=[mat(0x829187),mat(0x9c9f8b),mat(0x707d79)];let seed=67231;function rand(){seed=(seed*1664525+1013904223)>>>0;return seed/4294967296}
// Instancing keeps the forest inexpensive to render.
let trees=[];for(let i=0;i<360;i++){let x=(rand()-.5)*920,z=(rand()-.5)*870,n=nearest(x,z);if(n.d<17||n.d>380)continue;trees.push({x,z,h:heightAt(x,z,n),size:2.7+rand()*5.8,variant:Math.floor(rand()*4)})}let cyl=new T.CylinderGeometry(.14,.23,1,5),cone=new T.ConeGeometry(1,1,6),dummy=new T.Object3D();let trunks=new T.InstancedMesh(cyl,trunk,trees.length);trunks.castShadow=true;app.scene.add(trunks);for(let i=0;i<trees.length;i++){let t=trees[i];dummy.position.set(t.x,t.h+t.size*.26,t.z);dummy.scale.set(t.size*.23,t.size*.52,t.size*.23);dummy.rotation.y=rand()*TAU;dummy.updateMatrix();trunks.setMatrixAt(i,dummy.matrix)}for(let variant=0;variant<4;variant++){let arr=trees.filter(t=>t.variant===variant),fol=new T.InstancedMesh(cone,leafMats[variant],arr.length*2);fol.castShadow=true;app.scene.add(fol);arr.forEach((t,i)=>{for(let j=0;j<2;j++){dummy.position.set(t.x,t.h+t.size*(.58+j*.18),t.z);dummy.scale.set(t.size*(.35-j*.08),t.size*(.7-j*.13),t.size*(.35-j*.08));dummy.rotation.y=rand()*TAU;dummy.updateMatrix();fol.setMatrixAt(i*2+j,dummy.matrix)}})}
for(let i=0;i<105;i++){let x=(rand()-.5)*750,z=(rand()-.5)*710,n=nearest(x,z);if(n.d<8||n.d>255)continue;let r=.7+rand()*1.8,y=heightAt(x,z,n);let rock=new T.Mesh(new T.DodecahedronGeometry(r,0),rockMats[Math.floor(rand()*3)]);rock.position.set(x,y+r*.44,z);rock.rotation.set(rand(),rand()*TAU,rand());rock.scale.y=.6+rand()*.35;rock.castShadow=true;rock.receiveShadow=true;app.scene.add(rock);if(n.d<15)app.obstacles.push({x,z,r:r*.85})}
// Roadside chevrons, colored barrels and spectator stands at a few corners.
const arrowMat=mat(0xf5e8cc),orange=mat(0xea7140);for(let idx of [33,52,113,143,225,306,365]){let p=app.track[idx];for(let j=0;j<3;j++){let q=app.track[(idx+j*3)%app.track.length],x=q.x+q.nx*9.5,z=q.z+q.nz*9.5;let g=new T.Group();g.position.set(x,q.y,z);g.rotation.y=Math.atan2(q.tx,q.tz);app.scene.add(g);box(g,.14,1.2,.14,darkMat(),0,.6,0);box(g,1.5,.74,.12,orange,0,1.55,0);box(g,.65,.16,.14,arrowMat,.07,1.55,.09).rotation.z=-.5;app.obstacles.push({x,z,r:.7})}}function darkMat(){return mat(0x23362e)}
for(let idx of [49,151,291]){let p=app.track[idx];for(let side of [-1,1]){let x=p.x+p.nx*10*side,z=p.z+p.nz*10*side;let barrel=new T.Group();barrel.position.set(x,p.y,z);app.scene.add(barrel);let b=new T.Mesh(new T.CylinderGeometry(.65,.65,1.3,10),orange);b.position.y=.65;b.castShadow=true;barrel.add(b);box(barrel,1.35,.17,1.35,arrowMat,0,.72,0);app.obstacles.push({x,z,r:.8})}}
// Small colorful crowd clusters watch from safe banks by the start and hairpins.
let shirts=[mat(0xf2b75c),mat(0xe9e5d3),mat(0xe45f4e),mat(0x94bad0),mat(0xdde86c)],skin=mat(0xd9a978),pants=mat(0x263c36);for(let idx of [3,7,44,47,166,169,312]){let p=app.track[idx],side=idx%2?1:-1;for(let k=0;k<7;k++){let q=app.track[(idx+k)%app.track.length],x=q.x+q.nx*side*(13+rand()*4),z=q.z+q.nz*side*(13+rand()*4),h=heightAt(x,z)-.15,g=new T.Group();g.position.set(x,h,z);g.rotation.y=rand()*TAU;app.scene.add(g);box(g,.22,.7,.2,pants,0,.48,0);box(g,.48,.62,.28,shirts[Math.floor(rand()*shirts.length)],0,1.12,0);let head=new T.Mesh(new T.IcosahedronGeometry(.22,0),skin);head.position.y=1.62;g.add(head);box(g,.12,.52,.14,skin,-.34,1.13,0);box(g,.12,.52,.14,skin,.34,1.13,0)}}
// Distant mountain silhouettes create depth beyond the drivable terrain.
let mountainMat=mat(0x71938b);for(let i=0;i<28;i++){let a=i/28*TAU,rad=580+rand()*180,ht=45+rand()*105,m=new T.Mesh(new T.ConeGeometry(45+rand()*65,ht,5),mountainMat);m.position.set(Math.sin(a)*rad,ht*.42-7,Math.cos(a)*rad);m.rotation.y=rand()*TAU;app.scene.add(m)}
}
function buildCar(){let car=new T.Group();app.scene.add(car);app.car=car;const body=mat(0xe7eee0,.48),dark=mat(0x172b2b,.7),windows=mat(0x315058,.25),accent=mat(0xdaec50,.55),rubber=mat(0x1b2223),red=mat(0xff4939,.5),white=mat(0xffefca,.3);
box(car,2.22,.68,4.22,body,0,.93,0);box(car,2.35,.23,1.12,accent,0,.73,1.18);box(car,2.12,.18,1.1,accent,0,1.24,-1.32);let cabin=box(car,1.85,.89,2.22,windows,0,1.68,-.18);cabin.geometry=new T.BoxGeometry(1.85,.89,2.22);box(car,1.7,.1,1.54,body,0,2.15,-.21);box(car,2.39,.22,.2,dark,0,.7,-2.08);box(car,2.36,.23,.28,dark,0,.67,2.12);
for(let s of [-1,1]){let wheel=new T.Mesh(new T.CylinderGeometry(.53,.53,.32,12),rubber);wheel.rotation.z=Math.PI/2;wheel.position.set(s*1.18,.58,1.34);wheel.castShadow=true;car.add(wheel);app.wheels.push(wheel);let rear=wheel.clone();rear.position.z=-1.33;car.add(rear);app.wheels.push(rear);let rim=new T.Mesh(new T.CylinderGeometry(.26,.26,.335,10),mat(0xb8c8c2,.4));rim.rotation.z=Math.PI/2;rim.position.set(s*1.19,.58,1.34);car.add(rim);let rim2=rim.clone();rim2.position.z=-1.33;car.add(rim2);box(car,.38,.20,.12,red,s*.83,1.05,-2.13);box(car,.45,.16,.12,white,s*.81,1.03,2.14);box(car,.1,.85,.12,dark,s*.82,1.65,-1.86)}box(car,2.65,.13,.64,dark,0,2.23,-1.79);box(car,1.34,.10,.35,accent,0,2.31,-1.79);box(car,1.38,.07,.12,accent,0,1.23,-2.17);
car.position.set(app.carX,app.track[8].y,app.carZ);car.rotation.y=app.yaw}
function initAudio(){if(app.audio)return;try{let c=new (window.AudioContext||window.webkitAudioContext)(),engine=c.createOscillator(),sub=c.createOscillator(),gain=c.createGain(),filter=c.createBiquadFilter();engine.type='sawtooth';sub.type='triangle';filter.type='lowpass';filter.frequency.value=330;gain.gain.value=.0001;engine.connect(filter);sub.connect(filter);filter.connect(gain);gain.connect(c.destination);engine.start();sub.start();let buffer=c.createBuffer(1,c.sampleRate*2,c.sampleRate),data=buffer.getChannelData(0);for(let i=0;i<data.length;i++)data[i]=Math.random()*2-1;let noise=c.createBufferSource(),ng=c.createGain(),nf=c.createBiquadFilter();noise.buffer=buffer;noise.loop=true;nf.type='bandpass';nf.frequency.value=850;ng.gain.value=0;noise.connect(nf);nf.connect(ng);ng.connect(c.destination);noise.start();app.audio={c,engine,sub,gain,filter,ng}}catch(e){}}
function tone(freq,duration,type='sine',vol=.12){if(!app.audio)return;let {c}=app.audio,o=c.createOscillator(),g=c.createGain();o.type=type;o.frequency.setValueAtTime(freq,c.currentTime);g.gain.setValueAtTime(vol,c.currentTime);g.gain.exponentialRampToValueAtTime(.001,c.currentTime+duration);o.connect(g);g.connect(c.destination);o.start();o.stop(c.currentTime+duration+.01)}
function audioUpdate(){let a=app.audio;if(!a)return;let v=Math.abs(app.speed),gear=Math.max(1,Math.ceil(v/9)),rpm=clamp((v-(gear-1)*9)/9,0,1);a.engine.frequency.setTargetAtTime(48+gear*26+rpm*92,a.c.currentTime,.08);a.sub.frequency.setTargetAtTime(34+v*1.7,a.c.currentTime,.1);a.filter.frequency.setTargetAtTime(220+v*20,a.c.currentTime,.12);a.gain.gain.setTargetAtTime(app.state==='racing'?.035+v*.0014:.012,a.c.currentTime,.12);a.ng.gain.setTargetAtTime(app.state==='racing'?(app.off>0?Math.min(.085,v*.002):Math.min(.055,Math.abs(app.slide)*.009)):0,a.c.currentTime,.12)}
function startRace(){initAudio();if(app.audio)app.audio.c.resume();resetCar();app.clock=0;app.lapStart=0;app.lap=1;app.best=Infinity;app.lapTimes=[];app.gate=1;app.countVal=3;app.countTimer=1;app.state='countdown';$('#startOverlay').classList.add('hidden');$('#finishOverlay').classList.add('hidden');$('#pauseOverlay').classList.add('hidden');showCount('3');tone(460,.16)}
function resetCar(){let p=app.track[8];app.carX=p.x;app.carZ=p.z;app.yaw=Math.atan2(p.tx,p.tz);app.speed=0;app.slide=0;app.car.position.set(p.x,p.y,p.z);app.car.rotation.set(0,app.yaw,0);for(let m of app.marks)app.scene.remove(m.mesh);app.marks=[];for(let p of app.particles){app.scene.remove(p.mesh)}app.particles=[]}
function showCount(text){UI.count.textContent=text;UI.count.classList.toggle('go',text==='GO!');UI.count.classList.remove('hidden');UI.count.style.animation='none';void UI.count.offsetWidth;UI.count.style.animation='pulse .55s ease-out'}
function toast(text){UI.toast.textContent=text;UI.toast.classList.add('show');clearTimeout(app.toastTimer);app.toastTimer=setTimeout(()=>UI.toast.classList.remove('show'),2100)}
function togglePause(){if(app.state==='racing'){app.state='paused';$('#pauseOverlay').classList.remove('hidden')}else if(app.state==='paused'){app.state='racing';$('#pauseOverlay').classList.add('hidden')}}
function finish(){app.state='finished';app.speed=0;$('#finalTime').textContent=fmt(app.clock);$('#finalBest').textContent=fmt(app.best);$('#finishOverlay').classList.remove('hidden');tone(523,.3,'triangle');setTimeout(()=>tone(659,.3,'triangle'),180);setTimeout(()=>tone(784,.75,'triangle'),380)}
function checkGates(n){let N=app.track.length,count=12,gateIdx=app.gate===12?N-15:Math.round(app.gate*N/count)%N,delta=Math.min(Math.abs(n.i-gateIdx),N-Math.abs(n.i-gateIdx));if(app.gate<13&&delta<5&&n.d<10.8&&app.speed>2){if(app.gate===12){app.gate=13;app.lastGate=app.clock;toast('FINAL SECTOR · CROSS THE LINE');return}app.gate++}if(app.gate===13&&n.i>=app.track.length-2&&n.d<10.8&&app.speed>2&&app.clock-app.lastGate>1){let lapTime=app.clock-app.lapStart;app.lapTimes.push(lapTime);app.best=Math.min(app.best,lapTime);app.lapStart=app.clock;if(app.lap>=3){finish();return}app.lap++;toast('LAP '+(app.lap-1)+' · '+fmt(lapTime));tone(780,.16);setTimeout(()=>tone(1040,.2),125);app.gate=1}}
function collision(x,z,r){let dx=app.carX-x,dz=app.carZ-z,dist=Math.hypot(dx,dz);if(dist<r+1.2&&dist>.001){let push=r+1.2-dist;app.carX+=dx/dist*push;app.carZ+=dz/dist*push;app.speed*=-.26;app.slide+=((Math.random()-.5)*5);app.shake=.65;burst(app.carX,app.carZ,0xffca6b,8);tone(85,.23,'sawtooth',.2)}}
function physics(dt){let n=nearest(app.carX,app.carZ),p=n.p;app.near=n.i;let side=(app.carX-p.x)*p.nx+(app.carZ-p.z)*p.nz,off=Math.max(0,Math.abs(side)-p.width/2);app.off=off;let mud=p.type==='mud'&&off<1,grass=off>1.4,gravel=off>0&&!grass;let surface=grass?'GRASS':gravel?'GRAVEL':mud?'MUD':p.type==='asphalt'?'ASPHALT':'DIRT';let max=grass?20:gravel?27:mud?17:p.type==='asphalt'?48:42,grip=grass?.48:gravel?.65:mud?.43:p.type==='asphalt'?.95:.73;
let gas=control('gas'),brake=control('brake'),steer=(control('right')?1:0)-(control('left')?1:0),hand=control('hand');if(gas)app.speed+=(app.speed<0?19:24*(1-Math.max(0,app.speed)/max*.58))*dt;if(brake)app.speed-= (app.speed>0?37:17)*dt;app.speed-=app.speed*(gas||brake?.13:.65)*dt;app.speed=clamp(app.speed,-13,max);if(Math.abs(app.speed)<.08&&!gas&&!brake)app.speed=0;
let vel=Math.abs(app.speed),steerRate=lerp(1.95,.84,clamp(vel/47,0,1)),turn=steer*steerRate*clamp(vel/8,.15,1)*(app.speed<0?-1:1);app.yaw+=turn*dt*(hand?1.22:1);let desiredSlide=steer*vel*vel*.0028*(hand?3.3:1)*(1.14-grip*.55);app.slide=lerp(app.slide,desiredSlide,clamp(dt*(hand?1.9:grip*4.8),0,1));if(!steer)app.slide*=Math.exp(-dt*(hand?1.2:3.8));let fx=Math.sin(app.yaw),fz=Math.cos(app.yaw),rx=Math.cos(app.yaw),rz=-Math.sin(app.yaw);app.carX+=(fx*app.speed+rx*app.slide)*dt;app.carZ+=(fz*app.speed+rz*app.slide)*dt;
let updated=nearest(app.carX,app.carZ);if(updated.d>12.4){let dx=app.carX-updated.p.x,dz=app.carZ-updated.p.z,dist=Math.hypot(dx,dz);app.carX=updated.p.x+dx/dist*12.4;app.carZ=updated.p.z+dz/dist*12.4;app.speed*=-.34;app.slide*=-.4;app.shake=.45;burst(app.carX,app.carZ,0xffd897,7);tone(95,.2,'sawtooth',.16)}for(let ob of app.obstacles){if(Math.abs(ob.x-app.carX)<4&&Math.abs(ob.z-app.carZ)<4)collision(ob.x,ob.z,ob.r)}
let h=heightAt(app.carX,app.carZ,updated),targetYaw=app.yaw;app.car.position.set(app.carX,h,app.carZ);app.car.rotation.y=targetYaw;app.car.rotation.z=lerp(app.car.rotation.z,clamp(-steer*vel*.005-app.slide*.012,-.14,.14),dt*6);app.car.rotation.x=lerp(app.car.rotation.x,clamp((gas?-.025:0)+(brake?.045:0),-.06,.07),dt*5);for(let w of app.wheels)w.rotation.x+=app.speed*dt*.95;
if((grass||gravel||mud||Math.abs(app.slide)>1.15)&&vel>7){app.lastParticle-=dt;if(app.lastParticle<=0){app.lastParticle=.032;burst(app.carX-fx*1.6,app.carZ-fz*1.6,mud?0x796f5b:grass?0x9fa778:0xc8a777,2,false)}}if(Math.abs(app.slide)>1.3&&vel>10&&!grass){app.lastMark-=dt;if(app.lastMark<=0){app.lastMark=.075;addMark(h)}}
checkGates(updated);UI.surface.textContent=surface+' · '+(grass||mud?'LOW GRIP':gravel?'LOOSE SURFACE':'GOOD GRIP');UI.surface.style.color=grass||mud?'#ffbd81':'#d8f35d';UI.traction.textContent='TRACTION '+Math.round(grip*100)+'%';}
function burst(x,z,color,count,spark=true){for(let i=0;i<count;i++){if(app.particles.length>85){let old=app.particles.shift();app.scene.remove(old.mesh);old.mesh.geometry.dispose()}let size=spark?.12+Math.random()*.12:.27+Math.random()*.42,mesh=new T.Mesh(new T.SphereGeometry(size,5,4),new T.MeshBasicMaterial({color,transparent:true,opacity:spark?.9:.48,depthWrite:false}));let h=heightAt(x,z);mesh.position.set(x+(Math.random()-.5)*2,h+.3+Math.random()*.55,z+(Math.random()-.5)*2);app.scene.add(mesh);app.particles.push({mesh,life:spark?.45:.8,max:spark?.45:.8,vx:(Math.random()-.5)*(spark?11:3),vy:spark?2+Math.random()*4:1.2+Math.random(),vz:(Math.random()-.5)*(spark?11:3)})}}
function addMark(y){let f=new T.Vector3(Math.sin(app.yaw),0,Math.cos(app.yaw)),r=new T.Vector3(Math.cos(app.yaw),0,-Math.sin(app.yaw));for(let side of [-1,1]){let mesh=new T.Mesh(new T.PlaneGeometry(.22,1.4),new T.MeshBasicMaterial({color:0x292a25,transparent:true,opacity:.24,depthWrite:false,side:T.DoubleSide}));mesh.rotation.set(-Math.PI/2,0,-app.yaw);mesh.position.set(app.carX-f.x*1.4+r.x*side*.92,y+.04,app.carZ-f.z*1.4+r.z*side*.92);app.scene.add(mesh);app.marks.push({mesh,life:12})}if(app.marks.length>170){let old=app.marks.splice(0,2);old.forEach(o=>{app.scene.remove(o.mesh);o.mesh.geometry.dispose();o.mesh.material.dispose()})}}
function updateEffects(dt){for(let i=app.particles.length-1;i>=0;i--){let p=app.particles[i];p.life-=dt;if(p.life<=0){app.scene.remove(p.mesh);p.mesh.geometry.dispose();p.mesh.material.dispose();app.particles.splice(i,1);continue}p.mesh.position.x+=p.vx*dt;p.mesh.position.y+=p.vy*dt;p.mesh.position.z+=p.vz*dt;p.vy-=3*dt;p.mesh.material.opacity=p.life/p.max*.55;p.mesh.scale.setScalar(1+(1-p.life/p.max)*1.7)}for(let i=app.marks.length-1;i>=0;i--){let m=app.marks[i];m.life-=dt;if(m.life<0){app.scene.remove(m.mesh);m.mesh.geometry.dispose();m.mesh.material.dispose();app.marks.splice(i,1)}else m.mesh.material.opacity=Math.min(.24,m.life*.04)}}
function updateCamera(dt){let f=new T.Vector3(Math.sin(app.yaw),0,Math.cos(app.yaw)),r=new T.Vector3(Math.cos(app.yaw),0,-Math.sin(app.yaw)),high=app.cameraMode===1,portrait=innerWidth/innerHeight<.7,d=(high?17:10.8)*(portrait?1.18:1),h=(high?12.5:5.0)*(portrait?1.12:1);let camTarget=new T.Vector3(app.carX-f.x*d-r.x*app.slide*.18,app.car.position.y+h,app.carZ-f.z*d-r.z*app.slide*.18);app.shake=Math.max(0,app.shake-dt*1.9);camTarget.x+=(Math.random()-.5)*app.shake*.35;camTarget.y+=(Math.random()-.5)*app.shake*.25;app.camera.position.lerp(camTarget,1-Math.exp(-dt*4.3));let look=new T.Vector3(app.carX+f.x*(high?8:11),app.car.position.y+(high?1.3:1.5),app.carZ+f.z*(high?8:11));app.camera.lookAt(look);app.camera.fov=lerp(app.camera.fov,(high?67:69)+Math.min(8,Math.abs(app.speed)*.15),dt*2);app.camera.updateProjectionMatrix();app.sun.position.set(app.carX-90,145,app.carZ-105);app.sun.target.position.set(app.carX,0,app.carZ);app.sun.target.updateMatrixWorld()}
function drawMinimap(){let c=$('#minimap'),ctx=c.getContext('2d'),w=c.width,h=c.height;ctx.clearRect(0,0,w,h);let minx=-245,maxx=250,minz=-200,maxz=244,s=Math.min((w-26)/(maxx-minx),(h-20)/(maxz-minz)),X=x=>w/2+(x-(minx+maxx)/2)*s,Y=z=>h/2-(z-(minz+maxz)/2)*s;ctx.lineJoin='round';ctx.lineCap='round';ctx.beginPath();app.track.forEach((p,i)=>i?ctx.lineTo(X(p.x),Y(p.z)):ctx.moveTo(X(p.x),Y(p.z)));ctx.closePath();ctx.strokeStyle='#0c1b19';ctx.lineWidth=16;ctx.stroke();ctx.strokeStyle='#71817a';ctx.lineWidth=9;ctx.stroke();ctx.strokeStyle='#d1b896';ctx.lineWidth=4;ctx.stroke();let p=app.track[0];ctx.fillStyle='#e8f2d5';ctx.fillRect(X(p.x)-3,Y(p.z)-3,6,6);let x=X(app.carX),y=Y(app.carZ);ctx.beginPath();ctx.arc(x,y,8,0,TAU);ctx.fillStyle='#d8f35d44';ctx.fill();ctx.beginPath();ctx.arc(x,y,4.3,0,TAU);ctx.fillStyle='#e4f765';ctx.fill();ctx.strokeStyle='#17251f';ctx.lineWidth=1.5;ctx.stroke()}
function updateUI(){UI.lap.innerHTML=Math.min(app.lap,3)+' <em>/ 3</em>';UI.time.textContent=fmt(app.clock);UI.best.textContent=fmt(app.best);UI.last.textContent=fmt(app.lapTimes.at(-1)??Infinity);UI.speed.textContent=String(Math.round(Math.abs(app.speed)*3.6)).padStart(3,'0');let v=Math.abs(app.speed),g=v<1?'N':app.speed<0?'R':String(Math.min(6,Math.floor(v/8)+1));UI.gear.textContent=g;UI.rpm.style.width=(v<1?9:25+((v%8)/8)*72)+'%';drawMinimap()}
function frame(now){requestAnimationFrame(frame);let dt=Math.min((now-app.lastTime)/1000||.016,.05);app.lastTime=now;if(app.state==='countdown'){app.countTimer-=dt;if(app.countTimer<=0){app.countTimer+=1;app.countVal--;if(app.countVal>0){showCount(String(app.countVal));tone(460,.16)}else if(app.countVal===0){showCount('GO!');tone(900,.36,'square',.08);app.state='racing';setTimeout(()=>UI.count.classList.add('hidden'),740)}}}else if(app.state==='racing'){let steps=Math.ceil(dt/.0167);for(let i=0;i<steps;i++){app.clock+=dt/steps;physics(dt/steps);if(app.state!=='racing')break}}updateEffects(dt);updateCamera(dt);audioUpdate();updateUI();app.renderer.render(app.scene,app.camera)}
function bind(){const keyMap={ArrowUp:'gas',KeyW:'gas',ArrowDown:'brake',KeyS:'brake',ArrowLeft:'left',KeyA:'left',ArrowRight:'right',KeyD:'right',Space:'hand'};window.addEventListener('keydown',e=>{if(keyMap[e.code]){e.preventDefault();app.keys[keyMap[e.code]]=true;if(app.audio&&app.audio.c.state==='suspended')app.audio.c.resume()}if(e.repeat)return;if(e.code==='Enter'&&app.state==='menu')startRace();if(e.code==='KeyP'||e.code==='Escape')togglePause();if(e.code==='KeyR')startRace();if(e.code==='KeyC'){app.cameraMode=1-app.cameraMode;toast(app.cameraMode?'CINEMATIC CAMERA':'CHASE CAMERA')}});window.addEventListener('keyup',e=>{if(keyMap[e.code]){e.preventDefault();app.keys[keyMap[e.code]]=false}});window.addEventListener('blur',()=>{app.keys={};app.touch={};if(app.state==='racing')togglePause()});for(let b of document.querySelectorAll('[data-control]')){let name=b.dataset.control;b.addEventListener('pointerdown',e=>{e.preventDefault();b.setPointerCapture(e.pointerId);app.touch[name]=true;b.classList.add('pressed');if(app.audio&&app.audio.c.state==='suspended')app.audio.c.resume()});for(let ev of ['pointerup','pointercancel','lostpointercapture'])b.addEventListener(ev,()=>{app.touch[name]=false;b.classList.remove('pressed')})}$('#startBtn').onclick=startRace;$('#resumeBtn').onclick=togglePause;$('#pauseRestartBtn').onclick=startRace;$('#finishRestartBtn').onclick=startRace;window.addEventListener('resize',()=>{app.camera.aspect=innerWidth/innerHeight;app.camera.updateProjectionMatrix();app.renderer.setSize(innerWidth,innerHeight);app.renderer.setPixelRatio(Math.min(devicePixelRatio,1.7))})}
makeTrack();buildWorld();buildScenery();buildCar();bind();app.camera.position.set(app.carX,app.car.position.y+6,app.carZ-14);app.lastTime=performance.now();requestAnimationFrame(frame);
})();
</script></body></html>
<!-- agent-meta {"model":"openai/gpt-6-sol","provider":"openrouter","persona":"full","sessionId":"c2762570-ab00-40c7-b88d-85a57e5b31f9","tokensIn":1640962,"tokensOut":22283,"tokensTotal":1663245,"cost":0.6700551000000001,"turns":47,"toolCalls":46,"failedToolCalls":0,"timestamp":"2026-09-23T14:47:32.566Z"} -->