← Canyon Run results

Canyon Run

Gemma 4 31b · middle

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