Rally
GPT 6.1 Sol · full

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 29,129 bytes · SHA-256 7fa8fce4a133
<!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>Wildline — Rally Club</title><style>
*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#162a2e;color:#fff;font-family:Arial,Helvetica,sans-serif}button{font:inherit;cursor:pointer}#scene{position:absolute;inset:0;width:100%;height:100%;display:block}.ui{position:absolute;pointer-events:none;inset:0}.top{position:absolute;top:30px;left:36px;right:36px;display:flex;justify-content:space-between;align-items:flex-start}.brand{font-size:25px;font-weight:900;letter-spacing:5px;font-style:italic}.brand b{color:#d9ff62}.sub{font-size:10px;letter-spacing:3px;color:#b8c9c7;margin-top:8px}.session{display:flex;gap:28px;background:#142729b5;border:1px solid #ffffff22;border-radius:8px;padding:16px 24px;backdrop-filter:blur(12px)}.label{font-size:9px;letter-spacing:2px;color:#bfd1cc;margin-bottom:6px}.value{font-size:23px;font-weight:700;font-variant-numeric:tabular-nums}.value em{font-style:normal;font-size:13px;color:#a9bdb8}.bottom{position:absolute;bottom:30px;left:36px;right:36px;display:flex;align-items:flex-end;justify-content:space-between}.mapbox{background:#142729b5;border:1px solid #ffffff22;padding:15px;border-radius:8px;width:205px;backdrop-filter:blur(8px)}#map{width:173px;height:138px;display:block}.maptitle{font-size:10px;letter-spacing:2px;display:flex;justify-content:space-between;color:#c2d3ca}.maptitle b{color:#ddff72}.speedbox{text-align:right;min-width:205px}.speedline{display:flex;align-items:baseline;justify-content:flex-end;gap:12px}.speed{font-size:82px;font-weight:800;line-height:.9;letter-spacing:-5px;font-variant-numeric:tabular-nums;text-shadow:0 2px 15px #14272999}.unit{font-size:11px;letter-spacing:2px;color:#d5e0d4}.gear{border:1px solid #d9ff6266;color:#dcff65;border-radius:5px;padding:8px 12px;font-size:24px;font-weight:700;margin-right:6px}.rpm{height:5px;background:#ffffff2a;margin-top:16px;border-radius:3px;overflow:hidden}.rpm i{display:block;height:100%;background:#dcff65;width:15%}.surface{font-size:10px;letter-spacing:2px;color:#e3eddb;margin-top:10px}.controls{position:absolute;bottom:34px;left:50%;transform:translateX(-50%);font-size:10px;color:#d1dfd5;letter-spacing:1px;background:#14272999;padding:12px 16px;border-radius:5px;white-space:nowrap}.controls b{color:white}.controls span{margin:0 9px;color:#8aa497}.menu-btn{pointer-events:auto;background:#142729aa;border:1px solid #ffffff44;color:white;border-radius:6px;width:40px;height:40px;font-size:18px}.righttop{display:flex;gap:16px;align-items:center}#toast{position:absolute;top:130px;left:50%;transform:translateX(-50%);font-size:12px;letter-spacing:2px;color:#e0ff84;background:#142729aa;border-radius:5px;padding:10px 18px;opacity:0;transition:opacity .2s;text-align:center}.overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#0b202448;backdrop-filter:blur(5px);pointer-events:auto}.overlay.hidden{display:none}.panel{width:440px;max-width:calc(100% - 32px);padding:36px;background:#14282bee;border:1px solid #e6ffac33;border-radius:12px;box-shadow:0 20px 80px #001c2c66}.eyebrow{color:#d9ff62;font-size:10px;letter-spacing:3px;margin-bottom:15px}.panel h1{font-size:46px;line-height:1.02;letter-spacing:-2px;margin:0 0 18px;font-weight:900}.panel p{color:#bdcec9;font-size:13px;line-height:1.7;margin-bottom:25px}.course{display:flex;justify-content:space-between;border-top:1px solid #ffffff22;border-bottom:1px solid #ffffff22;padding:17px 0;margin:22px 0;font-size:12px;color:#cedbd4}.course b{display:block;color:white;font-size:17px;margin-top:6px}.primary,.secondary{width:100%;border:0;padding:16px;border-radius:5px;font-size:12px;font-weight:bold;letter-spacing:2px}.primary{background:#d9ff62;color:#1b3027}.primary:hover{background:#e9ffac}.secondary{background:#ffffff10;color:#dbe5df;margin-top:10px}#countdown{position:absolute;left:50%;top:43%;transform:translate(-50%,-50%);font-size:120px;font-weight:900;font-style:italic;color:#e6ff80;text-shadow:0 5px 30px #172d3088;letter-spacing:-7px}#laplist{font-size:12px;color:#bccfc5;line-height:2;margin-bottom:20px;white-space:pre-line}.touch{display:none;position:absolute;left:18px;right:18px;bottom:155px;justify-content:space-between;pointer-events:none}.touch div{display:flex;gap:8px;align-items:flex-end}.touch button{pointer-events:auto;touch-action:none;border:1px solid #ffffff50;background:#183339ad;color:#fff;border-radius:12px;width:60px;height:60px;font-size:22px;user-select:none}.touch .gas{height:85px;background:#d9ff6233;color:#e3ff93}.touch .small{font-size:10px;letter-spacing:1px}@media(pointer:coarse){.touch{display:flex}.controls{display:none}}@media(max-width:650px){.top{top:18px;left:18px;right:18px}.brand{font-size:18px;letter-spacing:3px}.sub{font-size:8px;letter-spacing:1px}.session{gap:14px;padding:12px}.session .beststat{display:none}.value{font-size:18px}.righttop{gap:8px}.label{font-size:8px;letter-spacing:1px}.bottom{bottom:18px;left:18px;right:18px}.mapbox{width:145px;padding:10px}#map{width:123px;height:100px}.speed{font-size:60px}.speedbox{min-width:140px}.gear{font-size:18px}.maptitle{font-size:8px;letter-spacing:1px}.controls{display:none}.panel{padding:28px}.panel h1{font-size:40px}#toast{top:105px;font-size:10px;width:max-content;max-width:90%}}
</style></head><body>
<canvas id="scene"></canvas><div class="ui"><div class="top"><div><div class="brand">WILD<b>LINE</b></div><div class="sub">RALLY CLUB / ALPINE RUN</div></div><div class="righttop"><div class="session"><div><div class="label">LAP</div><div class="value" id="lap">01 <em>/ 03</em></div></div><div><div class="label">RACE TIME</div><div class="value" id="timer">00:00.00</div></div><div class="beststat"><div class="label">BEST LAP</div><div class="value" id="best">—</div></div></div><button class="menu-btn" id="pauseBtn" aria-label="Pause">Ⅱ</button></div></div><div id="toast"></div><div id="countdown"></div><div class="bottom"><div class="mapbox"><div class="maptitle"><span>ALPINE LOOP</span><b>3 LAPS</b></div><canvas id="map" width="346" height="276"></canvas><div class="maptitle"><span id="progress">SECTOR 1 / 12</span><span>● LIVE</span></div></div><div class="speedbox"><div class="speedline"><span class="gear" id="gear">N</span><span class="speed" id="speed">0</span><span class="unit">KM/H</span></div><div class="rpm"><i id="rpm"></i></div><div class="surface" id="surface">DIRT • READY TO RALLY</div></div></div><div class="controls"><b>WASD / ↑↓←→</b> DRIVE<span>·</span><b>SPACE</b> DRIFT<span>·</span><b>C</b> CAMERA<span>·</span><b>P</b> PAUSE<span>·</span><b>R</b> RESTART</div><div class="touch"><div><button data-key="ArrowLeft">◀</button><button data-key="ArrowRight">▶</button></div><div><button class="small" data-key="Space">DRIFT</button><button data-key="ArrowDown">−</button><button class="gas" data-key="ArrowUp">+</button></div></div></div>
<div id="menu" class="overlay"><div class="panel"><div class="eyebrow" id="menuTag">STAGE 01 / THE HIGHLANDS</div><h1 id="menuTitle">Find your<br>wild line.</h1><p id="menuText">A mountain loop. Three laps. Just you and the clock.<br>Keep it on the dirt, lean into the corners, and let it slide.</p><div class="course" id="courseInfo"><span>TERRAIN<b>Mixed gravel</b></span><span>DISTANCE<b id="distance">—</b></span><span>CONDITIONS<b>Clear skies</b></span></div><div id="laplist"></div><button class="primary" id="mainBtn">START YOUR ENGINE →</button><button class="secondary" id="restartBtn" style="display:none">RESTART RACE</button><div id="loadStatus" style="font-size:10px;color:#a7bdb4;margin-top:16px;text-align:center;letter-spacing:1px">WASD OR ARROW KEYS TO DRIVE · SPACE TO DRIFT</div></div></div>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.1/build/three.min.js"></script>
<script>
(()=>{'use strict';
// One module owns the renderer, race state, input and procedural assets.
const $=id=>document.getElementById(id),keys={},clamp=(v,a,b)=>Math.max(a,Math.min(b,v));let T=window.THREE;
if(!T){$('loadStatus').textContent='Connecting to the 3D engine… internet access is required.';return;}
const scene=new T.Scene();scene.background=new T.Color('#91bdc2');scene.fog=new T.FogExp2('#91bdc2',.0028);
const renderer=new T.WebGLRenderer({canvas:$('scene'),antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,1.7));renderer.shadowMap.enabled=true;renderer.shadowMap.type=T.PCFSoftShadowMap;renderer.outputColorSpace=T.SRGBColorSpace;renderer.setClearColor('#91bdc2');
const camera=new T.PerspectiveCamera(58,innerWidth/innerHeight,.2,1700);scene.add(new T.HemisphereLight('#e2f5ff','#75824a',2));const sun=new T.DirectionalLight('#fff4ce',2.3);sun.position.set(100,180,-100);sun.castShadow=true;sun.shadow.mapSize.set(2048,2048);Object.assign(sun.shadow.camera,{left:-100,right:100,top:100,bottom:-100,near:1,far:450});sun.shadow.bias=-.001;scene.add(sun);scene.add(sun.target);
const mat=(color,extra={})=>new T.MeshStandardMaterial({color,roughness:.88,...extra}),M={road:mat('#a9916d'),shoulder:mat('#baa57e'),grass:mat('#718859'),bark:mat('#645743'),pine:mat('#355e4d'),pine2:mat('#47755a'),rock:mat('#87917b'),red:mat('#f45b37'),dark:mat('#202c30'),glass:mat('#273f48',{metalness:.3,roughness:.2}),white:mat('#fff3ce'),metal:mat('#9eada8',{metalness:.5}),mud:mat('#655b3f'),water:mat('#82a5a3',{metalness:.4,roughness:.2})};
function mesh(geo,material,parent=scene){const m=new T.Mesh(geo,material);parent.add(m);return m}function box(w,h,d,material,x=0,y=0,z=0,parent=scene){const m=mesh(new T.BoxGeometry(w,h,d),material,parent);m.position.set(x,y,z);m.castShadow=true;m.receiveShadow=true;return m}
// Closed spline with real elevation; sampled road ribbons also drive physics gates.
const controls=[[-180,5,-100],[-85,9,-205],[65,16,-220],[190,28,-155],[225,40,-40],[140,31,55],[205,16,165],[75,9,235],[-60,19,195],[-150,27,110],[-245,16,80],[-235,7,-20]];
const curve=new T.CatmullRomCurve3(controls.map(p=>new T.Vector3(...p)),true,'catmullrom',.45),N=720,samples=[],tangents=[],normals=[];let length=0;
for(let i=0;i<N;i++){const p=curve.getPointAt(i/N),t=curve.getTangentAt(i/N);samples.push(p);tangents.push(t);normals.push(new T.Vector3(t.z,0,-t.x).normalize());if(i)length+=p.distanceTo(samples[i-1]);}length+=samples[0].distanceTo(samples[N-1]);$('distance').textContent=(length/1000).toFixed(2)+' km / lap';
function ribbon(inner,outer,material){const verts=[],indices=[];for(let i=0;i<=N;i++){const j=i%N,p=samples[j],n=normals[j];for(const w of [inner,outer])verts.push(p.x+n.x*w,p.y+(material===M.road?.025:0),p.z+n.z*w);if(i<N){let k=i*2;indices.push(k,k+2,k+1,k+1,k+2,k+3);}}const g=new T.BufferGeometry();g.setAttribute('position',new T.Float32BufferAttribute(verts,3));g.setIndex(indices);g.computeVertexNormals();const m=mesh(g,material);m.material.side=T.DoubleSide;m.receiveShadow=true;return m;}
ribbon(-9,9,M.road);ribbon(-12,-9,M.shoulder);ribbon(9,12,M.shoulder);ribbon(-43,-12,M.grass);ribbon(12,43,M.grass);
const ground=mesh(new T.PlaneGeometry(5000,5000),M.grass);ground.rotation.x=-Math.PI/2;ground.position.y=-1;ground.receiveShadow=true;
let seed=73;function rand(){seed=(seed*16807)%2147483647;return(seed-1)/2147483646;}
// Low-poly distant mountains, clouds, trees, rocks and spectator camps.
for(let i=0;i<32;i++){const a=i/32*Math.PI*2,r=750+rand()*350,h=100+rand()*170;const m=mesh(new T.ConeGeometry(120+rand()*160,h,5),mat(i%2?'#829c8b':'#738e81'));m.position.set(Math.cos(a)*r,h/2-25,Math.sin(a)*r);m.rotation.y=rand()*6;}
for(let i=0;i<18;i++){let a=rand()*6.28;const cloud=new T.Group();scene.add(cloud);cloud.position.set(Math.cos(a)*900,190+rand()*130,Math.sin(a)*900);for(let j=0;j<4;j++){const m=mesh(new T.IcosahedronGeometry(20+rand()*20,1),mat('#dde8df'),cloud);m.position.set(j*25,rand()*15,rand()*12);m.scale.y=.45;}}
const obstacles=[];const treeGeo=new T.ConeGeometry(4.5,13,6),trunkGeo=new T.CylinderGeometry(.6,.9,7,5),rockGeo=new T.DodecahedronGeometry(1,0);
for(let i=0;i<500;i++){const idx=Math.floor(rand()*N),p=samples[idx],n=normals[idx],dist=(15+rand()*85)*(rand()>.5?1:-1),x=p.x+n.x*dist,z=p.z+n.z*dist;const y=p.y-Math.max(0,Math.abs(dist)-35)*.10; if(i%5===0){const r=1.2+rand()*2;const m=mesh(rockGeo,M.rock);m.position.set(x,y+.5,z);m.scale.set(r,r*.75,r);m.rotation.set(rand(),rand(),rand());m.castShadow=true;if(Math.abs(dist)<26)obstacles.push({x,z,r:r+1});}else{const scale=.65+rand()*.9,tr=mesh(trunkGeo,M.bark);tr.position.set(x,y+2*scale,z);tr.scale.setScalar(scale);const leaf=mesh(treeGeo,i%2?M.pine:M.pine2);leaf.position.set(x,y+9*scale,z);leaf.scale.setScalar(scale);leaf.castShadow=true;const top=mesh(treeGeo,M.pine);top.position.set(x,y+14*scale,z);top.scale.setScalar(scale*.7);if(Math.abs(dist)<26)obstacles.push({x,z,r:1.8});}}
function textSign(text,bg='#183f37',fg='#e5ff9c'){const c=document.createElement('canvas');c.width=512;c.height=128;const ctx=c.getContext('2d');ctx.fillStyle=bg;ctx.fillRect(0,0,512,128);ctx.fillStyle=fg;ctx.font='bold 54px Arial';ctx.textAlign='center';ctx.fillText(text,256,84);return new T.CanvasTexture(c);}
for(let i=0;i<12;i++){const j=Math.floor(i*N/12),p=samples[j],n=normals[j];for(let s of [-1,1]){const post=box(.3,2.4,.3,M.white,p.x+n.x*10.5*s,p.y+1.2,p.z+n.z*10.5*s);box(.4,.4,.4,M.red,post.position.x,p.y+2.2,post.position.z);}if(i%2===0){const sign=box(8,2.4,.25,mat('#ffffff',{map:textSign(i===0?'WILDLINE':'› RALLY CLUB ›')}));sign.position.copy(p).addScaledVector(n,16);sign.position.y+=4;sign.rotation.y=Math.atan2(n.x,n.z);box(.3,4,.3,M.bark,sign.position.x,p.y+2,sign.position.z);}}
const start=samples[0],startYaw=Math.atan2(tangents[0].x,tangents[0].z);const gate=new T.Group();scene.add(gate);gate.position.copy(start);gate.rotation.y=startYaw;box(.6,8,.6,M.dark,-11,4,0,gate);box(.6,8,.6,M.dark,11,4,0,gate);box(22.8,2.2,.65,mat('#ffffff',{map:textSign('WILDLINE / START')}),0,8,0,gate);
for(let i=0;i<12;i++)for(let j=0;j<2;j++)box(1.5,.035,1.2,(i+j)%2?M.white:M.dark,-8.25+i*1.5,.06,-1.2+j*1.2,gate);
for(let i=0;i<28;i++){const idx=(i*3+8)%N,p=samples[idx],n=normals[idx];const person=new T.Group();scene.add(person);person.position.copy(p).addScaledVector(n,14+rand()*8);box(.6,1.1,.5,mat(['#e47743','#ddd594','#577796','#d0d8c6'][i%4]),0,1.3,0,person);mesh(new T.IcosahedronGeometry(.3,0),M.white,person).position.y=2.1;box(.17,.8,.2,M.dark,-.2,.45,0,person);box(.17,.8,.2,M.dark,.2,.45,0,person);}
// Slow wet patch, set into the shoulder of one bend.
const mudIndex=490,mudCenter=samples[mudIndex].clone().addScaledVector(normals[mudIndex],12);const mud=mesh(new T.CircleGeometry(10,24),M.mud);mud.rotation.x=-Math.PI/2;mud.position.copy(mudCenter);mud.position.y+=.06;const puddle=mesh(new T.CircleGeometry(5,20),M.water);puddle.rotation.x=-Math.PI/2;puddle.position.copy(mudCenter);puddle.position.y+=.08;
// Rally car: local +Z is forward. Suspension lean stays separate from world heading.
const car=new T.Group(),body=new T.Group();scene.add(car);car.add(body);box(2.1,.6,4.1,M.red,0,.8,0,body);box(1.95,.3,1.3,M.red,0,1.07,1.2,body);box(1.72,.75,1.9,M.glass,0,1.4,-.25,body);box(1.85,.12,1.75,M.red,0,1.82,-.3,body);box(.3,.04,3.9,M.white,-.45,1.12,0,body);box(.3,.04,3.9,M.white,.45,1.12,0,body);box(2.2,.18,.55,M.dark,0,1.6,-1.78,body);box(.12,.5,.16,M.dark,-.8,1.3,-1.75,body);box(.12,.5,.16,M.dark,.8,1.3,-1.75,body);box(2,.23,.2,M.dark,0,.65,2.07,body);box(1,.22,.08,M.dark,0,.9,2.09,body);for(const s of [-1,1]){box(.5,.23,.09,mat('#fff5c2',{emissive:'#fff2ba',emissiveIntensity:.7}),s*.73,1,2.08,body);box(.55,.22,.09,mat('#ff341b',{emissive:'#fa2310',emissiveIntensity:.9}),s*.7,1,-2.08,body);box(.3,.13,.45,M.dark,s*1.06,1.4,.3,body);}
const wheels=[];for(const x of [-1.12,1.12])for(const z of [-1.25,1.3]){const pivot=new T.Group();body.add(pivot);pivot.position.set(x,.48,z);const tire=mesh(new T.CylinderGeometry(.47,.47,.32,12),M.dark,pivot);tire.rotation.z=Math.PI/2;const hub=mesh(new T.CylinderGeometry(.26,.26,.34,8),M.metal,pivot);hub.rotation.z=Math.PI/2;wheels.push({pivot,tire,hub,front:z>0});}
// Reusable particle and skid pools: no per-frame geometry allocations.
const particles=[],particleGeo=new T.IcosahedronGeometry(1,0);for(let i=0;i<100;i++){const m=mesh(particleGeo,mat('#c6b693',{transparent:true,opacity:0,depthWrite:false}));m.visible=false;particles.push({m,life:0,v:new T.Vector3(),max:1});}let particleCursor=0;
const skids=[];for(let i=0;i<220;i++){const m=mesh(new T.PlaneGeometry(.27,1.4),mat('#4c4537',{transparent:true,opacity:.4,depthWrite:false}));m.rotation.x=-Math.PI/2;m.visible=false;skids.push(m);}let skidCursor=0,fxClock=0;
function emit(spark=false){const p=particles[particleCursor++%particles.length],side=rand()>.5?1:-1;p.m.position.copy(car.position);p.m.position.x+=Math.cos(state.heading)*side;p.m.position.z-=Math.sin(state.heading)*side;p.m.position.y+=.4;p.life=p.max=spark?.35:.8+rand()*.6;p.v.set((rand()-.5)*5,spark?3+rand()*4:1+rand()*2,(rand()-.5)*5);p.m.material.color.set(spark?'#ffce69':'#c6b693');p.m.scale.setScalar(spark?.12:.4);p.m.visible=true;}
// Synthesized engine, filtered noise for gravel, and short one-shot tones.
const audio={ctx:null,engine:null,gain:null,noiseGain:null};function initAudio(){if(audio.ctx){audio.ctx.resume();return;}try{const c=audio.ctx=new(window.AudioContext||window.webkitAudioContext)();const osc=c.createOscillator(),gain=c.createGain(),filter=c.createBiquadFilter();osc.type='sawtooth';filter.type='lowpass';filter.frequency.value=600;gain.gain.value=0;osc.connect(filter).connect(gain).connect(c.destination);osc.start();audio.engine=osc;audio.gain=gain;const 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;const noise=c.createBufferSource();noise.buffer=buffer;noise.loop=true;const nf=c.createBiquadFilter();nf.type='lowpass';nf.frequency.value=1100;const ng=audio.noiseGain=c.createGain();ng.gain.value=0;noise.connect(nf).connect(ng).connect(c.destination);noise.start();}catch(e){}}
function beep(freq=500,dur=.15,vol=.12,type='sine'){if(!audio.ctx)return;const c=audio.ctx,o=c.createOscillator(),g=c.createGain();o.type=type;o.frequency.setValueAtTime(freq,c.currentTime);o.frequency.exponentialRampToValueAtTime(Math.max(40,freq*.6),c.currentTime+dur);g.gain.setValueAtTime(vol,c.currentTime);g.gain.exponentialRampToValueAtTime(.001,c.currentTime+dur);o.connect(g).connect(c.destination);o.start();o.stop(c.currentTime+dur);}
const state={mode:'ready',x:0,z:0,y:0,heading:0,vx:0,vz:0,speed:0,time:0,lapStart:0,lap:1,laps:[],best:Infinity,checkpoint:1,nearest:0,surface:'DIRT',count:3.0,lastBeep:4,camHigh:false,collision:0,toast:0,steer:0};
function reset(){Object.assign(state,{mode:'countdown',x:start.x,z:start.z,y:start.y,heading:startYaw,vx:0,vz:0,speed:0,time:0,lapStart:0,lap:1,laps:[],best:Infinity,checkpoint:1,nearest:0,surface:'DIRT',count:3.0,lastBeep:4,collision:0});for(const s of skids)s.visible=false;for(const p of particles){p.life=0;p.m.visible=false;}$('menu').classList.add('hidden');$('countdown').textContent='3';camera.position.set(start.x-Math.sin(startYaw)*13,start.y+7,start.z-Math.cos(startYaw)*13);initAudio();}
function format(t){return Math.floor(t/60).toString().padStart(2,'0')+':'+(t%60).toFixed(2).padStart(5,'0');}
function toast(s){$('toast').textContent=s;state.toast=2.8;$('toast').style.opacity=1;}
function showMenu(type){$('menu').classList.remove('hidden');$('courseInfo').style.display='none';$('restartBtn').style.display=type==='paused'?'block':'none';$('loadStatus').textContent='WASD / ARROWS · SPACE TO DRIFT · R TO RESTART';$('menuTag').textContent=type==='paused'?'TAKE A BREATHER':'STAGE COMPLETE / ALPINE RUN';$('menuTitle').innerHTML=type==='paused'?'Race paused.':'That’s a<br>wild finish.';$('menuText').textContent=type==='paused'?'Your next corner is waiting. Get back out there.':'Final time '+format(state.time)+' · Best lap '+format(state.best);$('mainBtn').textContent=type==='paused'?'RESUME RACE →':'RACE AGAIN →';$('laplist').textContent=type==='finished'?state.laps.map((t,i)=>'LAP '+(i+1)+' '+format(t)).join('\n'):'';}
function pause(){if(state.mode==='running'||state.mode==='countdown'){state.previous=state.mode;state.mode='paused';showMenu('paused');}else if(state.mode==='paused'){state.mode=state.previous;$('menu').classList.add('hidden');initAudio();}}
$('mainBtn').onclick=()=>state.mode==='paused'?pause():reset();$('restartBtn').onclick=reset;$('pauseBtn').onclick=pause;
window.addEventListener('keydown',e=>{if(['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code))e.preventDefault();keys[e.code]=true;if(!e.repeat){if(e.code==='KeyR')reset();if(e.code==='KeyP'||e.code==='Escape')pause();if(e.code==='KeyC')state.camHigh=!state.camHigh;}});window.addEventListener('keyup',e=>keys[e.code]=false);window.addEventListener('blur',()=>{for(const k in keys)keys[k]=false;if(state.mode==='running'||state.mode==='countdown')pause();});
document.querySelectorAll('[data-key]').forEach(b=>{b.addEventListener('pointerdown',e=>{e.preventDefault();b.setPointerCapture(e.pointerId);keys[b.dataset.key]=true;initAudio();});const up=()=>keys[b.dataset.key]=false;b.addEventListener('pointerup',up);b.addEventListener('pointercancel',up);});
function nearest(x,z){let best=Infinity,index=0;for(let i=0;i<N;i++){const p=samples[i],d=(x-p.x)**2+(z-p.z)**2;if(d<best){best=d;index=i;}}return {index,d:Math.sqrt(best)};}
function finishLap(){const t=state.time-state.lapStart;state.laps.push(t);state.best=Math.min(state.best,t);state.lapStart=state.time;if(state.lap===3){state.mode='finished';showMenu('finished');beep(880,.5,.2);setTimeout(()=>beep(1320,.7,.15),220);}else{state.lap++;toast('LAP '+state.lap+' / 3 · '+format(t));beep(800,.25,.1);}state.checkpoint=1;}
function physics(dt){const s=state;if(s.mode==='countdown'){s.count-=dt;const v=Math.ceil(s.count);if(v!==s.lastBeep&&v>0){s.lastBeep=v;beep(420,.13,.14);} $('countdown').textContent=v>0?v:'GO!';if(s.count<=0){s.mode='running';beep(850,.25,.16);s.toast=1;}$('surface').textContent='DIRT • GET READY';return;}if(s.mode!=='running')return;
s.time+=dt;if(s.count>-1){s.count-=dt;if(s.count<=-1)$('countdown').textContent='';}s.collision=Math.max(0,s.collision-dt);
const near=nearest(s.x,s.z);s.nearest=near.index;const p=samples[near.index],n=normals[near.index],lateral=(s.x-p.x)*n.x+(s.z-p.z)*n.z;const wet=Math.hypot(s.x-mudCenter.x,s.z-mudCenter.z)<10;const off=near.d>10;s.surface=wet?'MUD / WATER':off?'GRASS / GRAVEL':'DIRT';const gas=keys.ArrowUp||keys.KeyW,brake=keys.ArrowDown||keys.KeyS,hand=!!keys.Space,input=(keys.ArrowRight||keys.KeyD?1:0)-(keys.ArrowLeft||keys.KeyA?1:0);s.steer+=(input-s.steer)*Math.min(1,dt*9);
const forward=s.vx*Math.sin(s.heading)+s.vz*Math.cos(s.heading),speed=Math.hypot(s.vx,s.vz),max=wet?10:off?22:55;let acceleration=gas?19:0;if(brake)acceleration=forward>1?-36:-10;if(hand)acceleration-=forward>0?18:-9;acceleration-=forward*(off?1.05:.17);if(forward>max)acceleration-=(forward-max)*3;if(forward<-10)acceleration+=(-10-forward)*3;
const turn=s.steer*(1.3/(1+speed*.026))*(clamp(forward/8,-1,1))*(hand?1.6:1);s.heading+=turn*dt;const fx=Math.sin(s.heading),fz=Math.cos(s.heading),side=s.vx*fz-s.vz*fx;const grip=wet?1.4:off?2.0:hand?1.0:4.8;const newSide=side*Math.exp(-grip*dt),newForward=forward+acceleration*dt;s.vx=fx*newForward+fz*newSide;s.vz=fz*newForward-fx*newSide;s.x+=s.vx*dt;s.z+=s.vz*dt;s.speed=newForward;s.y+=(p.y-s.y)*Math.min(1,dt*14);
// Soft outer boundary and solid scenery both bounce and dissipate energy.
let crash=false;if(near.d>35){const tx=(s.x-p.x)/near.d,tz=(s.z-p.z)/near.d;s.x=p.x+tx*34.8;s.z=p.z+tz*34.8;const dot=s.vx*tx+s.vz*tz;if(dot>0){s.vx-=dot*1.5*tx;s.vz-=dot*1.5*tz;crash=true;}}
for(const o of obstacles){const d=Math.hypot(s.x-o.x,s.z-o.z);if(d<o.r+1){const nx=(s.x-o.x)/(d||1),nz=(s.z-o.z)/(d||1);s.x=o.x+nx*(o.r+1.05);s.z=o.z+nz*(o.r+1.05);const dot=s.vx*nx+s.vz*nz;if(dot<0){s.vx=(s.vx-1.5*dot*nx)*.5;s.vz=(s.vz-1.5*dot*nz)*.5;crash=true;}}}
if(crash&&s.collision===0){s.collision=.7;beep(90,.2,.24,'triangle');for(let i=0;i<12;i++)emit(true);toast('CONTACT! · FIND YOUR LINE');}
// Gates must be visited in order; crossing the start alone cannot award a lap.
const gateIndex=Math.floor((s.checkpoint%12)*N/12),gp=samples[gateIndex];if(Math.hypot(s.x-gp.x,s.z-gp.z)<15&&near.d<12){s.checkpoint++;if(s.checkpoint===13)finishLap();}
fxClock+=dt;if(fxClock>.045){fxClock=0;if(speed>5&&(off||Math.abs(side)>2||hand)){emit();emit();}if(speed>8&&(Math.abs(side)>2||hand)){for(const sign of [-1,1]){const m=skids[skidCursor++%skids.length];m.visible=true;m.position.set(s.x+fz*sign,s.y+.055,s.z-fx*sign);m.rotation.set(-Math.PI/2,0,-s.heading);}}}
}
function updateEffects(dt){for(const p of particles){if(p.life<=0)continue;p.life-=dt;p.m.visible=p.life>0;p.m.position.addScaledVector(p.v,dt);p.v.y-=dt*1.8;p.m.material.opacity=Math.max(0,p.life/p.max)*.4;p.m.scale.multiplyScalar(1+dt*1.1);}}
const look=new T.Vector3(),targetCam=new T.Vector3();function cameraUpdate(dt){const s=state;car.position.set(s.x,s.y+.06,s.z);car.rotation.y=s.heading;body.rotation.z+=(s.steer*Math.min(Math.abs(s.speed)/50,.12)-body.rotation.z)*Math.min(1,dt*7);body.rotation.x=Math.sin(s.time*17)*Math.min(Math.abs(s.speed)/1200,.025);for(const w of wheels){if(w.front)w.pivot.rotation.y=s.steer*.3;w.tire.rotation.x+=s.speed*dt*2;w.hub.rotation.x+=s.speed*dt*2;}const high=s.camHigh,back=high?21:11+Math.abs(s.speed)*.055;targetCam.set(s.x-Math.sin(s.heading)*back,s.y+(high?15:6.1),s.z-Math.cos(s.heading)*back);camera.position.lerp(targetCam,1-Math.exp(-dt*5));look.set(s.x+Math.sin(s.heading)*9,s.y+1.5,s.z+Math.cos(s.heading)*9);camera.lookAt(look);sun.position.set(s.x+70,s.y+150,s.z-80);sun.target.position.copy(car.position);}
const ctx=$('map').getContext('2d'),bounds={minX:Math.min(...samples.map(p=>p.x)),maxX:Math.max(...samples.map(p=>p.x)),minZ:Math.min(...samples.map(p=>p.z)),maxZ:Math.max(...samples.map(p=>p.z))};function mapPoint(x,z){return [25+(x-bounds.minX)/(bounds.maxX-bounds.minX)*296,18+(z-bounds.minZ)/(bounds.maxZ-bounds.minZ)*240];}
function hud(){const s=state;$('speed').textContent=Math.round(Math.abs(s.speed)*3.6);const gear=s.speed<-.5?'R':Math.abs(s.speed)<1?'N':Math.min(5,1+Math.floor(Math.abs(s.speed)/11));$('gear').textContent=gear;$('rpm').style.width=(18+(Math.abs(s.speed)%11)/11*78)+'%';$('timer').textContent=format(s.time);$('best').textContent=Number.isFinite(s.best)?format(s.best):'—';$('lap').innerHTML=String(s.lap).padStart(2,'0')+' <em>/ 03</em>';$('progress').textContent='SECTOR '+Math.min(s.checkpoint,12)+' / 12';if(s.mode==='running')$('surface').textContent=s.surface+(keys.Space?' • HANDBRAKE':' • ALPINE RUN');ctx.clearRect(0,0,346,276);ctx.beginPath();samples.forEach((p,i)=>{const a=mapPoint(p.x,p.z);i?ctx.lineTo(...a):ctx.moveTo(...a);});ctx.closePath();ctx.strokeStyle='#9ab3a566';ctx.lineWidth=12;ctx.stroke();ctx.strokeStyle='#d7dfbb';ctx.lineWidth=3;ctx.stroke();for(let i=0;i<12;i++){const a=mapPoint(samples[i*60].x,samples[i*60].z);ctx.fillStyle=i<s.checkpoint?'#d9ff62':'#688378';ctx.beginPath();ctx.arc(...a,3,0,6.28);ctx.fill();}const a=mapPoint(s.x,s.z);ctx.save();ctx.translate(...a);ctx.rotate(-s.heading);ctx.fillStyle='#d9ff62';ctx.shadowColor='#d9ff62';ctx.shadowBlur=10;ctx.beginPath();ctx.moveTo(0,8);ctx.lineTo(-6,-6);ctx.lineTo(6,-6);ctx.closePath();ctx.fill();ctx.restore();}
function audioUpdate(){if(!audio.ctx)return;const s=state,c=audio.ctx,active=s.mode==='running'||s.mode==='countdown',v=Math.abs(s.speed);audio.engine.frequency.setTargetAtTime(45+(v%11)*9+v*.9,c.currentTime,.08);audio.gain.gain.setTargetAtTime(active?.035+v*.001:0,c.currentTime,.1);audio.noiseGain.gain.setTargetAtTime(s.mode==='running'&&v>5&&(s.surface!=='DIRT'||keys.Space||Math.abs(s.steer)>.5&&v>25)?.06:0,c.currentTime,.12);}
function resize(){renderer.setSize(innerWidth,innerHeight);camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();}window.addEventListener('resize',resize);resize();Object.assign(state,{x:start.x,z:start.z,y:start.y,heading:startYaw});camera.position.set(start.x-Math.sin(startYaw)*16,start.y+9,start.z-Math.cos(startYaw)*16);cameraUpdate(.1);hud();let previous=performance.now();function frame(now){requestAnimationFrame(frame);let dt=Math.min((now-previous)/1000,.05);previous=now;physics(dt);if(state.mode!=='paused'){updateEffects(dt);cameraUpdate(dt);}if(state.toast>0){state.toast-=dt;if(state.toast<=0)$('toast').style.opacity=0;}audioUpdate();hud();renderer.render(scene,camera);}requestAnimationFrame(frame);
})();
</script></body></html>
<!-- agent-meta {"model":"openai/gpt-6.1-sol","provider":"openrouter","persona":"full","sessionId":"aea99e3f-d0ed-497b-bbaa-8b6478b165f9","tokensIn":172606,"tokensOut":11498,"tokensTotal":184104,"cost":0.17240490000000003,"turns":11,"toolCalls":10,"failedToolCalls":0,"timestamp":"2026-09-29T19:56:35.207Z"} -->