← Canyon Run results

Canyon Run

Gemma 4 31b · basic

Initial view of Canyon Run

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