Canyon Run
Gemma 4 31b · middle

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
- 3 / 5
- Works
- 2 / 5
- Prompt spirit
- 3 / 5
- Delight
- 2 / 5
Core loop gets broken when pressing space; unable to collect some energy pickups; competent but imperfect and ugly
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 19,198 bytes · SHA-256 ac82110616cf
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NEON CANYON - High Speed Racer</title>
<style>
body { margin: 0; overflow: hidden; background: #0a0a1a; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #fff; user-select: none; }
canvas { display: block; }
#hud {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
pointer-events: none; display: flex; flex-direction: column; justify-content: space-between;
padding: 30px; box-sizing: border-box;
}
#top-bar { display: flex; justify-content: space-between; align-items: flex-start; }
.stat-group { display: flex; flex-direction: column; }
.label { font-size: 12px; text-transform: uppercase; opacity: 0.6; letter-spacing: 2px; margin-bottom: 5px; }
.value { font-size: 32px; font-weight: 800; font-variant-numeric: tabular-nums; }
#multiplier-container { text-align: center; }
#multiplier {
font-size: 64px; font-weight: 900; color: #ffcc00;
text-shadow: 0 0 20px rgba(255, 204, 0, 0.6);
transition: transform 0.1s ease-out;
}
#energy-container {
width: 300px; height: 10px; background: rgba(255,255,255,0.1);
border-radius: 5px; overflow: hidden; margin: 0 auto 50px auto;
border: 1px solid rgba(255,255,255,0.2);
}
#energy-bar {
width: 100%; height: 100%; background: linear-gradient(90deg, #ff00cc, #33ccff);
transition: width 0.1s linear; box-shadow: 0 0 15px #33ccff;
}
#overlay {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.8); backdrop-filter: blur(10px);
display: flex; flex-direction: column; justify-content: center; align-items: center;
z-index: 10; transition: opacity 0.5s;
}
#overlay.hidden { opacity: 0; pointer-events: none; }
.menu-title { font-size: 48px; font-weight: 900; letter-spacing: 10px; margin-bottom: 20px; text-align: center; }
.menu-score { font-size: 24px; margin-bottom: 40px; text-align: center; line-height: 1.6; }
.btn {
padding: 15px 40px; background: none; border: 2px solid #fff; color: #fff;
font-size: 18px; cursor: pointer; transition: 0.3s; letter-spacing: 2px;
text-transform: uppercase; font-weight: bold;
}
.btn:hover { background: #fff; color: #000; }
#instructions {
position: absolute; bottom: 80px; width: 100%; text-align: center;
font-size: 14px; opacity: 0.7; letter-spacing: 1px;
}
</style>
</head>
<body>
<div id="hud">
<div id="top-bar">
<div class="stat-group">
<div class="label">Score</div>
<div id="score" class="value">0</div>
</div>
<div id="multiplier-container">
<div class="label">Multiplier</div>
<div id="multiplier" class="value">1x</div>
</div>
<div class="stat-group" style="text-align: right;">
<div class="label">Velocity</div>
<div id="speed" class="value">0</div>
</div>
</div>
<div id="energy-container">
<div id="energy-bar"></div>
</div>
</div>
<div id="overlay">
<div class="menu-title">NEON CANYON</div>
<div id="final-stats" class="menu-score"></div>
<button class="btn" onclick="startGame()">Enter the Void</button>
<div id="instructions">USE ARROWS or WASD to steer & bank</div>
</div>
<!-- Three.js and Post-processing -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
// --- Game Constants ---
const CONFIG = {
BASE_SPEED: 80,
MAX_SPEED: 220,
ACCEL: 0.5,
STEER_SPEED: 0.05,
BANK_SPEED: 0.1,
ENERGY_DECAY: 0.15,
ENERGY_GAIN: 25,
CHUNK_SIZE: 600,
CHUNK_DISTANCE: 3,
NEAR_MISS_DIST: 15,
COLLISION_DIST: 4,
};
// --- State ---
let state = {
score: 0,
multiplier: 1,
energy: 100,
currentSpeed: CONFIG.BASE_SPEED,
playing: false,
lastTime: 0,
distanceTraveled: 0,
input: { left: false, right: false, up: false, down: false }
};
// --- Audio Engine ---
const AudioEngine = (() => {
let ctx, masterGain, engineOsc;
function init() {
ctx = new (window.AudioContext || window.webkitAudioContext)();
masterGain = ctx.createGain();
masterGain.gain.value = 0.3;
masterGain.connect(ctx.destination);
engineOsc = ctx.createOscillator();
const engineGain = ctx.createGain();
engineOsc.type = 'sawtooth';
engineOsc.frequency.value = 60;
engineGain.gain.value = 0.1;
engineOsc.connect(engineGain);
engineGain.connect(masterGain);
engineOsc.start();
}
function updateEngine(speed) {
if(!engineOsc) return;
const freq = 60 + (speed / CONFIG.MAX_SPEED) * 100;
engineOsc.frequency.setTargetAtTime(freq, ctx.currentTime, 0.1);
}
function playPing(freq, type = 'sine', volume = 0.2) {
if(!ctx) return;
const osc = ctx.createOscillator();
const g = ctx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, ctx.currentTime);
g.gain.setValueAtTime(volume, ctx.currentTime);
g.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
osc.connect(g);
g.connect(masterGain);
osc.start();
osc.stop(ctx.currentTime + 0.3);
}
return { init, updateEngine, playPing };
})();
// --- Core 3D Setup ---
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x0a0a1a, 0.002);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x0a0a1a);
document.body.appendChild(renderer.domElement);
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
composer.addPass(bloomPass);
const ambientLight = new THREE.AmbientLight(0x404070, 0.5);
scene.add(ambientLight);
const sunLight = new THREE.DirectionalLight(0xffaa33, 1.5);
sunLight.position.set(100, 200, -500);
sunLight.castShadow = true;
scene.add(sunLight);
// --- Player ---
const playerGroup = new THREE.Group();
const craftBody = new THREE.Mesh(
new THREE.ConeGeometry(1, 4, 4),
new THREE.MeshPhongMaterial({ color: 0xcccccc, flatShading: true })
);
craftBody.rotation.x = Math.PI / 2;
playerGroup.add(craftBody);
const engineGlow = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 8, 8),
new THREE.MeshBasicMaterial({ color: 0x00ffff })
);
engineGlow.position.z = 2;
playerGroup.add(engineGlow);
scene.add(playerGroup);
// --- Procedural Generation ---
const chunks = [];
const obstacles = [];
const collectibles = [];
const particles = [];
const chunkGeo = new THREE.PlaneGeometry(200, CONFIG.CHUNK_SIZE, 10, 10);
const groundMat = new THREE.MeshPhongMaterial({
color: 0x1a1a2e,
flatShading: true,
shininess: 10
});
function createChunk(zOffset) {
const group = new THREE.Group();
// Ground
const ground = new THREE.Mesh(chunkGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.position.z = zOffset - CONFIG.CHUNK_SIZE/2;
group.add(ground);
// Walls & Obstacles
const wallMat = new THREE.MeshPhongMaterial({ color: 0x2a2a4e, flatShading: true });
for(let i = 0; i < 8; i++) {
const height = 20 + Math.random() * 60;
const width = 10 + Math.random() * 40;
const wall = new THREE.Mesh(new THREE.BoxGeometry(width, height, 20), wallMat);
const side = Math.random() > 0.5 ? 1 : -1;
wall.position.set(
side * (40 + Math.random() * 40),
height/2 - 5,
zOffset - Math.random() * CONFIG.CHUNK_SIZE
);
group.add(wall);
obstacles.push(wall);
}
// Energy orbs
for(let i = 0; i < 3; i++) {
const orb = new THREE.Mesh(
new THREE.IcosahedronGeometry(2, 0),
new THREE.MeshBasicMaterial({ color: 0x00ffff })
);
orb.position.set(
(Math.random() - 0.5) * 60,
2 + Math.random() * 10,
zOffset - Math.random() * CONFIG.CHUNK_SIZE
);
group.add(orb);
collectibles.push(orb);
}
scene.add(group);
chunks.push({ group, z: zOffset });
}
// Initial Chunks
for(let i = 0; i < CONFIG.CHUNK_DISTANCE; i++) {
createChunk(-i * CONFIG.CHUNK_SIZE);
}
// --- Particle System ---
function createTrail() {
const p = new THREE.Mesh(
new THREE.BoxGeometry(0.1, 0.1, 0.1),
new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true })
);
p.position.copy(playerGroup.position);
p.position.z += 2;
scene.add(p);
particles.push({ mesh: p, life: 1.0 });
}
// --- Input Handling ---
const handleKey = (e, val) => {
if(e.key === 'ArrowLeft' || e.key === 'a') state.input.left = val;
if(e.key === 'ArrowRight' || e.key === 'd') state.input.right = val;
if(e.key === 'ArrowUp' || e.key === 'w') state.input.up = val;
if(e.key === 'ArrowDown' || e.key === 's') state.input.down = val;
};
window.addEventListener('keydown', (e) => handleKey(e, true));
window.addEventListener('keyup', (e) => handleKey(e, false));
// --- Game Loop ---
function update(time) {
if(!state.playing) return;
const delta = (time - state.lastTime) / 1000;
state.lastTime = time;
// 1. Physics & Movement
const targetSpeed = state.input.up ? CONFIG.MAX_SPEED :
state.input.down ? CONFIG.BASE_SPEED :
(CONFIG.BASE_SPEED + (state.multiplier * 5));
state.currentSpeed = THREE.MathUtils.lerp(state.currentSpeed, targetSpeed, 0.02);
state.distanceTraveled += state.currentSpeed * delta;
// Steering
const steerDir = (state.input.left ? -1 : 0) + (state.input.right ? 1 : 0);
playerGroup.position.x += steerDir * CONFIG.STEER_SPEED * (state.currentSpeed / 50) * 10;
// Banking (Visual roll)
const targetRoll = -steerDir * 0.6;
playerGroup.rotation.z = THREE.MathUtils.lerp(playerGroup.rotation.z, targetRoll, CONFIG.BANK_SPEED);
playerGroup.rotation.y = THREE.MathUtils.lerp(playerGroup.rotation.y, -steerDir * 0.2, CONFIG.BANK_SPEED);
// Bounds
playerGroup.position.x = THREE.MathUtils.clamp(playerGroup.position.x, -80, 80);
// Position ship at fixed Z, walls move back?
// Simple approach: Ship actually moves in Z, camera follows.
playerGroup.position.z -= state.currentSpeed * delta;
// 2. Camera Follow
camera.position.lerp(new THREE.Vector3(
playerGroup.position.x * 0.5,
playerGroup.position.y + 4,
playerGroup.position.z + 12
), 0.1);
camera.lookAt(playerGroup.position.x * 0.2, playerGroup.position.y, playerGroup.position.z - 20);
// FOV effect based on speed
camera.fov = 75 + (state.currentSpeed / CONFIG.MAX_SPEED) * 20;
camera.updateProjectionMatrix();
// 3. Procedural Generation Cleanup & Expansion
if(playerGroup.position.z < chunks[0].z - CONFIG.CHUNK_SIZE) {
const oldChunk = chunks.shift();
scene.remove(oldChunk.group);
// Cleanup arrays
const currentZ = oldChunk.z;
for(let i = obstacles.length-1; i>=0; i--) {
if(obstacles[i].position.z > currentZ + 100) obstacles.splice(i, 1);
}
for(let i = collectibles.length-1; i>=0; i--) {
if(collectibles[i].position.z > currentZ + 100) collectibles.splice(i, 1);
}
const lastChunkZ = chunks[chunks.length-1].z;
createChunk(lastChunkZ - CONFIG.CHUNK_SIZE);
}
// 4. Collisions & Scoring
let nearMiss = false;
obstacles.forEach(obj => {
const dist = playerGroup.position.distanceTo(obj.position);
if(dist < CONFIG.COLLISION_DIST) {
gameOver();
} else if(dist < CONFIG.NEAR_MISS_DIST) {
nearMiss = true;
}
});
if(nearMiss) {
state.multiplier = Math.min(state.multiplier + 0.01, 10);
state.score += Math.floor(state.multiplier);
document.getElementById('multiplier').style.transform = 'scale(1.2)';
} else {
state.multiplier = THREE.MathUtils.lerp(state.multiplier, 1, 0.005);
document.getElementById('multiplier').style.transform = 'scale(1.0)';
}
// Collectibles
for(let i = collectibles.length-1; i>=0; i--) {
const orb = collectibles[i];
if(playerGroup.position.distanceTo(orb.position) < 6) {
state.energy = Math.min(state.energy + CONFIG.ENERGY_GAIN, 100);
AudioEngine.playPing(880, 'sine', 0.2);
// Remove from scene and array
orb.parent.remove(orb);
collectibles.splice(i, 1);
}
}
// Energy decay
state.energy -= CONFIG.ENERGY_DECAY * delta * 10;
if(state.energy <= 0) gameOver();
// Particles
if(Math.random() > 0.5) createTrail();
for(let i = particles.length-1; i>=0; i--) {
const p = particles[i];
p.life -= delta * 2;
p.mesh.scale.setScalar(p.life);
p.mesh.position.z += 0.5;
if(p.life <= 0) {
scene.remove(p.mesh);
particles.splice(i, 1);
}
}
// 5. UI Update
document.getElementById('score').innerText = Math.floor(state.score);
document.getElementById('multiplier').innerText = state.multiplier.toFixed(1) + 'x';
document.getElementById('speed').innerText = Math.floor(state.currentSpeed);
document.getElementById('energy-bar').style.width = state.energy + '%';
AudioEngine.updateEngine(state.currentSpeed);
composer.render();
requestAnimationFrame(update);
}
function gameOver() {
state.playing = false;
AudioEngine.playPing(110, 'sawtooth', 0.5);
document.getElementById('overlay').classList.remove('hidden');
document.getElementById('final-stats').innerHTML = `FINAL SCORE: ${Math.floor(state.score)}<br>MAX SPEED: ${Math.floor(state.currentSpeed)} km/h`;
}
window.startGame = function() {
// Reset state
state = {
score: 0,
multiplier: 1,
energy: 100,
currentSpeed: CONFIG.BASE_SPEED,
playing: true,
lastTime: performance.now(),
distanceTraveled: 0,
input: { left: false, right: false, up: false, down: false }
};
playerGroup.position.set(0, 0, 0);
// Clear previous chunks
chunks.forEach(c => scene.remove(c.group));
chunks.length = 0;
obstacles.length = 0;
collectibles.length = 0;
for(let i = 0; i < CONFIG.CHUNK_DISTANCE; i++) {
createChunk(-i * CONFIG.CHUNK_SIZE);
}
document.getElementById('overlay').classList.add('hidden');
AudioEngine.init();
requestAnimationFrame(update);
};
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"middle","sessionId":"076c6004-0800-468d-9dc8-e30fdcc78955","tokensIn":8867,"tokensOut":5640,"tokensTotal":14507,"turns":2,"toolCalls":1,"failedToolCalls":0,"timestamp":"2026-07-14T16:04:08.709Z"} -->