Canyon Run
Qwen 3.6 27b MTP · 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 33,885 bytes · SHA-256 7fa750fc6d64
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CANYON RACER</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{overflow:hidden;background:#0a0015;font-family:'Courier New',monospace;user-select:none}
canvas{display:block}
#hud{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:10}
.bar-wrap{position:absolute;height:4px;background:rgba(255,255,255,.08);border-radius:2px;overflow:hidden}
.bar-fill{height:100%;border-radius:2px;transition:width .1s}
.bar-label{color:rgba(255,255,255,.35);font-size:9px;letter-spacing:4px;text-transform:uppercase;margin-bottom:6px}
#speed-wrap{bottom:30px;right:40px;width:180px}
#speed-fill{background:linear-gradient(90deg,#ff6a00,#ee0979)}
#energy-wrap{bottom:30px;left:40px;width:180px}
#energy-fill{background:linear-gradient(90deg,#00d4ff,#7b2ff7)}
#turbo-wrap{bottom:65px;left:40px;right:40px;width:auto}
#turbo-fill{background:linear-gradient(90deg,#ff6a00,#ff0044);width:0%}
#score-display{position:absolute;top:24px;left:50%;transform:translateX(-50%);color:rgba(255,255,255,.7);font-size:16px;letter-spacing:5px;text-shadow:0 0 20px rgba(255,140,0,.3)}
#multiplier{position:absolute;top:38%;left:50%;transform:translate(-50%,-50%);font-size:72px;color:transparent;font-weight:bold;letter-spacing:-2px;transition:all .15s ease-out}
#multiplier.active{color:rgba(255,160,50,.9);text-shadow:0 0 80px rgba(255,100,0,.7)}
#start-screen,#game-over{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:100;pointer-events:all}
#start-screen{background:radial-gradient(ellipse at 50% 60%,#1a0533 0%,#0a0015 70%)}
#game-over{display:none;background:rgba(5,0,15,.92)}
.title{font-size:58px;color:#ff8c00;letter-spacing:10px;text-shadow:0 0 60px rgba(255,140,0,.5);margin-bottom:8px}
.subtitle{font-size:12px;color:rgba(255,255,255,.35);letter-spacing:7px;margin-bottom:50px}
.btn{padding:14px 48px;background:transparent;border:1px solid rgba(255,140,0,.5);color:#ff8c00;font-size:14px;letter-spacing:5px;cursor:pointer;font-family:'Courier New',monospace;transition:all .3s}
.btn:hover{background:rgba(255,140,0,.12);border-color:#ff8c00}
.controls-info{margin-top:36px;color:rgba(255,255,255,.25);font-size:11px;letter-spacing:2px;text-align:center;line-height:2.2}
.go-title{font-size:44px;color:#ff3366;letter-spacing:8px;margin-bottom:36px}
.stat-line{color:rgba(255,255,255,.5);font-size:14px;letter-spacing:3px;margin:6px 0}
.stat-val{color:#ff8c00}
#near-miss-flash,#boost-flash{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:5;opacity:0;transition:opacity .1s}
#near-miss-flash{background:radial-gradient(ellipse at center,rgba(255,140,0,.3),transparent 70%)}
#boost-flash{background:radial-gradient(ellipse at center,rgba(255,50,0,.12),transparent 60%);transition:opacity .2s}
#vignette{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:4;
background:radial-gradient(ellipse at center,transparent 50%,rgba(0,0,0,.6) 100%)}
#speed-lines{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:3;opacity:0}
</style>
</head>
<body>
<div id="vignette"></div>
<div id="start-screen">
<div class="title">CANYON RACER</div>
<div class="subtitle">PROCEDURAL HIGH-SPEED FLIGHT</div>
<button class="btn" id="start-btn">LAUNCH</button>
<div class="controls-info">WASD / ARROWS — STEER SHIFT — BOOST SPACE — BRAKE</div>
</div>
<div id="game-over">
<div class="go-title">SIGNAL LOST</div>
<div class="stat-line">SCORE: <span class="stat-val" id="final-score">0</span></div>
<div class="stat-line">MAX SPEED: <span class="stat-val" id="max-speed">0</span></div>
<div class="stat-line">DISTANCE: <span class="stat-val" id="final-distance">0</span></div>
<button class="btn" id="restart-btn" style="margin-top:36px">RELAUNCH</button>
</div>
<div id="hud">
<div id="score-display">SCORE: 0</div>
<div id="multiplier"></div>
<div id="speed-wrap">
<div class="bar-label">VELOCITY</div>
<div class="bar-wrap"><div class="bar-fill" id="speed-fill"></div></div>
</div>
<div id="energy-wrap">
<div class="bar-label">ENERGY</div>
<div class="bar-wrap"><div class="bar-fill" id="energy-fill" style="width:100%"></div></div>
</div>
<div id="turbo-wrap">
<div class="bar-wrap"><div class="bar-fill" id="turbo-fill"></div></div>
</div>
</div>
<div id="near-miss-flash"></div>
<div id="boost-flash"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
(function(){'use strict';
// ════════════════════════════════════════
// CONSTANTS
// ════════════════════════════════════════
const CHUNK_LEN = 200;
const NUM_CHUNKS = 14;
const CANYON_W = 70;
const CANYON_H = 55;
const BASE_SPEED = 1.8;
const TOP_SPEED = 3.5;
const BOOST_MULT = 2.0;
const ENERGY_DECAY = 2.5;
const NEAR_MISS_DIST = 9;
const COLLIDE_DIST = 3.0;
// ════════════════════════════════════════
// GAME STATE
// ════════════════════════════════════════
let running = false, score=0, mult=1, energy=100, turbo=0, spd=BASE_SPEED;
let maxSpd=0, distTraveled=0, difficulty=1;
// ════════════════════════════════════════
// THREE.JS CORE
// ════════════════════════════════════════
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x1a0533, 0.003);
const camera = new THREE.PerspectiveCamera(78, innerWidth/innerHeight, 0.1, 900);
camera.position.set(0,6,-14);
const renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
document.body.appendChild(renderer.domElement);
// ════════════════════════════════════════
// LIGHTING — Golden Hour / Cyber Sunset
// ════════════════════════════════════════
scene.add(new THREE.AmbientLight(0x2a1050, 0.6));
const sun = new THREE.DirectionalLight(0xff8c42, 1.8);
sun.position.set(30, 70, -80);
sun.castShadow = true;
sun.shadow.mapSize.set(2048,2048);
Object.assign(sun.shadow.camera,{near:1,far:350,left:-80,right:80,top:80,bottom:-80});
scene.add(sun);
const rim = new THREE.DirectionalLight(0x7b2ff7, 0.4);
rim.position.set(-40, 15, 60);
scene.add(rim);
// ════════════════════════════════════════
// SKY DOME (gradient shader)
// ════════════════════════════════════════
const skyGeo = new THREE.SphereGeometry(450,16,8);
const skyMat = new THREE.ShaderMaterial({
side:THREE.BackSide, depthWrite:false,
vertexShader:`varying vec3 vWP;void main(){vWP=position;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}`,
fragmentShader:`
varying vec3 vWP;
void main(){
float h=normalize(vWP).y;
vec3 c=mix(vec3(.6,.25,.08),vec3(.35,.08,.28),smoothstep(-.1,.3,h));
c=mix(c,vec3(.04,0.,.12),smoothstep(.2,.7,h));
// sun glow
vec3 sd=normalize(vec3(30.,70.,-80.));
float sun=pow(max(dot(normalize(vWP),sd),0.),64.);
c+=vec3(1.,.5,.2)*sun*2.;
gl_FragColor=vec4(c,1.);
}`
});
const sky = new THREE.Mesh(skyGeo,skyMat);
scene.add(sky);
// ════════════════════════════════════════
// PLAYER CRAFT — Sleek low-poly fighter
// ════════════════════════════════════════
let playerGrp;
function buildPlayer(){
const g = new THREE.Group();
// Fuselage
const bodyG = new THREE.ConeGeometry(.5,3.2,6);
bodyG.rotateX(Math.PI/2);
const bodyM = new THREE.MeshPhongMaterial({color:0xbbbbcc,flatShading:true,shininess:120,specular:0xff8c00});
g.add(new THREE.Mesh(bodyG,bodyM));
// Nose tip (bright)
const noseG = new THREE.ConeGeometry(.15,.6,4);
noseG.rotateX(Math.PI/2);
const noseM = new THREE.MeshPhongMaterial({color:0xff8c00,emissive:0xff4400,emissiveIntensity:.3});
const nose = new THREE.Mesh(noseG,noseM);
nose.position.z=1.9;
g.add(nose);
// Wings — swept back
const wingShape=new THREE.Shape();
wingShape.moveTo(0,.15);wingShape.lineTo(-3.2,-1.8);wingShape.lineTo(-2.8,-.6);wingShape.lineTo(0,.4);
const wGeo=new THREE.ExtrudeGeometry(wingShape,{depth:.06,bevelEnabled:false});
const wMat=new THREE.MeshPhongMaterial({color:0x3a3a5c,flatShading:true});
const lw=new THREE.Mesh(wGeo,wMat);lw.position.set(.2,-.1,-.7);g.add(lw);
const rw=lw.clone();rw.scale.x=-1;rw.position.x=-.2;g.add(rw);
// Tail fins
const tailShape=new THREE.Shape();
tailShape.moveTo(0,0);tailShape.lineTo(-1.2,-1.5);tailShape.lineTo(-1.0,-.4);tailShape.lineTo(0,.2);
const tGeo=new THREE.ExtrudeGeometry(tailShape,{depth:.05,bevelEnabled:false});
const tf=new THREE.Mesh(tGeo,wMat);tf.position.set(.15,.3,-1.6);g.add(tf);
const tr=tf.clone();tr.scale.x=-1;tr.position.x=-.15;g.add(tr);
// Engine pods + glow
const podG=new THREE.CylinderGeometry(.2,.3,1.2,6);
podG.rotateX(Math.PI/2);
const podM=new THREE.MeshPhongMaterial({color:0x444455,flatShading:true});
[-.35,.35].forEach(x=>{
const p=new THREE.Mesh(podG,podM);p.position.set(x,-.15,-1.2);g.add(p);
// glow sphere
const gl=new THREE.Mesh(new THREE.SphereGeometry(.2,6,4),new THREE.MeshBasicMaterial({color:0xff6a00}));
gl.position.set(x,-.15,-1.85);g.add(gl);
});
// Engine point light
const eL=new THREE.PointLight(0xff6a00,2,18);eL.position.z=-2;g.add(eL);
scene.add(g);
return g;
}
// ════════════════════════════════════════
// PHYSICS STATE
// ════════════════════════════════════════
const phys={
pos:new THREE.Vector3(0,5,0),
vel:new THREE.Vector3(),
roll:0,pitch:0,targetRoll:0,targetPitch:0
};
// ════════════════════════════════════════
// INPUT
// ════════════════════════════════════════
const keys={};
addEventListener('keydown',e=>{keys[e.code]=true});
addEventListener('keyup',e=>{keys[e.code]=false});
// ════════════════════════════════════════
// PROCEDURAL CHUNK GENERATION
// ════════════════════════════════════════
let chunks=[], chunkIdx=0;
function createChunk(zPos){
const grp=new THREE.Group();
grp.position.z=zPos;
const df=Math.min(difficulty,6);
const w=CANYON_W-df*7;
const h=CANYON_H-df*3;
buildWalls(grp,w,h,zPos);
// Monoliths
if(Math.random()<.25+df*.08) addMonoliths(grp,w,h,zPos);
// Tight squeezes
if(df>1.5&&Math.random()<.18) addSqueeze(grp,w,h,zPos);
// Energy orbs
for(let i=0;i<1+~~(Math.random()*3);i++) addOrb(grp,w,h,zPos);
scene.add(grp);
return{grp,z:zPos,idx:chunkIdx++};
}
// Simple hash-based pseudo-random for consistent generation
function hash(n){let s=Math.sin(n)*43758.5453;return s-Math.floor(s)}
function buildWalls(grp,w,h,zBase){
const segs=24, sl=CHUNK_LEN/segs;
const ptsL=[],ptsR=[];
for(let i=0;i<=segs;i++){
const t=i/segs, z=-sl*i;
// Use hash-based noise for consistent generation per chunk
const seed=zBase*0.1+i;
const nL=(hash(seed)-.5)*w*.45;
const nR=(hash(seed+999)-.5)*w*.45;
const hL=(hash(seed+500)-.5)*h*.35;
const hR=(hash(seed+1500)-.5)*h*.35;
ptsL.push(new THREE.Vector3(-w/2+nL, h/2+hL, z));
ptsR.push(new THREE.Vector3( w/2+nR, h/2+hR, z));
}
// Wall materials — warm canyon colors
const wm1=new THREE.MeshPhongMaterial({color:0x8b5e3c,flatShading:true});
const wm2=new THREE.MeshPhongMaterial({color:0x7a4e2c,flatShading:true});
const wm3=new THREE.MeshPhongMaterial({color:0x6d4020,flatShading:true});
for(let i=0;i<segs;i++){
const p1=ptsL[i],p2=ptsL[i+1];
const b1=new THREE.Vector3(p1.x,-h*.5,p1.z);
const b2=new THREE.Vector3(p2.x,-h*.5,p2.z);
// Left wall face (top)
addTri(grp,p1,p2,b1, i%3===0?wm1:wm2);
addTri(grp,p2,new THREE.Vector3(p2.x,Math.max(0,h*.1),p2.z),b2, wm3);
// Right wall
const r1=ptsR[i],r2=ptsR[i+1];
const rb1=new THREE.Vector3(r1.x,-h*.5,r1.z);
const rb2=new THREE.Vector3(r2.x,-h*.5,r2.z);
addTri(grp,r1,r2,rb1, i%3===0?wm2:wm1);
addTri(grp,r2,new THREE.Vector3(r2.x,Math.max(0,h*.1),r2.z),rb2, wm3);
}
// Canyon floor — dark ground plane for this chunk
const floorGeo=new THREE.PlaneGeometry(w*1.5,CHUNK_LEN+10);
floorGeo.rotateX(-Math.PI/2);
const floorMat=new THREE.MeshPhongMaterial({color:0x2a1810,flatShading:true});
const floor=new THREE.Mesh(floorGeo,floorMat);
floor.position.y=-h*.5;
floor.receiveShadow=true;
grp.add(floor);
// Store collision data
grp.userData.walls={ptsL,ptsR,h};
}
function addTri(grp,a,b,c,mat){
const g=new THREE.BufferGeometry();
g.setAttribute('position',new THREE.BufferAttribute(new Float32Array([
a.x,a.y,a.z,b.x,b.y,b.z,c.x,c.y,c.z
]),3));
g.computeVertexNormals();
const m=new THREE.Mesh(g,mat);
m.castShadow=true;m.receiveShadow=true;
grp.add(m);
}
function addMonoliths(grp,w,h,zBase){
const cnt=1+~~(Math.random()*2);
if(!grp.userData.obs) grp.userData.obs=[];
for(let i=0;i<cnt;i++){
const x=(hash(zBase*7+i*3)-.5)*w*.6;
const z=(hash(zBase*13+i*7)-.5)*CHUNK_LEN*.8;
const mh=8+Math.random()*22;
// Tall geometric pillar — octagonal
const g=new THREE.CylinderGeometry(1.5,2,mh,6);
const m=new THREE.MeshPhongMaterial({color:0x9b59b6,flatShading:true,emissive:0x2a1040,emissiveIntensity:.3});
const mesh=new THREE.Mesh(g,m);
mesh.position.set(x,-h*.5+mh/2,z);
mesh.rotation.y=Math.random()*Math.PI;
mesh.castShadow=true;
grp.add(mesh);
// Add glowing top cap
const capG=new THREE.OctahedronGeometry(1.2,0);
const capM=new THREE.MeshBasicMaterial({color:0xcc66ff});
const cap=new THREE.Mesh(capG,capM);
cap.position.y=mh/2;
mesh.add(cap);
grp.userData.obs.push({mesh,type:'monolith'});
}
}
function addSqueeze(grp,w,h,zBase){
if(!grp.userData.obs) grp.userData.obs=[];
const sz=(hash(zBase*17)-.5)*CHUNK_LEN*.6;
const gapW=Math.max(6,18-difficulty*2);
const cx=(hash(zBase*23)-.5)*w*.25;
// Two large barriers with a narrow gap
const barrierG=new THREE.BoxGeometry(w*.4,h*1.1,gapW+4);
const barrierM=new THREE.MeshPhongMaterial({color:0x6d4020,flatShading:true});
[-1,1].forEach(side=>{
const b=new THREE.Mesh(barrierG,barrierM);
b.position.set(cx+side*(gapW/2+w*.2),0,sz);
b.castShadow=true;b.receiveShadow=true;
grp.add(b);
grp.userData.obs.push({mesh:b,type:'squeeze'});
});
// Orb in the gap as incentive
addOrb(grp,w,h,zBase,cx,-h*.5+10,sz,true);
}
function addOrb(grp,w,h,zBase,fx,fy,fz){
const x=fx!==undefined?fx:(hash(zBase*31)-.5)*w*.6;
const y=fy!==undefined?fy:2+(hash(zBase*37)-.5)*h*.4+5;
const z=fz!==undefined?fz:(hash(zBase*41)-.5)*CHUNK_LEN*.8;
// Octahedron orb
const g=new THREE.OctahedronGeometry(.7,0);
const m=new THREE.MeshBasicMaterial({color:0x00d4ff});
const mesh=new THREE.Mesh(g,m);
mesh.position.set(x,y,z);
// Glow shell
const glowG=new THREE.SphereGeometry(1.3,6,4);
const glowM=new THREE.MeshBasicMaterial({color:0x00d4ff,transparent:true,opacity:.15});
mesh.add(new THREE.Mesh(glowG,glowM));
// Point light
mesh.add(new THREE.PointLight(0x00d4ff,.8,12));
grp.add(mesh);
if(!grp.userData.orbs) grp.userData.orbs=[];
grp.userData.orbs.push({mesh,collected:false});
}
// ════════════════════════════════════════
// PARTICLE SYSTEMS
// ════════════════════════════════════════
let trailPts,trailPosArr,trailAlphaArr,trailIdx=0;
function initTrail(){
const N=300;
const g=new THREE.BufferGeometry();
const pos=new Float32Array(N*3);
const sz=new Float32Array(N);
const al=new Float32Array(N);
for(let i=0;i<N;i++){pos[i*3+1]=-999;sz[i]=Math.random()*4+1;}
g.setAttribute('position',new THREE.BufferAttribute(pos,3));
g.setAttribute('size',new THREE.BufferAttribute(sz,1));
g.setAttribute('alpha',new THREE.BufferAttribute(al,1));
const mat=new THREE.ShaderMaterial({
vertexShader:`attribute float size;attribute float alpha;varying float vA;void main(){vA=alpha;vec4 mv=modelViewMatrix*vec4(position,1.);gl_PointSize=size*(250./-mv.z);gl_Position=projectionMatrix*mv;}`,
fragmentShader:`varying float vA;void main(){float d=length(gl_PointCoord-.5);if(d>.5)discard;float a=smoothstep(.5,.1,d)*vA;gl_FragColor=vec4(1.,.35,.08,a);}`,
transparent:true,depthWrite:false,blending:THREE.AdditiveBlending
});
trailPts=new THREE.Points(g,mat);scene.add(trailPts);
trailPosArr=pos;trailAlphaArr=al;
}
// Speed/debris particles
let spdPts,spdPosArr;
function initSpeedParticles(){
const N=200;
const g=new THREE.BufferGeometry();
const pos=new Float32Array(N*3);
const sz=new Float32Array(N);
for(let i=0;i<N;i++){resetSpdP(pos,i);sz[i]=Math.random()*2+.5;}
g.setAttribute('position',new THREE.BufferAttribute(pos,3));
g.setAttribute('size',new THREE.BufferAttribute(sz,1));
const mat=new THREE.ShaderMaterial({
vertexShader:`attribute float size;void main(){vec4 mv=modelViewMatrix*vec4(position,1.);gl_PointSize=size*(180./-mv.z);gl_Position=projectionMatrix*mv;}`,
fragmentShader:`void main(){float d=length(gl_PointCoord-.5);if(d>.5)discard;float a=smoothstep(.5,.1,d)*.25;gl_FragColor=vec4(.7,.5,.85,a);}`,
transparent:true,depthWrite:false
});
spdPts=new THREE.Points(g,mat);scene.add(spdPts);
spdPosArr=pos;
}
function resetSpdP(pos,i){
const sp=60;
pos[i*3]=(Math.random()-.5)*sp;
pos[i*3+1]=Math.random()*40-10;
pos[i*3+2]=Math.random()*200-50;
}
// ════════════════════════════════════════
// AUDIO — Web Audio API procedural sounds
// ════════════════════════════════════════
let aCtx,engOsc,engGain,masterG,engFilter;
function initAudio(){
try{
aCtx=new(window.AudioContext||window.webkitAudioContext)();
masterG=aCtx.createGain();masterG.gain.value=.25;masterG.connect(aCtx.destination);
engOsc=aCtx.createOscillator();engOsc.type='sawtooth';engOsc.frequency.value=55;
engFilter=aCtx.createBiquadFilter();engFilter.type='lowpass';engFilter.frequency.value=180;engFilter.Q.value=4;
engGain=aCtx.createGain();engGain.gain.value=.12;
const lfo=aCtx.createOscillator();lfo.frequency.value=6;
const lg=aCtx.createGain();lg.gain.value=8;lfo.connect(lg);lg.connect(engFilter.frequency);lfo.start();
engOsc.connect(engFilter);engFilter.connect(engGain);engGain.connect(masterG);engOsc.start();
}catch(e){}
}
function sfxCollect(){
if(!aCtx)return;
const o=aCtx.createOscillator();o.type='sine';
o.frequency.setValueAtTime(700,aCtx.currentTime);
o.frequency.exponentialRampToValueAtTime(1400,aCtx.currentTime+.12);
const g=aCtx.createGain();g.gain.setValueAtTime(.25,aCtx.currentTime);
g.gain.exponentialRampToValueAtTime(.001,aCtx.currentTime+.18);
o.connect(g);g.connect(masterG);o.start();o.stop(aCtx.currentTime+.18);
}
function sfxNearMiss(){
if(!aCtx)return;
const N=aCtx.sampleRate*.12,buf=aCtx.createBuffer(1,N,aCtx.sampleRate),d=buf.getChannelData(0);
for(let i=0;i<N;i++) d[i]=(Math.random()*2-1)*Math.exp(-i/(N*.08));
const s=aCtx.createBufferSource();s.buffer=buf;
const f=aCtx.createBiquadFilter();f.type='bandpass';f.frequency.value=2500;f.Q.value=3;
const g=aCtx.createGain();g.gain.setValueAtTime(.12,aCtx.currentTime);
g.gain.exponentialRampToValueAtTime(.001,aCtx.currentTime+.12);
s.connect(f);f.connect(g);g.connect(masterG);s.start();
}
function sfxCrash(){
if(!aCtx)return;
const N=aCtx.sampleRate*.6,buf=aCtx.createBuffer(1,N,aCtx.sampleRate),d=buf.getChannelData(0);
for(let i=0;i<N;i++) d[i]=(Math.random()*2-1)*Math.exp(-i/(N*.25))*.6;
const s=aCtx.createBufferSource();s.buffer=buf;
const f=aCtx.createBiquadFilter();f.type='lowpass';
f.frequency.setValueAtTime(800,aCtx.currentTime);f.frequency.exponentialRampToValueAtTime(80,aCtx.currentTime+.5);
const g=aCtx.createGain();g.gain.value=.35;
s.connect(f);f.connect(g);g.connect(masterG);s.start();
}
function updateEngine(){
if(!engOsc)return;
const r=(spd-BASE_SPEED)/(TOP_SPEED*BOOST_MULT-BASE_SPEED);
engOsc.frequency.value=55+r*130;
engGain.gain.value=.08+r*.12;
}
// ════════════════════════════════════════
// CAMERA SHAKE
// ════════════════════════════════════════
let camShake=0;
function addShake(a){camShake=Math.min(camShake+a,2.5)}
// ════════════════════════════════════════
// NEAR MISS SYSTEM
// ════════════════════════════════════════
let nmCD=0,nmFlashT=0;
function checkNearMiss(){
if(nmCD>0)return;
const pp=phys.pos, closest={d:Infinity};
for(const ch of chunks){
const wz=ch.grp.position.z;
const wd=ch.grp.userData.walls;
if(wd&&wd.ptsL){
for(let i=0;i<wd.ptsL.length;i+=2){
const lp=wd.ptsL[i],rp=wd.ptsR[i];
const wz2=lp.z+wz;
if(Math.abs(wz2-pp.z)<18){
const dl=Math.abs(pp.x-lp.x),dr=Math.abs(pp.x-rp.x);
const md=Math.min(dl,dr);
if(md<closest.d) closest.d=md;
}
}
}
if(ch.grp.userData.obs){
for(const o of ch.grp.userData.obs){
const box=new THREE.Box3().setFromObject(o.mesh);
const d=box.distanceToPoint(pp);
if(d<closest.d) closest.d=d;
}
}
}
if(closest.d<NEAR_MISS_DIST&&closest.d>COLLIDE_DIST){
nmCD=.45;
score+=~~((NEAR_MISS_DIST-closest.d)*12)*mult;
mult=Math.min(mult+.5,10);
turbo=Math.min(turbo+6,100);
nmFlashT=.35;addShake(.4);sfxNearMiss();
const el=document.getElementById('multiplier');
el.textContent='x'+mult.toFixed(1);el.classList.add('active');
setTimeout(()=>el.classList.remove('active'),350);
}
}
// ════════════════════════════════════════
// COLLISION DETECTION
// ════════════════════════════════════════
function checkCollisions(){
const pp=phys.pos, pr=COLLIDE_DIST;
for(const ch of chunks){
const wz=ch.grp.position.z;
const wd=ch.grp.userData.walls;
if(wd&&wd.ptsL){
for(let i=0;i<wd.ptsL.length;i+=2){
const lp=wd.ptsL[i],rp=wd.ptsR[i];
const wz2=lp.z+wz;
if(Math.abs(wz2-pp.z)<12){
if(pp.x<lp.x+pr||pp.x>rp.x-pr) return true;
if(pp.y>Math.min(lp.y,rp.y)-3||pp.y<-wd.h*.45) return true;
}
}
}
if(ch.grp.userData.obs){
for(const o of ch.grp.userData.obs){
const box=new THREE.Box3().setFromObject(o.mesh);
box.expandByScalar(pr*.6);
if(box.containsPoint(pp)) return true;
}
}
// Orb collection
if(ch.grp.userData.orbs){
for(const orb of ch.grp.userData.orbs){
if(!orb.collected){
const wp=new THREE.Vector3();orb.mesh.getWorldPosition(wp);
if(pp.distanceTo(wp)<4.5){
orb.collected=true;orb.mesh.visible=false;
energy=Math.min(energy+18,100);
score+=60*mult;turbo=Math.min(turbo+10,100);
sfxCollect();
}
}
}
}
}
return false;
}
// ════════════════════════════════════════
// GAME INIT / RESET
// ════════════════════════════════════════
function initGame(){
for(const ch of chunks){scene.remove(ch.grp);ch.grp.traverse(c=>{if(c.geometry)c.geometry.dispose();if(c.material&&!Array.isArray(c.material))c.material.dispose()});}
chunks=[];chunkIdx=0;
score=0;mult=1;energy=100;turbo=0;spd=BASE_SPEED;maxSpd=0;distTraveled=0;difficulty=1;
phys.pos.set(0,5,0);phys.vel.set(0,0,0);phys.roll=0;phys.pitch=0;
for(let i=0;i<NUM_CHUNKS;i++){
chunks.push(createChunk(i*CHUNK_LEN-CHUNK_LEN*3));
}
if(playerGrp) playerGrp.position.copy(phys.pos);
}
// ════════════════════════════════════════
// HUD UPDATE
// ════════════════════════════════════════
function updateHUD(){
document.getElementById('score-display').textContent='SCORE: '+Math.floor(score).toLocaleString();
document.getElementById('speed-fill').style.width=((spd/TOP_SPEED)*100)+'%';
const ef=document.getElementById('energy-fill');
ef.style.width=energy+'%';
ef.style.background=energy<25?'linear-gradient(90deg,#ff3366,#ff0044)':'linear-gradient(90deg,#00d4ff,#7b2ff7)';
document.getElementById('turbo-fill').style.width=turbo+'%';
const nm=document.getElementById('near-miss-flash');
nm.style.opacity=nmFlashT>0?(nmFlashT/.35).toString():'0';
const bf=document.getElementById('boost-flash');
bf.style.opacity=(keys.ShiftLeft||keys.ShiftRight)&&turbo>0?'1':'0';
}
// ════════════════════════════════════════
// GAME OVER
// ════════════════════════════════════════
function doGameOver(){
running=false;sfxCrash();
document.getElementById('final-score').textContent=Math.floor(score).toLocaleString();
document.getElementById('max-speed').textContent=(maxSpd*120).toFixed(0)+' km/h';
document.getElementById('final-distance').textContent=Math.floor(distTraveled/10)+' m';
document.getElementById('game-over').style.display='flex';
if(engGain) engGain.gain.value=0;
}
// ════════════════════════════════════════
// MAIN LOOP
// ════════════════════════════════════════
let lastT=performance.now();
const clock=new THREE.Clock();
function loop(){
requestAnimationFrame(loop);
const now=performance.now();
let dt=Math.min((now-lastT)/1000,.05);
lastT=now;
if(!running){renderer.render(scene,camera);return;}
// ── INPUT ──
const ms=28*dt;
let ix=0,iy=0;
if(keys.ArrowLeft||keys.KeyA)ix--;if(keys.ArrowRight||keys.KeyD)ix++;
if(keys.ArrowUp||keys.KeyW)iy++;if(keys.ArrowDown||keys.KeyS)iy--;
// Boost
let boosting=false;
if((keys.ShiftLeft||keys.ShiftRight)&&turbo>0){boosting=true;turbo-=dt*35;spd=Math.min(spd+dt*6,TOP_SPEED*BOOST_MULT);}
if(keys.Space) spd=Math.max(spd-dt*10,BASE_SPEED*.4);
else if(!boosting) spd=Math.min(spd+dt*.4,TOP_SPEED);
maxSpd=Math.max(maxSpd,spd);
// ── PHYSICS ──
phys.targetRoll=-ix*.9;phys.targetPitch=iy*.5;
const rl=1-Math.pow(.04,dt),pl=1-Math.pow(.07,dt);
phys.roll+=(phys.targetRoll-phys.roll)*rl;
phys.pitch+=(phys.targetPitch-phys.pitch)*pl;
phys.vel.x+=ix*ms*12;phys.vel.y+=iy*ms*6;
phys.vel.x*=Math.pow(.03,dt);phys.vel.y*=Math.pow(.05,dt);
phys.vel.x=Math.max(-35,Math.min(35,phys.vel.x));
phys.vel.y=Math.max(-22,Math.min(22,phys.vel.y));
const fwd=spd*CHUNK_LEN*dt;
phys.pos.z+=fwd;phys.pos.x+=phys.vel.x*dt;phys.pos.y+=phys.vel.y*dt;
phys.pos.y=Math.max(-15,Math.min(40,phys.pos.y));
distTraveled+=fwd;
// ── ENERGY ──
energy-=ENERGY_DECAY*dt*(spd/TOP_SPEED);
if(energy<=0){doGameOver();return;}
// ── SCORE & DIFFICULTY ──
score+=spd*mult*dt*2.5;
difficulty=1+distTraveled/400;
// ── NEAR MISS ──
nmCD-=dt;nmFlashT-=dt;checkNearMiss();
// ── COLLISIONS ──
if(checkCollisions()){doGameOver();return;}
// ── CHUNK MANAGEMENT ──
const pz=phys.pos.z, front=pz+CHUNK_LEN*6, back=pz-CHUNK_LEN*4;
chunks=chunks.filter(ch=>{if(ch.grp.position.z<back){scene.remove(ch.grp);ch.grp.traverse(c=>{if(c.geometry)c.geometry.dispose();if(c.material&&!Array.isArray(c.material))c.material.dispose()});return false;}return true;});
let lastZ=chunks.length?Math.max(...chunks.map(c=>c.grp.position.z)):pz;
while(lastZ<front){lastZ+=CHUNK_LEN;chunks.push(createChunk(lastZ));}
// ── PLAYER VISUALS ──
if(playerGrp){
playerGrp.position.copy(phys.pos);
playerGrp.rotation.z=phys.roll;
playerGrp.rotation.x=-phys.pitch*.5;
const ei=1+(spd/TOP_SPEED)*4;
// Engine light is child index varies, find it
for(const c of playerGrp.children){if(c.isPointLight)c.intensity=ei;}
}
// ── CAMERA ──
const ct=new THREE.Vector3(phys.pos.x*.35,phys.pos.y+5.5,phys.pos.z-16);
camera.position.lerp(ct,1-Math.pow(.08,dt));
if(camShake>.01){
camera.position.x+=(Math.random()-.5)*camShake;
camera.position.y+=(Math.random()-.5)*camShake*.4;
camShake*=Math.pow(.92,dt*60);
}
const tFOV=78+(spd/TOP_SPEED)*18+(boosting?12:0);
camera.fov+=(tFOV-camera.fov)*dt*3;camera.updateProjectionMatrix();
const lt=new THREE.Vector3(phys.pos.x*.5,phys.pos.y+2,phys.pos.z+45);
camera.lookAt(lt);
// ── SKY & LIGHTS FOLLOW ──
sky.position.set(phys.pos.x*.1,phys.pos.y*.1,phys.pos.z);
sun.position.set(phys.pos.x+30,70,phys.pos.z-80);
sun.target.position.copy(phys.pos);sun.target.updateMatrixWorld();
// ── TRAIL PARTICLES ──
for(let i=0;i<300;i++){
if(trailAlphaArr[i]>0){trailAlphaArr[i]-=dt*1.8;trailPosArr[i*3+2]-=spd*CHUNK_LEN*dt*.5;}
}
if(Math.random()<spd*2.5){
const idx=trailIdx%300;
trailPosArr[idx*3]=phys.pos.x+(Math.random()-.5)*1.2;
trailPosArr[idx*3+1]=phys.pos.y+(Math.random()-.5)*.6;
trailPosArr[idx*3+2]=phys.pos.z-2.5;
trailAlphaArr[idx]=.9;trailIdx++;
}
trailPts.geometry.attributes.position.needsUpdate=true;
trailPts.geometry.attributes.alpha.needsUpdate=true;
// ── SPEED PARTICLES ──
for(let i=0;i<200;i++){
spdPosArr[i*3+2]-=spd*CHUNK_LEN*dt*1.8;
if(spdPosArr[i*3+2]<phys.pos.z-70){resetSpdP(spdPosArr,i);spdPosArr[i*3+2]=phys.pos.z+Math.random()*120;}
}
spdPts.geometry.attributes.position.needsUpdate=true;
// ── ANIMATE ORBS ──
const t=clock.getElapsedTime();
for(const ch of chunks){
if(ch.grp.userData.orbs)for(const o of ch.grp.userData.orbs){
if(!o.collected){o.mesh.rotation.y=t*2.5;o.mesh.position.y+=Math.sin(t*4+o.mesh.position.x)*dt*.6;}
}
}
// ── AUDIO & HUD ──
updateEngine();updateHUD();
renderer.render(scene,camera);
}
// ════════════════════════════════════════
// RESIZE
// ════════════════════════════════════════
addEventListener('resize',()=>{camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight);});
// ════════════════════════════════════════
// START / RESTART
// ════════════════════════════════════════
function startGame(){
document.getElementById('start-screen').style.display='none';
document.getElementById('game-over').style.display='none';
initAudio();buildPlayer();initTrail();initSpeedParticles();initGame();running=true;
}
document.getElementById('start-btn').addEventListener('click',startGame);
document.getElementById('restart-btn').addEventListener('click',()=>{
if(playerGrp){scene.remove(playerGrp);playerGrp.traverse(c=>{if(c.geometry)c.geometry.dispose();if(c.material&&!Array.isArray(c.material))c.material.dispose()});}
initAudio();buildPlayer();initGame();
document.getElementById('game-over').style.display='none';running=true;
});
// Kick off render loop
loop();
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"qwen3.6-27b-mtp","provider":"lmstudio","persona":"full","sessionId":"371f5420-56ae-4ea0-9c5a-40abe56ae34f","tokensIn":520479,"tokensOut":28102,"tokensTotal":548581,"turns":17,"toolCalls":16,"failedToolCalls":0,"timestamp":"2026-07-17T23:07:15.644Z"} -->