Canyon Run
Gemma 4 31b · composer

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 & 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"} -->