Canyon Run
Gemma 4 31b · basic

Reviews & generation details
Reviews
Human review · Jul 16, 2026 · rubric 1
Historical path-based review. Association with these exact bytes is unverified.
- Visual design
- 1 / 5
- UX / feel
- 2 / 5
- Works
- 1 / 5
- Prompt spirit
- 2 / 5
- Delight
- 1 / 5
core loop broken; menu works and sound works
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 14,569 bytes · SHA-256 fa5d529c5e53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NEON CANYON RUNNER</title>
<style>
body { margin: 0; overflow: hidden; background: #0a0a0f; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #fff; user-select: none; }
#ui { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; }
#hud { position: absolute; top: 20px; left: 20px; width: calc(100% - 40px); display: flex; justify-content: space-between; align-items: flex-start; }
.stat { text-align: left; text-transform: uppercase; letter-spacing: 2px; }
.label { font-size: 10px; opacity: 0.6; }
.value { font-size: 24px; font-weight: bold; text-shadow: 0 0 10px #ff00ff; }
#energy-container { position: absolute; bottom: 40px; left: 50%; transform: translateX(-50%); width: 300px; height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden; }
#energy-bar { width: 100%; height: 100%; background: linear-gradient(90deg, #00f2ff, #0066ff); transition: width 0.1s linear; box-shadow: 0 0 15px #00f2ff; }
#multiplier { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 80px; font-weight: 900; opacity: 0; transition: opacity 0.2s, transform 0.2s; color: #ffdf00; text-shadow: 0 0 20px #ffaa00; }
#overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); display: flex; flex-direction: column; justify-content: center; align-items: center; pointer-events: all; text-align: center; }
h1 { font-size: 48px; margin-bottom: 10px; letter-spacing: 10px; color: #00f2ff; text-shadow: 0 0 20px #00f2ff; }
p { margin-bottom: 30px; opacity: 0.8; }
button { padding: 15px 40px; background: transparent; border: 2px solid #00f2ff; color: #00f2ff; font-size: 18px; cursor: pointer; transition: 0.3s; text-transform: uppercase; letter-spacing: 2px; }
button:hover { background: #00f2ff; color: #000; box-shadow: 0 0 20px #00f2ff; }
#game-over { display: none; }
</style>
</head>
<body>
<div id="ui">
<div id="hud">
<div class="stat">
<div class="label">Score</div>
<div id="score-val" class="value">0</div>
</div>
<div class="stat" style="text-align: right;">
<div class="label">Speed</div>
<div id="speed-val" class="value">0</div>
</div>
</div>
<div id="multiplier">1X</div>
<div id="energy-container"><div id="energy-bar"></div></div>
<div id="overlay">
<div id="start-screen">
<h1>NEON CANYON</h1>
<p>WASD / Arrows to Fly<br>Stay close to walls for Multiplier<br>Collect Blue Orbs for Energy</p>
<button onclick="startGame()">Launch</button>
</div>
<div id="game-over">
<h1>CRASHED</h1>
<p id="final-stats"></p>
<button onclick="startGame()">Retry</button>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js"></script>
<script>
/** GAME STATE **/
const STATE = {
playing: false,
score: 0,
energy: 100,
speed: 0,
targetSpeed: 0.8,
multiplier: 1,
distance: 0,
maxSpeed: 0,
input: { w: false, a: false, s: false, d: false }
};
/** AUDIO SYNTH **/
const AudioCtx = window.AudioContext || window.webkitAudioContext;
let audioCtx;
function playSound(freq, type, duration, vol = 0.1) {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
gain.gain.setValueAtTime(vol, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + duration);
}
/** THREE.JS SETUP **/
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x0a0a1a, 0.015);
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(0x0a0a1a);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const ambientLight = new THREE.AmbientLight(0x404070, 0.5);
scene.add(ambientLight);
const sunLight = new THREE.DirectionalLight(0xffaa00, 1.5);
sunLight.position.set(10, 50, 10);
scene.add(sunLight);
/** PLAYER CRAFT **/
const shipGroup = new THREE.Group();
const shipBody = new THREE.Mesh(
new THREE.ConeGeometry(0.2, 1, 4),
new THREE.MeshPhongMaterial({ color: 0xcccccc, flatShading: true })
);
shipBody.rotation.x = Math.PI / 2;
shipGroup.add(shipBody);
const engineGlow = new THREE.Mesh(
new THREE.SphereGeometry(0.1, 8, 8),
new THREE.MeshBasicMaterial({ color: 0x00f2ff })
);
engineGlow.position.z = 0.5;
shipGroup.add(engineGlow);
scene.add(shipGroup);
/** ENVIRONMENT GENERATION **/
const chunks = [];
const CHUNK_SIZE = 100;
const chunkMaterial = new THREE.MeshPhongMaterial({ color: 0x2a2a4a, flatShading: true });
function createChunk(zOffset) {
const group = new THREE.Group();
const wallGeo = new THREE.BoxGeometry(1, 1, 1);
// Procedural Walls
for (let i = 0; i < 20; i++) {
const z = i * (CHUNK_SIZE / 20);
const width = 15 + Math.sin(z * 0.1) * 5;
const height = 20 + Math.random() * 20;
// Left Wall
const leftWall = new THREE.Mesh(new THREE.BoxGeometry(width, height, 10), chunkMaterial);
leftWall.position.set(-25 - width/2, height/2 - 5, z);
leftWall.rotation.y = Math.random() * 0.2;
group.add(leftWall);
// Right Wall
const rightWall = new THREE.Mesh(new THREE.BoxGeometry(width, height, 10), chunkMaterial);
rightWall.position.set(25 + width/2, height/2 - 5, z);
rightWall.rotation.y = -Math.random() * 0.2;
group.add(rightWall);
// Random Pillars
if (Math.random() > 0.7) {
const pillar = new THREE.Mesh(new THREE.BoxGeometry(4, 40, 4), chunkMaterial);
pillar.position.set((Math.random() - 0.5) * 30, 10, z);
group.add(pillar);
}
// Energy Orb
if (Math.random() > 0.8) {
const orb = new THREE.Mesh(
new THREE.IcosahedronGeometry(0.5, 0),
new THREE.MeshBasicMaterial({ color: 0x00f2ff })
);
orb.position.set((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 10, z);
orb.userData = { type: 'energy' };
group.add(orb);
}
}
group.position.z = zOffset;
scene.add(group);
chunks.push(group);
}
// Initial Chunks
for (let i = 0; i < 3; i++) createChunk(-i * CHUNK_SIZE);
/** INPUTS **/
const keyMap = {
'KeyW': 'w', 'ArrowUp': 'w',
'KeyS': 's', 'ArrowDown': 's',
'KeyA': 'a', 'ArrowLeft': 'a',
'KeyD': 'd', 'ArrowRight': 'd'
};
window.addEventListener('keydown', (e) => { if(keyMap[e.code]) STATE.input[keyMap[e.code]] = true; });
window.addEventListener('keyup', (e) => { if(keyMap[e.code]) STATE.input[keyMap[e.code]] = false; });
/** GAME LOGIC **/
function startGame() {
audioCtx = new AudioCtx();
STATE.playing = true;
STATE.score = 0;
STATE.energy = 100;
STATE.distance = 0;
STATE.speed = 0;
STATE.maxSpeed = 0;
shipGroup.position.set(0, 0, 0);
document.getElementById('overlay').style.display = 'none';
document.getElementById('game-over').style.display = 'none';
document.getElementById('start-screen').style.display = 'none';
playSound(200, 'square', 0.5);
}
function gameOver() {
STATE.playing = false;
document.getElementById('overlay').style.display = 'flex';
document.getElementById('game-over').style.display = 'block';
document.getElementById('final-stats').innerHTML = `Final Score: ${Math.floor(STATE.score)}<br>Max Speed: ${Math.floor(STATE.maxSpeed * 100)} km/h`;
playSound(100, 'sawtooth', 1, 0.3);
}
function update() {
if (!STATE.playing) return;
// Physics
STATE.targetSpeed = 0.8 + (STATE.score / 10000);
STATE.speed = THREE.MathUtils.lerp(STATE.speed, STATE.targetSpeed, 0.01);
STATE.distance += STATE.speed;
// Banking and Movement
const turnSpeed = 0.15;
if (STATE.input.a) shipGroup.position.x -= turnSpeed;
if (STATE.input.d) shipGroup.position.x += turnSpeed;
if (STATE.input.w) shipGroup.position.y += turnSpeed;
if (STATE.input.s) shipGroup.position.y -= turnSpeed;
// Visual Tilt
const targetRotationZ = STATE.input.a ? 0.4 : STATE.input.d ? -0.4 : 0;
const targetRotationX = STATE.input.w ? -0.2 : STATE.input.s ? 0.2 : 0;
shipGroup.rotation.z = THREE.MathUtils.lerp(shipGroup.rotation.z, targetRotationZ, 0.1);
shipGroup.rotation.x = THREE.MathUtils.lerp(shipGroup.rotation.x, targetRotationX, 0.1);
// Energy Decay
STATE.energy -= 0.05;
if (STATE.energy <= 0) gameOver();
// Chunk Management
if (STATE.distance > chunks[0].position.z + CHUNK_SIZE) {
const old = chunks.shift();
scene.remove(old);
createChunk(chunks[chunks.length - 1].position.z - CHUNK_SIZE);
}
// Collision & Proximity
let nearMiss = false;
const playerBox = new THREE.Box3().setFromObject(shipGroup);
chunks.forEach(chunk => {
chunk.children.forEach(obj => {
if (obj.userData.type === 'energy') {
const dist = shipGroup.position.distanceTo(obj.position);
if (dist < 2) {
STATE.energy = Math.min(100, STATE.energy + 20);
obj.position.y = -100; // "Collect" it
playSound(800, 'sine', 0.1);
}
} else {
const objBox = new THREE.Box3().setFromObject(obj);
if (playerBox.intersectsBox(objBox)) {
gameOver();
} else if (playerBox.distanceToBox(objBox) < 3) {
nearMiss = true;
}
}
});
});
if (nearMiss) {
STATE.multiplier = Math.min(10, STATE.multiplier + 0.05);
const multEl = document.getElementById('multiplier');
multEl.style.opacity = 1;
multEl.style.transform = `translate(-50%, -50%) scale(${1 + STATE.multiplier * 0.1})`;
multEl.innerText = Math.floor(STATE.multiplier) + 'X';
} else {
STATE.multiplier = THREE.MathUtils.lerp(STATE.multiplier, 1, 0.01);
document.getElementById('multiplier').style.opacity = 0;
}
STATE.score += STATE.speed * STATE.multiplier;
STATE.maxSpeed = Math.max(STATE.maxSpeed, STATE.speed);
// Camera Follow
camera.position.lerp(new THREE.Vector3(
shipGroup.position.x * 0.5,
shipGroup.position.y * 0.5 + 1,
shipGroup.position.z + 5
), 0.1);
camera.lookAt(new THREE.Vector3(shipGroup.position.x, shipGroup.position.y, shipGroup.position.z - 10));
// Dynamic FOV
camera.fov = 75 + (STATE.speed * 10);
camera.updateProjectionMatrix();
// Update HUD
document.getElementById('score-val').innerText = Math.floor(STATE.score);
document.getElementById('speed-val').innerText = Math.floor(STATE.speed * 200) + ' KM/H';
document.getElementById('energy-bar').style.width = STATE.energy + '%';
// Position ship to stay "fixed" in view relative to world flow
// Actually, in this implementation, ship stays near 0,0,0 and we move the world
// But since we use chunks, we just increment distance and shift chunks?
// Let's use the simpler "Ship moves forward" approach.
shipGroup.position.z -= STATE.speed;
}
function animate() {
requestAnimationFrame(animate);
update();
renderer.render(scene, camera);
}
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"basic","sessionId":"e349b9d0-6f64-46fb-b051-bee2a89b5161","tokensIn":979,"tokensOut":4210,"tokensTotal":5189,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-07-14T21:55:49.819Z"} -->