← Canyon Run results

Canyon Run

Gemma 4 31b · composer

Initial view of Canyon Run

Reviews & generation details

Reviews

Human review · Jul 17, 2026 · rubric 1

Historical path-based review. Association with these exact bytes is unverified.

Visual design
2 / 5
UX / feel
1 / 5
Works
1 / 5
Prompt spirit
1 / 5
Delight
1 / 5
Not quite broken, but no terrain visible; controls/status looks okay, simple but competent 3d model for the ship

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 17,874 bytes · SHA-256 b5b1ee1f23e7
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Neon Canyon Racer</title>
<script src="https://unpkg.com/three@0.160.0/build/three.min.js"></script>
<style>
/* ── css: layout ── */

body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #050510;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  color: #fff;
  user-select: none;
}
#hud {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  padding: 20px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  z-index: 10;
}
#energy-container {
  width: 300px;
  height: 10px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 5px;
  overflow: hidden;
  margin: 0 auto;
}
#energy-bar {
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, #ff00ff, #00ffff);
  box-shadow: 0 0 15px #00ffff;
  transition: width 0.1s ease-out;
}
#stats {
  position: absolute;
  top: 20px;
  right: 20px;
  text-align: right;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 2px;
}
#score {
  font-size: 32px;
  text-shadow: 0 0 10px #00ffff;
}
#multiplier {
  font-size: 24px;
  color: #ff00ff;
  transition: transform 0.1s;
}
#speed {
  font-size: 18px;
  opacity: 0.7;
}
#game-over {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.85);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 100;
  text-align: center;
}
#game-over.hidden {
  display: none;
}
#game-over h1 {
  font-size: 72px;
  margin-bottom: 10px;
  color: #ff00ff;
  text-shadow: 0 0 20px #ff00ff;
}
#final-stats {
  font-size: 24px;
  margin-bottom: 30px;
  line-height: 1.6;
}
#restart-btn {
  padding: 15px 40px;
  font-size: 20px;
  background: transparent;
  color: #fff;
  border: 2px solid #00ffff;
  cursor: pointer;
  transition: all 0.3s;
  text-transform: uppercase;
  letter-spacing: 2px;
}
#restart-btn:hover {
  background: #00ffff;
  color: #000;
  box-shadow: 0 0 20px #00ffff;
}
#instructions {
  position: absolute;
  bottom: 20px;
  left: 20px;
  font-size: 14px;
  opacity: 0.5;
  pointer-events: none;
  line-height: 1.5;
}
</style>
</head>
<body>
<div id="app">
  <div id="hud">
    <div id="energy-container">
      <div id="energy-bar"></div>
    </div>
    <div id="stats">
      <div id="score">0</div>
      <div id="multiplier">x1</div>
      <div id="speed">0 km/h</div>
    </div>
  </div>
  <div id="game-over" class="hidden">
    <h1>CRASHED</h1>
    <div id="final-stats"></div>
    <button id="restart-btn">RESTART</button>
  </div>
</div>
<div id="instructions">
  A/D or Left/Right: Tilt &amp; Turn<br>
  W/S or Up/Down: Pitch<br>
  SPACE: Boost (costs energy)
</div>
<script>
'use strict';

// ── class: AudioSystem ──
class AudioSystem {
  constructor() {
    this.ctx = new (window.AudioContext || window.webkitAudioContext)();
    this.masterGain = this.ctx.createGain();
    this.masterGain.connect(this.ctx.destination);
    this.engineOsc = null;
    this.engineGain = null;
  }

  startEngine() {
    if (this.engineOsc) return;
    this.engineOsc = this.ctx.createOscillator();
    this.engineGain = this.ctx.createGain();
    this.engineOsc.type = 'sawtooth';
    this.engineOsc.frequency.setValueAtTime(40, this.ctx.currentTime);
    this.engineGain.gain.setValueAtTime(0.1, this.ctx.currentTime);
    this.engineOsc.connect(this.engineGain);
    this.engineGain.connect(this.masterGain);
    this.engineOsc.start();
  }

  updateEngine(speed) {
    if (!this.engineOsc) return;
    const freq = 40 + speed * 0.5;
    this.engineOsc.frequency.setTargetAtTime(freq, this.ctx.currentTime, 0.1);
    this.engineGain.gain.setTargetAtTime(0.1 + speed * 0.001, this.ctx.currentTime, 0.1);
  }

  playPing() {
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(880, this.ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(440, this.ctx.currentTime + 0.1);
    gain.gain.setValueAtTime(0.2, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.1);
    osc.connect(gain);
    gain.connect(this.masterGain);
    osc.start();
    osc.stop(this.ctx.currentTime + 0.1);
  }

  playWhoosh() {
    const noise = this.ctx.createBufferSource();
    const bufSize = this.ctx.sampleRate * 0.2;
    const buf = this.ctx.createBuffer(1, bufSize, this.ctx.sampleRate);
    const data = buf.getChannelData(0);
    for (let i = 0; i < bufSize; i++) data[i] = Math.random() * 2 - 1;
    noise.buffer = buf;
    const filter = this.ctx.createBiquadFilter();
    filter.type = 'lowpass';
    filter.frequency.setValueAtTime(100, this.ctx.currentTime);
    filter.frequency.exponentialRampToValueAtTime(2000, this.ctx.currentTime + 0.1);
    filter.frequency.exponentialRampToValueAtTime(100, this.ctx.currentTime + 0.2);
    const gain = this.ctx.createGain();
    gain.gain.setValueAtTime(0, this.ctx.currentTime);
    gain.gain.linearRampToValueAtTime(0.1, this.ctx.currentTime + 0.1);
    gain.gain.linearRampToValueAtTime(0, this.ctx.currentTime + 0.2);
    noise.connect(filter);
    filter.connect(gain);
    gain.connect(this.masterGain);
    noise.start();
  }

  stop() {
    if (this.engineOsc) {
      this.engineOsc.stop();
      this.engineOsc = null;
    }
  }
}

// ── class: GameState ──
class GameState {
  constructor() {
    this.score = 0;
    this.multiplier = 1;
    this.energy = 100;
    this.speed = 0;
    this.maxSpeed = 0;
    this.targetSpeed = 50;
    this.isGameOver = false;
    this.boostActive = false;
    this.distance = 0;
    this.difficulty = 1.0;
  }

  reset() {
    this.score = 0;
    this.multiplier = 1;
    this.energy = 100;
    this.speed = 0;
    this.maxSpeed = 0;
    this.targetSpeed = 50;
    this.isGameOver = false;
    this.boostActive = false;
    this.distance = 0;
    this.difficulty = 1.0;
  }
}

// ── class: Player ──
class Player {
  constructor(scene) {
    this.mesh = this.createMesh();
    scene.add(this.mesh);
    this.velocity = new THREE.Vector3(0, 0, 0);
    this.rotationTarget = new THREE.Euler(0, 0, 0);
    this.position = new THREE.Vector3(0, 0, 0);
    this.radius = 0.5;
  }

  createMesh() {
    const group = new THREE.Group();
    const bodyGeo = new THREE.ConeGeometry(0.3, 1.2, 4);
    bodyGeo.rotateX(Math.PI / 2);
    const mat = new THREE.MeshPhongMaterial({ color: 0x00ffff, flatShading: true });
    const body = new THREE.Mesh(bodyGeo, mat);
    group.add(body);

    const wingGeo = new THREE.BoxGeometry(1.5, 0.1, 0.4);
    const wing = new THREE.Mesh(wingGeo, mat);
    wing.position.set(0, 0, 0.2);
    group.add(wing);

    const engineGeo = new THREE.CylinderGeometry(0.2, 0.2, 0.2, 8);
    engineGeo.rotateX(Math.PI / 2);
    const engineMat = new THREE.MeshBasicMaterial({ color: 0xff00ff });
    const engine = new THREE.Mesh(engineGeo, engineMat);
    engine.position.set(0, 0, 0.6);
    group.add(engine);

    return group;
  }

  update(input, state, delta) {
    // Banking and Turning
    const turnSpeed = 2.0;
    const pitchSpeed = 1.5;
    
    if (input.left) {
      this.rotationTarget.z = Math.PI / 6;
      this.velocity.x -= turnSpeed * delta * (state.speed / 50);
    } else if (input.right) {
      this.rotationTarget.z = -Math.PI / 6;
      this.velocity.x += turnSpeed * delta * (state.speed / 50);
    } else {
      this.rotationTarget.z = 0;
      this.velocity.x *= 0.95;
    }

    if (input.up) {
      this.rotationTarget.x = -Math.PI / 12;
      this.velocity.y += pitchSpeed * delta * (state.speed / 50);
    } else if (input.down) {
      this.rotationTarget.x = Math.PI / 12;
      this.velocity.y -= pitchSpeed * delta * (state.speed / 50);
    } else {
      this.rotationTarget.x = 0;
      this.velocity.y *= 0.95;
    }

    // Movement
    this.position.add(this.velocity);
    this.position.z -= state.speed * delta;
    
    // Constraints
    const boundary = 20;
    if (this.position.x > boundary) this.position.x = boundary;
    if (this.position.x < -boundary) this.position.x = -boundary;
    if (this.position.y > boundary) this.position.y = boundary;
    if (this.position.y < -boundary) this.position.y = -boundary;

    // Smooth rotation
    this.mesh.position.copy(this.position);
    this.mesh.rotation.x += (this.rotationTarget.x - this.mesh.rotation.x) * 0.1;
    this.mesh.rotation.z += (this.rotationTarget.z - this.mesh.rotation.z) * 0.1;
    this.mesh.rotation.y = -this.mesh.rotation.z * 0.5;
  }
}

// ── class: World ──
class World {
  constructor(scene) {
    this.scene = scene;
    this.chunks = [];
    this.lastChunkZ = 0;
    this.chunkSize = 100;
    this.objects = [];
    this.orbs = [];
    this.wallMaterial = new THREE.MeshPhongMaterial({ color: 0x221133, flatShading: true });
  }

  generateChunk() {
    const zStart = this.lastChunkZ;
    const group = new THREE.Group();
    
    // Dynamic canyon width that changes per chunk
    const width = 30 + Math.sin(zStart * 0.01) * 10;
    const height = 30 + Math.cos(zStart * 0.01) * 5;

    // Left Wall
    const leftWallGeo = new THREE.BoxGeometry(10, height, this.chunkSize);
    const leftWall = new THREE.Mesh(leftWallGeo, this.wallMaterial);
    leftWall.position.set(-width/2 - 5, 0, zStart - this.chunkSize / 2);
    group.add(leftWall);

    // Right Wall
    const rightWallGeo = new THREE.BoxGeometry(10, height, this.chunkSize);
    const rightWall = new THREE.Mesh(rightWallGeo, this.wallMaterial);
    rightWall.position.set(width/2 + 5, 0, zStart - this.chunkSize / 2);
    group.add(rightWall);

    // Floor
    const floorGeo = new THREE.BoxGeometry(width + 20, 2, this.chunkSize);
    const floor = new THREE.Mesh(floorGeo, this.wallMaterial);
    floor.position.set(0, -height/2 - 1, zStart - this.chunkSize / 2);
    group.add(floor);

    // Procedural Obstacles
    const obstacleCount = 4 + Math.floor(Math.random() * 4);
    for (let i = 0; i < obstacleCount; i++) {
      const type = Math.random();
      const zPos = zStart - Math.random() * this.chunkSize;
      
      if (type < 0.6) {
        // Monolith
        const size = 2 + Math.random() * 8;
        const geo = new THREE.BoxGeometry(size, size * 2, size);
        const mesh = new THREE.Mesh(geo, this.wallMaterial);
        mesh.position.set((Math.random() - 0.5) * width, (Math.random() - 0.5) * height/2, zPos);
        group.add(mesh);
        this.objects.push({ mesh, radius: size/2 });
      } else {
        // Floating Block
        const size = 4 + Math.random() * 4;
        const geo = new THREE.BoxGeometry(size, size, size);
        const mesh = new THREE.Mesh(geo, this.wallMaterial);
        mesh.position.set((Math.random() - 0.5) * width, (Math.random() - 0.5) * height, zPos);
        group.add(mesh);
        this.objects.push({ mesh, radius: size/2 });
      }
    }

    // Energy Orbs
    const orbCount = 2 + Math.floor(Math.random() * 3);
    for (let i = 0; i < orbCount; i++) {
      const orbGeo = new THREE.IcosahedronGeometry(0.8, 0);
      const orbMat = new THREE.MeshBasicMaterial({ color: 0x00ffff });
      const orb = new THREE.Mesh(orbGeo, orbMat);
      orb.position.set((Math.random() - 0.5) * width, (Math.random() - 0.5) * height, zStart - Math.random() * this.chunkSize);
      group.add(orb);
      this.orbs.push({ mesh: orb, radius: 0.8 });
    }

    this.scene.add(group);
    this.chunks.push(group);
    this.lastChunkZ -= this.chunkSize;
  }

  update(playerPos) {
    while (playerPos.z > this.lastChunkZ + this.chunkSize * 2) {
      this.generateChunk();
    }
    
    if (this.chunks.length > 10) {
      const oldChunk = this.chunks.shift();
      this.scene.remove(oldChunk);
      
      // Sync objects/orbs lists with what's actually in the scene
      this.objects = this.objects.filter(obj => obj.mesh.parent !== null && obj.mesh.position.z < playerPos.z + 100);
      this.orbs = this.orbs.filter(orb => orb.mesh.parent !== null && orb.mesh.position.z < playerPos.z + 100);
    }
  }
}

// ── main ──

const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x050510, 0.008);

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x050510);
document.body.appendChild(renderer.domElement);

const ambientLight = new THREE.AmbientLight(0x404040, 2);
scene.add(ambientLight);

const sunLight = new THREE.DirectionalLight(0xffaa00, 3);
sunLight.position.set(10, 20, 10);
scene.add(sunLight);

const audio = new AudioSystem();
const state = new GameState();
const player = new Player(scene);
const world = new World(scene);

const input = { left: false, right: false, up: false, down: false, boost: false };

window.addEventListener('keydown', (e) => {
  if (e.code === 'KeyA' || e.code === 'ArrowLeft') input.left = true;
  if (e.code === 'KeyD' || e.code === 'ArrowRight') input.right = true;
  if (e.code === 'KeyW' || e.code === 'ArrowUp') input.up = true;
  if (e.code === 'KeyS' || e.code === 'ArrowDown') input.down = true;
  if (e.code === 'Space') input.boost = true;
  if (audio.ctx.state === 'suspended') audio.ctx.resume();
  audio.startEngine();
});

window.addEventListener('keyup', (e) => {
  if (e.code === 'KeyA' || e.code === 'ArrowLeft') input.left = false;
  if (e.code === 'KeyD' || e.code === 'ArrowRight') input.right = false;
  if (e.code === 'KeyW' || e.code === 'ArrowUp') input.up = false;
  if (e.code === 'KeyS' || e.code === 'ArrowDown') input.down = false;
  if (e.code === 'Space') input.boost = false;
});

const hudScore = document.getElementById('score');
const hudMultiplier = document.getElementById('multiplier');
const hudSpeed = document.getElementById('speed');
const hudEnergy = document.getElementById('energy-bar');
const gameOverEl = document.getElementById('game-over');
const finalStats = document.getElementById('final-stats');
const restartBtn = document.getElementById('restart-btn');

restartBtn.onclick = () => {
  state.reset();
  player.position.set(0, 0, 0);
  player.velocity.set(0, 0, 0);
  world.chunks.forEach(c => scene.remove(c));
  world.chunks = [];
  world.objects = [];
  world.orbs = [];
  world.lastChunkZ = 0;
  gameOverEl.classList.add('hidden');
  audio.startEngine();
};

function updateHUD() {
  hudScore.innerText = Math.floor(state.score);
  hudMultiplier.innerText = `x${state.multiplier.toFixed(1)}`;
  hudSpeed.innerText = `${Math.floor(state.speed * 10)} km/h`;
  hudEnergy.style.width = `${state.energy}%`;
}

let lastTime = performance.now();
function loop() {
  const now = performance.now();
  const delta = Math.min((now - lastTime) / 1000, 0.1); 
  lastTime = now;

  if (!state.isGameOver) {
    let currentTarget = state.targetSpeed;
    if (input.boost && state.energy > 1) {
      currentTarget *= 2;
      state.energy -= 25 * delta;
      state.boostActive = true;
    } else {
      state.boostActive = false;
    }
    
    state.speed += (currentTarget - state.speed) * 1.5 * delta;
    state.maxSpeed = Math.max(state.maxSpeed, state.speed);
    
    state.energy -= 3 * delta;
    if (state.energy <= 0) {
      state.energy = 0;
      state.isGameOver = true;
    }

    player.update(input, state, delta);
    world.update(player.position);

    camera.position.lerp(new THREE.Vector3(
      player.position.x * 0.6,
      player.position.y * 0.6 + 2.5,
      player.position.z + 6
    ), 0.1);
    camera.lookAt(player.position.x, player.position.y, player.position.z - 15);
    camera.fov = 75 + (state.speed / 5) + (state.boostActive ? 15 : 0);
    camera.updateProjectionMatrix();

    const pPos = player.position;
    
    for (let i = world.objects.length - 1; i >= 0; i--) {
      const obj = world.objects[i];
      if (!obj.mesh.parent) continue;
      const dist = pPos.distanceTo(obj.mesh.position);
      if (dist < obj.radius + player.radius) {
        state.isGameOver = true;
      } else if (dist < obj.radius + 4) {
        state.multiplier += 0.1 * delta;
        state.score += state.multiplier * delta * 20;
        if (Math.random() < 0.02) audio.playWhoosh();
      }
    }

    for (let i = world.orbs.length - 1; i >= 0; i--) {
      const orb = world.orbs[i];
      if (!orb.mesh.parent) continue;
      const dist = pPos.distanceTo(orb.mesh.position);
      if (dist < orb.radius + player.radius) {
        state.energy = Math.min(100, state.energy + 15);
        state.score += 200 * state.multiplier;
        audio.playPing();
        world.scene.remove(orb.mesh);
        world.orbs.splice(i, 1);
      }
    }

    state.score += state.multiplier * delta * 10;
    audio.updateEngine(state.speed);
    updateHUD();
  } else {
    gameOverEl.classList.remove('hidden');
    finalStats.innerHTML = `Distance: ${Math.floor(-player.position.z)}m<br>Score: ${Math.floor(state.score)}<br>Max Speed: ${Math.floor(state.maxSpeed * 10)} km/h`;
    audio.stop();
  }

  renderer.render(scene, camera);
  requestAnimationFrame(loop);
}

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

loop();
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"composer","sessionId":"fcf9f931-29ea-4f65-ae6a-5ba939d2f04b","tokensIn":108121,"tokensOut":9521,"tokensTotal":117642,"turns":12,"toolCalls":11,"failedToolCalls":0,"timestamp":"2026-07-14T16:13:21.215Z"} -->