Models 44

Claude Haiku 4.5

by AnthropicReasoningClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
16.9 s
Cost
$0.028
Output tokens
5,462
Spent thinking
0 tokens
Speed
340.9 tok/s
Console errors
0
First token after
828 ms
First visible text after
828 ms
Prompt tokens
524
Code size
17.8 KB
Finish reason
stop
Served by
Amazon Bedrock
Model id
anthropic/claude-haiku-4.5
Reasoning sent
{"effort":"none"}
Max output
32,000 tokens
Generated
Nov 10, 2026

What it was asked

The prompt
Build a polished Snake game in a single HTML file using vanilla JavaScript and a <canvas>. Requirements: - A grid of at least 20×20 cells, centered on the page. - Arrow keys and WASD control the snake. Quick successive key presses must both be honored, but the snake can never reverse onto itself. - Eating food adds 10 points and grows the snake by one cell; new food never spawns on the snake. - Game over when the snake hits a wall or itself, with an overlay showing the score; Space or Enter restarts, P pauses. - Visual polish: smooth movement, a snake with a distinct head and eyes facing its direction, and a small particle burst when eating. - Display the current score and the best score of the session.
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task.

Output rules:
- Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block.
- Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task.
- Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw.
- The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar.
- Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction.
- Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session.
- Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing.
- The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message.
- The code must run with zero errors in the console of the latest Chrome.

Code run-1.html · 566 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            font-family: 'Arial', sans-serif;
        }

        #gameContainer {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 20px;
        }

        #scoreBoard {
            display: flex;
            gap: 40px;
            color: white;
            font-size: 18px;
            font-weight: bold;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
        }

        #scoreBoard div {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        #scoreBoard .label {
            font-size: 14px;
            opacity: 0.9;
        }

        #scoreBoard .value {
            font-size: 28px;
            margin-top: 5px;
        }

        canvas {
            border: 4px solid white;
            background-color: #1a1a2e;
            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
            display: block;
            image-rendering: pixelated;
        }

        #controls {
            color: white;
            font-size: 14px;
            text-align: center;
            opacity: 0.8;
        }

        #gameOverlay {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.9);
            padding: 40px;
            border-radius: 10px;
            text-align: center;
            color: white;
            display: none;
            z-index: 1000;
            min-width: 300px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
        }

        #gameOverlay h1 {
            font-size: 48px;
            margin-bottom: 20px;
            color: #ff6b6b;
        }

        #gameOverlay .score {
            font-size: 24px;
            margin: 15px 0;
        }

        #gameOverlay .label {
            font-size: 14px;
            opacity: 0.8;
        }

        #gameOverlay .hint {
            font-size: 14px;
            margin-top: 25px;
            opacity: 0.7;
        }

        #pauseOverlay {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.9);
            padding: 40px;
            border-radius: 10px;
            text-align: center;
            color: white;
            display: none;
            z-index: 1000;
            min-width: 300px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
        }

        #pauseOverlay h1 {
            font-size: 48px;
            margin-bottom: 20px;
            color: #4ecdc4;
        }

        #pauseOverlay .hint {
            font-size: 14px;
            margin-top: 15px;
            opacity: 0.7;
        }

        #startOverlay {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.9);
            padding: 40px;
            border-radius: 10px;
            text-align: center;
            color: white;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 20px;
            z-index: 1000;
            min-width: 300px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
        }

        #startOverlay h1 {
            font-size: 48px;
            color: #ffd700;
        }

        #startOverlay .hint {
            font-size: 16px;
            opacity: 0.8;
            animation: pulse 1.5s infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 0.8; }
            50% { opacity: 1; }
        }

        @media (max-width: 768px) {
            #gameContainer {
                gap: 15px;
            }

            #scoreBoard {
                gap: 20px;
                font-size: 16px;
            }

            #scoreBoard .value {
                font-size: 24px;
            }

            canvas {
                border: 3px solid white;
            }

            #gameOverlay, #pauseOverlay, #startOverlay {
                padding: 30px;
                min-width: 250px;
            }

            #gameOverlay h1, #pauseOverlay h1, #startOverlay h1 {
                font-size: 36px;
            }
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <div id="scoreBoard">
            <div>
                <div class="label">Score</div>
                <div class="value" id="currentScore">0</div>
            </div>
            <div>
                <div class="label">Best</div>
                <div class="value" id="bestScore">0</div>
            </div>
        </div>
        <canvas id="gameCanvas"></canvas>
        <div id="controls">Arrow keys or WASD to move • P to pause • Space/Enter to restart</div>
    </div>

    <div id="startOverlay">
        <h1>🐍 SNAKE</h1>
        <div class="hint">Click or press a key to start</div>
    </div>

    <div id="gameOverlay">
        <h1>GAME OVER</h1>
        <div class="score">
            <div class="label">Final Score</div>
            <div id="finalScore">0</div>
        </div>
        <div class="hint">Press Space or Enter to restart</div>
    </div>

    <div id="pauseOverlay">
        <h1>PAUSED</h1>
        <div class="hint">Press P to resume</div>
    </div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');

        // Game constants
        const GRID_SIZE = 20;
        const CELL_SIZE = 30;
        const canvas_width = GRID_SIZE * CELL_SIZE;
        const canvas_height = GRID_SIZE * CELL_SIZE;
        canvas.width = canvas_width;
        canvas.height = canvas_height;

        // Game state
        let gameState = 'start'; // 'start', 'playing', 'paused', 'gameOver', 'aiDemo'
        let score = 0;
        let bestScore = 0;

        // Snake
        let snake = [{ x: 10, y: 10 }];
        let direction = { x: 1, y: 0 };
        let nextDirection = { x: 1, y: 0 };

        // Food
        let food = { x: 15, y: 15 };

        // Particles for food eating effect
        let particles = [];

        // Input queue for buffering key presses
        let inputQueue = [];

        // Game loop timing
        let lastMoveTime = 0;
        const MOVE_INTERVAL = 100; // ms

        // AI demo
        let aiMoveCounter = 0;

        // Initialize game
        function init() {
            snake = [{ x: 10, y: 10 }];
            direction = { x: 1, y: 0 };
            nextDirection = { x: 1, y: 0 };
            score = 0;
            particles = [];
            inputQueue = [];
            lastMoveTime = 0;
            aiMoveCounter = 0;
            spawnFood();
        }

        // Spawn food at random location not on snake
        function spawnFood() {
            let newFood;
            let valid = false;
            while (!valid) {
                newFood = {
                    x: Math.floor(Math.random() * GRID_SIZE),
                    y: Math.floor(Math.random() * GRID_SIZE)
                };
                valid = !snake.some(seg => seg.x === newFood.x && seg.y === newFood.y);
            }
            food = newFood;
        }

        // Handle keyboard input
        document.addEventListener('keydown', (e) => {
            const key = e.key.toLowerCase();

            if (gameState === 'start') {
                if (key === ' ' || key === 'enter' || ['arrowup', 'arrowdown', 'arrowleft', 'arrowright', 'w', 'a', 's', 'd'].includes(key)) {
                    e.preventDefault();
                    init();
                    gameState = 'playing';
                    document.getElementById('startOverlay').style.display = 'none';
                    lastMoveTime = Date.now();
                }
            } else if (gameState === 'gameOver') {
                if (key === ' ' || key === 'enter') {
                    e.preventDefault();
                    init();
                    gameState = 'playing';
                    document.getElementById('gameOverlay').style.display = 'none';
                    lastMoveTime = Date.now();
                }
            } else if (gameState === 'paused') {
                if (key === 'p') {
                    e.preventDefault();
                    gameState = 'playing';
                    document.getElementById('pauseOverlay').style.display = 'none';
                    lastMoveTime = Date.now();
                }
            } else if (gameState === 'playing' || gameState === 'aiDemo') {
                if (key === 'p') {
                    e.preventDefault();
                    gameState = 'paused';
                    document.getElementById('pauseOverlay').style.display = 'flex';
                } else if (key === ' ' || key === 'enter') {
                    e.preventDefault();
                    init();
                    gameState = 'playing';
                    document.getElementById('gameOverlay').style.display = 'none';
                } else {
                    handleMove(key);
                }
            }
        });

        // Handle move input
        function handleMove(key) {
            let newDir = null;
            if (key === 'arrowup' || key === 'w') newDir = { x: 0, y: -1 };
            else if (key === 'arrowdown' || key === 's') newDir = { x: 0, y: 1 };
            else if (key === 'arrowleft' || key === 'a') newDir = { x: -1, y: 0 };
            else if (key === 'arrowright' || key === 'd') newDir = { x: 1, y: 0 };

            if (newDir) {
                inputQueue.push(newDir);
            }
        }

        // Update game logic
        function update(currentTime) {
            if (gameState !== 'playing' && gameState !== 'aiDemo') return;

            // Process AI moves in demo mode
            if (gameState === 'aiDemo') {
                aiMoveCounter++;
                if (aiMoveCounter >= 2) {
                    aiMoveCounter = 0;
                    const head = snake[0];
                    const dist = {
                        up: Math.abs(food.x - head.x) + Math.abs(food.y - 1 - head.y),
                        down: Math.abs(food.x - head.x) + Math.abs(food.y + 1 - head.y),
                        left: Math.abs(food.x - 1 - head.x) + Math.abs(food.y - head.y),
                        right: Math.abs(food.x + 1 - head.x) + Math.abs(food.y - head.y)
                    };
                    const minDist = Math.min(dist.up, dist.down, dist.left, dist.right);
                    if (minDist === dist.up && direction.y === 0) nextDirection = { x: 0, y: -1 };
                    else if (minDist === dist.down && direction.y === 0) nextDirection = { x: 0, y: 1 };
                    else if (minDist === dist.left && direction.x === 0) nextDirection = { x: -1, y: 0 };
                    else if (minDist === dist.right && direction.x === 0) nextDirection = { x: 1, y: 0 };
                }
            } else {
                // Process input queue
                if (inputQueue.length > 0) {
                    const newDir = inputQueue.shift();
                    // Prevent reversing
                    if (!(newDir.x === -direction.x && newDir.y === -direction.y)) {
                        nextDirection = newDir;
                    }
                }
            }

            // Move snake
            if (currentTime - lastMoveTime > MOVE_INTERVAL) {
                direction = nextDirection;
                const head = snake[0];
                const newHead = {
                    x: head.x + direction.x,
                    y: head.y + direction.y
                };

                // Check wall collision
                if (newHead.x < 0 || newHead.x >= GRID_SIZE || newHead.y < 0 || newHead.y >= GRID_SIZE) {
                    endGame();
                    return;
                }

                // Check self collision
                if (snake.some(seg => seg.x === newHead.x && seg.y === newHead.y)) {
                    endGame();
                    return;
                }

                snake.unshift(newHead);

                // Check food collision
                if (newHead.x === food.x && newHead.y === food.y) {
                    score += 10;
                    document.getElementById('currentScore').textContent = score;
                    if (score > bestScore) {
                        bestScore = score;
                        document.getElementById('bestScore').textContent = bestScore;
                    }
                    createParticles(food.x * CELL_SIZE + CELL_SIZE / 2, food.y * CELL_SIZE + CELL_SIZE / 2);
                    spawnFood();
                } else {
                    snake.pop();
                }

                lastMoveTime = currentTime;
            }

            // Update particles
            particles = particles.filter(p => {
                p.life -= 0.02;
                return p.life > 0;
            });
        }

        // Create particle burst
        function createParticles(x, y) {
            for (let i = 0; i < 8; i++) {
                const angle = (Math.PI * 2 * i) / 8;
                particles.push({
                    x: x,
                    y: y,
                    vx: Math.cos(angle) * 3,
                    vy: Math.sin(angle) * 3,
                    life: 1
                });
            }
        }

        // End game
        function endGame() {
            gameState = 'gameOver';
            document.getElementById('finalScore').textContent = score;
            document.getElementById('gameOverlay').style.display = 'flex';
        }

        // Draw game
        function draw() {
            // Clear canvas
            ctx.fillStyle = '#1a1a2e';
            ctx.fillRect(0, 0, canvas_width, canvas_height);

            // Draw grid (faint)
            ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
            ctx.lineWidth = 1;
            for (let i = 0; i <= GRID_SIZE; i++) {
                ctx.beginPath();
                ctx.moveTo(i * CELL_SIZE, 0);
                ctx.lineTo(i * CELL_SIZE, canvas_height);
                ctx.stroke();

                ctx.beginPath();
                ctx.moveTo(0, i * CELL_SIZE);
                ctx.lineTo(canvas_width, i * CELL_SIZE);
                ctx.stroke();
            }

            // Draw food
            ctx.fillStyle = '#ff6b6b';
            ctx.beginPath();
            ctx.arc(food.x * CELL_SIZE + CELL_SIZE / 2, food.y * CELL_SIZE + CELL_SIZE / 2, CELL_SIZE / 2 - 2, 0, Math.PI * 2);
            ctx.fill();
            ctx.strokeStyle = '#ff4444';
            ctx.lineWidth = 2;
            ctx.stroke();

            // Draw snake
            snake.forEach((seg, index) => {
                const x = seg.x * CELL_SIZE;
                const y = seg.y * CELL_SIZE;

                if (index === 0) {
                    // Draw head
                    ctx.fillStyle = '#4ecdc4';
                    ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, CELL_SIZE - 2);

                    // Draw eyes
                    ctx.fillStyle = '#ffffff';
                    const eyeSize = 4;
                    const eyeOffset = 5;
                    let eyeX1, eyeY1, eyeX2, eyeY2;

                    if (direction.x === 1) { // Right
                        eyeX1 = x + CELL_SIZE - eyeOffset;
                        eyeY1 = y + eyeOffset;
                        eyeX2 = x + CELL_SIZE - eyeOffset;
                        eyeY2 = y + CELL_SIZE - eyeOffset;
                    } else if (direction.x === -1) { // Left
                        eyeX1 = x + eyeOffset;
                        eyeY1 = y + eyeOffset;
                        eyeX2 = x + eyeOffset;
                        eyeY2 = y + CELL_SIZE - eyeOffset;
                    } else if (direction.y === -1) { // Up
                        eyeX1 = x + eyeOffset;
                        eyeY1 = y + eyeOffset;
                        eyeX2 = x + CELL_SIZE - eyeOffset;
                        eyeY2 = y + eyeOffset;
                    } else { // Down
                        eyeX1 = x + eyeOffset;
                        eyeY1 = y + CELL_SIZE - eyeOffset;
                        eyeX2 = x + CELL_SIZE - eyeOffset;
                        eyeY2 = y + CELL_SIZE - eyeOffset;
                    }

                    ctx.fillRect(eyeX1 - eyeSize / 2, eyeY1 - eyeSize / 2, eyeSize, eyeSize);
                    ctx.fillRect(eyeX2 - eyeSize / 2, eyeY2 - eyeSize / 2, eyeSize, eyeSize);
                } else {
                    // Draw body
                    ctx.fillStyle = index === 1 ? '#45b7aa' : '#3a9e92';
                    ctx.fillRect(x + 2, y + 2, CELL_SIZE - 4, CELL_SIZE - 4);
                    ctx.strokeStyle = '#2a7e72';
                    ctx.lineWidth = 1;
                    ctx.strokeRect(x + 2, y + 2, CELL_SIZE - 4, CELL_SIZE - 4);
                }
            });

            // Draw particles
            particles.forEach(p => {
                p.x += p.vx;
                p.y += p.vy;
                ctx.fillStyle = `rgba(255, 107, 107, ${p.life * 0.7})`;
                const size = 3 * p.life;
                ctx.fillRect(p.x - size / 2, p.y - size / 2, size, size);
            });
        }

        // Game loop
        function gameLoop(currentTime) {
            update(currentTime);
            draw();
            requestAnimationFrame(gameLoop);
        }

        // Start AI demo
        function startAIDemo() {
            gameState = 'aiDemo';
            init();
            lastMoveTime = Date.now();
        }

        // Initialize
        startAIDemo();
        requestAnimationFrame(gameLoop);
    </script>
</body>
</html>