Models 44

DeepSeek V4 Pro 0813

by DeepSeekreasoning defaultClean
Open in new window

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

How it went

Total time
5 min 2 s
Cost
$0.084
Output tokens
18,902
Spent thinking
8,856 tokens
Speed
62.7 tok/s
Console errors
0
First token after
657 ms
First visible text after
2 min 7 s
Prompt tokens
359
Code size
38.1 KB
Finish reason
stop
Served by
Wafer
Model id
deepseek/deepseek-v4-pro-0813
Reasoning sent
model default
Max output
943,718 tokens
Generated
Nov 12, 2026

What it was asked

The prompt
“i want a breakout game where the paddle is a hamburger and every brick you break explodes into tons of little hearts”
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 · 967 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Heartbreak Burger - Breakout Game</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            background: linear-gradient(135deg, #fce4ec 0%, #f8bbd0 30%, #fce4ec 60%, #f3e5f5 100%);
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            overflow: hidden;
            font-family: 'Segoe UI', 'Arial', sans-serif;
            -webkit-user-select: none;
            user-select: none;
            -webkit-tap-highlight-color: transparent;
        }
        #gameContainer {
            position: relative;
            width: 100%;
            max-width: 800px;
            aspect-ratio: 4 / 3;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(255, 105, 135, 0.3), 0 8px 24px rgba(0, 0, 0, 0.12);
            overflow: hidden;
            background: #fff5f7;
        }
        canvas {
            display: block;
            width: 100%;
            height: 100%;
            touch-action: none;
            cursor: pointer;
        }
        #overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            pointer-events: auto;
            cursor: pointer;
            z-index: 10;
            background: rgba(255, 245, 247, 0.75);
            backdrop-filter: blur(4px);
            -webkit-backdrop-filter: blur(4px);
            transition: opacity 0.4s ease;
        }
        #overlay.hidden {
            opacity: 0;
            pointer-events: none;
        }
        #overlay .message {
            font-size: clamp(1.2rem, 3.5vw, 2rem);
            font-weight: 700;
            color: #d6336c;
            text-align: center;
            padding: 0 20px;
            letter-spacing: 0.5px;
            text-shadow: 0 2px 8px rgba(214, 51, 108, 0.2);
            animation: pulse 1.6s ease-in-out infinite;
        }
        #overlay .heart-icon {
            font-size: clamp(3rem, 7vw, 5rem);
            margin-bottom: 12px;
            animation: bounce 1.2s ease-in-out infinite;
        }
        @keyframes pulse {
            0%,
            100% {
                transform: scale(1);
                opacity: 1;
            }
            50% {
                transform: scale(1.04);
                opacity: 0.9;
            }
        }
        @keyframes bounce {
            0%,
            100% {
                transform: translateY(0) scale(1);
            }
            50% {
                transform: translateY(-15px) scale(1.08);
            }
        }
        #gameOverlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            pointer-events: none;
            z-index: 5;
            opacity: 0;
            transition: opacity 0.5s ease;
        }
        #gameOverlay.visible {
            opacity: 1;
            pointer-events: auto;
        }
        #gameOverlay .msg {
            font-size: clamp(1.5rem, 4vw, 2.5rem);
            font-weight: 800;
            color: #d6336c;
            text-align: center;
            text-shadow: 0 3px 12px rgba(214, 51, 108, 0.3);
        }
        @media (max-width: 500px) {
            #gameContainer {
                border-radius: 10px;
            }
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <canvas id="gameCanvas" width="800" height="600"></canvas>
        <div id="overlay">
            <div class="heart-icon">💖</div>
            <div class="message">Click or press a key to start</div>
        </div>
        <div id="gameOverlay">
            <div class="msg" id="gameOverMsg"></div>
        </div>
    </div>

    <script>
        (function() {
            const canvas = document.getElementById('gameCanvas');
            const ctx = canvas.getContext('2d');
            const overlay = document.getElementById('overlay');
            const gameOverlayEl = document.getElementById('gameOverlay');
            const gameOverMsg = document.getElementById('gameOverMsg');

            const W = 800,
                H = 600;
            const PADDLE_WIDTH = 110;
            const PADDLE_HEIGHT = 52;
            const PADDLE_Y = H - 50;
            const BALL_RADIUS = 8;
            const BRICK_ROWS = 5;
            const BRICK_COLS = 10;
            const BRICK_WIDTH = 62;
            const BRICK_HEIGHT = 22;
            const BRICK_GAP = 6;
            const BRICK_TOP = 60;
            const BRICKS_TOTAL_WIDTH = BRICK_COLS * BRICK_WIDTH + (BRICK_COLS - 1) * BRICK_GAP;
            const BRICKS_OFFSET_X = (W - BRICKS_TOTAL_WIDTH) / 2;

            const BN_COLORS = [
                ['#FF6B8A', '#FF8FA3'],
                ['#FF4777', '#FF6B8A'],
                ['#FF8FA3', '#FFB3C1'],
                ['#E84393', '#FF4777'],
                ['#FFB3C1', '#FFD4DC'],
            ];

            const HEART_COLORS = [
                '#FF4777', '#FF3355', '#FF6B8A', '#FF8FA3', '#FFB3C1',
                '#E84393', '#FF2E63', '#FF5C8A', '#FF7EB3', '#FF4D6D'
            ];

            let gameState = 'demo';
            let score = 0;
            let lives = 3;
            let ball = { x: W / 2, y: PADDLE_Y - BALL_RADIUS - 2, vx: 3.8, vy: -4.6, radius: BALL_RADIUS };
            let paddle = { x: W / 2 - PADDLE_WIDTH / 2, y: PADDLE_Y, width: PADDLE_WIDTH, height: PADDLE_HEIGHT };
            let bricks = [];
            let hearts = [];
            let ballTrail = [];
            let bgHearts = [];
            let keys = {};
            let mouseX = W / 2;
            let lastTime = 0;
            let aiMomentum = 0;
            let ballLaunched = false;
            let launchDelay = 0;
            let gameOverShown = false;
            let demoRestartTimer = 0;

            function initBricks() {
                bricks = [];
                for (let r = 0; r < BRICK_ROWS; r++) {
                    for (let c = 0; c < BRICK_COLS; c++) {
                        const x = BRICKS_OFFSET_X + c * (BRICK_WIDTH + BRICK_GAP);
                        const y = BRICK_TOP + r * (BRICK_HEIGHT + BRICK_GAP);
                        bricks.push({
                            x,
                            y,
                            w: BRICK_WIDTH,
                            h: BRICK_HEIGHT,
                            row: r,
                            col: c,
                            alive: true,
                            colorTop: BN_COLORS[r][0],
                            colorBottom: BN_COLORS[r][1]
                        });
                    }
                }
            }

            function initBgHearts() {
                bgHearts = [];
                for (let i = 0; i < 18; i++) {
                    bgHearts.push({
                        x: Math.random() * W,
                        y: Math.random() * H,
                        size: 10 + Math.random() * 25,
                        alpha: 0.06 + Math.random() * 0.12,
                        speedY: 0.15 + Math.random() * 0.4,
                        speedX: (Math.random() - 0.5) * 0.25,
                        rotation: Math.random() * Math.PI * 2,
                        rotSpeed: (Math.random() - 0.5) * 0.01,
                        driftPhase: Math.random() * Math.PI * 2,
                        color: HEART_COLORS[Math.floor(Math.random() * HEART_COLORS.length)]
                    });
                }
            }

            function resetBall() {
                ball.x = paddle.x + paddle.width / 2;
                ball.y = PADDLE_Y - BALL_RADIUS - 2;
                ball.vx = (Math.random() > 0.5 ? 1 : -1) * (3.2 + Math.random() * 0.8);
                ball.vy = -(4.2 + Math.random() * 0.8);
                ballLaunched = false;
                launchDelay = 40 + Math.floor(Math.random() * 20);
                ballTrail = [];
            }

            function resetGame() {
                score = 0;
                lives = 3;
                initBricks();
                initBgHearts();
                hearts = [];
                ballTrail = [];
                paddle.x = W / 2 - PADDLE_WIDTH / 2;
                mouseX = W / 2;
                resetBall();
                gameOverShown = false;
                gameOverlayEl.classList.remove('visible');
                demoRestartTimer = 0;
            }

            function spawnHeartExplosion(bx, by) {
                const count = 18 + Math.floor(Math.random() * 12);
                for (let i = 0; i < count; i++) {
                    const angle = Math.random() * Math.PI * 2;
                    const speed = 1.5 + Math.random() * 5.5;
                    const size = 2.5 + Math.random() * 6;
                    hearts.push({
                        x: bx + (Math.random() - 0.5) * BRICK_WIDTH * 0.7,
                        y: by + (Math.random() - 0.5) * BRICK_HEIGHT * 0.7,
                        vx: Math.cos(angle) * speed * (0.7 + Math.random() * 0.6),
                        vy: Math.sin(angle) * speed * (0.7 + Math.random() * 0.6) - 1.2 - Math.random() * 2,
                        size: size,
                        rotation: Math.random() * Math.PI * 2,
                        rotSpeed: (Math.random() - 0.5) * 0.25,
                        alpha: 1.0,
                        fadeSpeed: 0.008 + Math.random() * 0.014,
                        gravity: 0.08 + Math.random() * 0.12,
                        color: HEART_COLORS[Math.floor(Math.random() * HEART_COLORS.length)]
                    });
                }
            }

            function drawHeartShape(cx, cy, size, rotation, alpha, color) {
                ctx.save();
                ctx.translate(cx, cy);
                ctx.rotate(rotation);
                ctx.globalAlpha = alpha;
                const s = size;
                ctx.fillStyle = color;
                ctx.beginPath();
                ctx.moveTo(0, s * 0.35);
                ctx.bezierCurveTo(-s * 0.65, -s * 0.12, -s * 0.42, -s * 0.72, 0, -s * 0.22);
                ctx.bezierCurveTo(s * 0.42, -s * 0.72, s * 0.65, -s * 0.12, 0, s * 0.35);
                ctx.fill();
                ctx.restore();
            }

            function drawBackgroundHeart(h) {
                drawHeartShape(h.x, h.y, h.size, h.rotation, h.alpha, h.color);
            }

            function drawHamburger(px, py, w, h) {
                const cx = px + w / 2;
                const topY = py;
                const bottomY = py + h;
                const bunWidth = w;
                const bunHeight = 16;
                const pattyWidth = w * 0.82;
                const pattyHeight = 9;
                const cheeseWidth = w * 0.92;
                const cheeseHeight = 7;
                const lettuceWidth = w * 1.0;
                const lettuceHeight = 8;

                // Bottom bun (rounded)
                const bbGrad = ctx.createLinearGradient(cx - bunWidth / 2, topY + 14, cx + bunWidth / 2, topY +
                    14);
                bbGrad.addColorStop(0, '#C88E4A');
                bbGrad.addColorStop(0.35, '#E0A85C');
                bbGrad.addColorStop(0.65, '#D49A50');
                bbGrad.addColorStop(1, '#B87A38');
                ctx.fillStyle = bbGrad;
                ctx.beginPath();
                const bbY = topY + 28;
                const bbR = 7;
                ctx.moveTo(cx - bunWidth / 2 + bbR, bbY);
                ctx.lineTo(cx + bunWidth / 2 - bbR, bbY);
                ctx.quadraticCurveTo(cx + bunWidth / 2, bbY, cx + bunWidth / 2, bbY - bbR);
                ctx.lineTo(cx + bunWidth / 2, bbY - 14 + bbR);
                ctx.quadraticCurveTo(cx + bunWidth / 2, bbY - 14, cx + bunWidth / 2 - bbR, bbY - 14);
                ctx.lineTo(cx - bunWidth / 2 + bbR, bbY - 14);
                ctx.quadraticCurveTo(cx - bunWidth / 2, bbY - 14, cx - bunWidth / 2, bbY - 14 + bbR);
                ctx.lineTo(cx - bunWidth / 2, bbY - bbR);
                ctx.quadraticCurveTo(cx - bunWidth / 2, bbY, cx - bunWidth / 2 + bbR, bbY);
                ctx.fill();

                // Bottom bun highlight
                ctx.fillStyle = 'rgba(255,230,180,0.3)';
                ctx.fillRect(cx - bunWidth / 2 + 4, bbY - 12, bunWidth - 8, 4);

                // Patty
                const pattyGrad = ctx.createLinearGradient(cx - pattyWidth / 2, topY + 14, cx + pattyWidth / 2, topY +
                    14);
                pattyGrad.addColorStop(0, '#4A2C12');
                pattyGrad.addColorStop(0.4, '#6B3F1F');
                pattyGrad.addColorStop(0.6, '#7A4A28');
                pattyGrad.addColorStop(1, '#3E2510');
                ctx.fillStyle = pattyGrad;
                const pyTop = topY + 18;
                const pyH = pattyHeight;
                ctx.beginPath();
                ctx.moveTo(cx - pattyWidth / 2 + 5, pyTop + pyH);
                ctx.lineTo(cx + pattyWidth / 2 - 5, pyTop + pyH);
                ctx.quadraticCurveTo(cx + pattyWidth / 2, pyTop + pyH, cx + pattyWidth / 2, pyTop + pyH - 4);
                ctx.lineTo(cx + pattyWidth / 2, pyTop + 3);
                ctx.quadraticCurveTo(cx + pattyWidth / 2, pyTop, cx + pattyWidth / 2 - 4, pyTop);
                ctx.lineTo(cx - pattyWidth / 2 + 4, pyTop);
                ctx.quadraticCurveTo(cx - pattyWidth / 2, pyTop, cx - pattyWidth / 2, pyTop + 3);
                ctx.lineTo(cx - pattyWidth / 2, pyTop + pyH - 4);
                ctx.quadraticCurveTo(cx - pattyWidth / 2, pyTop + pyH, cx - pattyWidth / 2 + 5, pyTop + pyH);
                ctx.fill();

                // Patty texture lines
                ctx.strokeStyle = 'rgba(40,20,5,0.3)';
                ctx.lineWidth = 1;
                for (let i = 0; i < 3; i++) {
                    const ly = pyTop + 2 + i * 2.5;
                    ctx.beginPath();
                    ctx.moveTo(cx - pattyWidth / 2 + 7, ly);
                    ctx.lineTo(cx + pattyWidth / 2 - 7, ly + (i % 2 === 0 ? 1 : -0.5));
                    ctx.stroke();
                }

                // Cheese
                const cheeseGrad = ctx.createLinearGradient(cx - cheeseWidth / 2, topY + 12, cx + cheeseWidth / 2, topY +
                    12);
                cheeseGrad.addColorStop(0, '#FFC840');
                cheeseGrad.addColorStop(0.3, '#FFD866');
                cheeseGrad.addColorStop(0.7, '#FFD866');
                cheeseGrad.addColorStop(1, '#FFB830');
                ctx.fillStyle = cheeseGrad;
                const chTop = topY + 10;
                const chH = cheeseHeight;
                ctx.beginPath();
                ctx.moveTo(cx - cheeseWidth / 2, chTop + chH);
                ctx.lineTo(cx - cheeseWidth / 2 + 2, chTop + 2);
                ctx.lineTo(cx - 15, chTop);
                ctx.lineTo(cx + 10, chTop);
                ctx.lineTo(cx + cheeseWidth / 2 - 3, chTop + 1);
                ctx.lineTo(cx + cheeseWidth / 2, chTop + chH);
                ctx.closePath();
                ctx.fill();

                // Cheese drip
                ctx.fillStyle = '#FFC840';
                ctx.beginPath();
                ctx.moveTo(cx + 22, chTop + chH);
                ctx.lineTo(cx + 36, chTop + chH + 5);
                ctx.lineTo(cx + 42, chTop + chH);
                ctx.closePath();
                ctx.fill();

                ctx.fillStyle = '#FFD866';
                ctx.beginPath();
                ctx.moveTo(cx - 30, chTop + chH);
                ctx.lineTo(cx - 20, chTop + chH + 3);
                ctx.lineTo(cx - 14, chTop + chH);
                ctx.closePath();
                ctx.fill();

                // Lettuce (wavy green ribbon)
                const lettuceGrad = ctx.createLinearGradient(cx - lettuceWidth / 2, topY + 6, cx + lettuceWidth / 2,
                    topY + 6);
                lettuceGrad.addColorStop(0, '#4CAF50');
                lettuceGrad.addColorStop(0.3, '#66BB6A');
                lettuceGrad.addColorStop(0.6, '#5CB85C');
                lettuceGrad.addColorStop(1, '#3D8B40');
                ctx.fillStyle = lettuceGrad;
                const ltTop = topY + 2;
                const ltH = lettuceHeight;
                ctx.beginPath();
                ctx.moveTo(cx - lettuceWidth / 2, ltTop + ltH);
                for (let i = 0; i < 8; i++) {
                    const lx = cx - lettuceWidth / 2 + (i / 7) * lettuceWidth;
                    const ly = ltTop + Math.sin(i * 1.2 + 0.5) * 3;
                    if (i === 0) {
                        ctx.lineTo(lx, ly - 2);
                    } else {
                        ctx.quadraticCurveTo(lx - 7, ly - 5, lx, ly);
                    }
                }
                ctx.lineTo(cx + lettuceWidth / 2, ltTop + ltH);
                ctx.closePath();
                ctx.fill();

                // Lettuce frill edges
                ctx.strokeStyle = '#4CAF50';
                ctx.lineWidth = 1.5;
                ctx.beginPath();
                for (let i = 0; i < 10; i++) {
                    const lx = cx - lettuceWidth / 2 + (i / 9) * lettuceWidth;
                    const ly = ltTop - 1 + Math.sin(i * 0.9 + 0.3) * 2;
                    if (i === 0) ctx.moveTo(lx, ly);
                    else ctx.lineTo(lx, ly);
                }
                ctx.stroke();

                // Top bun (dome)
                const tbGrad = ctx.createLinearGradient(cx - bunWidth / 2, topY - 8, cx + bunWidth / 2, topY + 2);
                tbGrad.addColorStop(0, '#D4A050');
                tbGrad.addColorStop(0.3, '#E8B866');
                tbGrad.addColorStop(0.6, '#DCA858');
                tbGrad.addColorStop(1, '#C89040');
                ctx.fillStyle = tbGrad;
                const tbTop = topY - 14;
                const tbBottom = topY + 2;
                const tbH = tbBottom - tbTop;
                ctx.beginPath();
                ctx.moveTo(cx - bunWidth / 2, tbBottom);
                ctx.quadraticCurveTo(cx - bunWidth / 2, tbTop + tbH * 0.3, cx - bunWidth / 2 + 8, tbTop + 4);
                ctx.quadraticCurveTo(cx - bunWidth / 4, tbTop - 2, cx, tbTop);
                ctx.quadraticCurveTo(cx + bunWidth / 4, tbTop - 2, cx + bunWidth / 2 - 8, tbTop + 4);
                ctx.quadraticCurveTo(cx + bunWidth / 2, tbTop + tbH * 0.3, cx + bunWidth / 2, tbBottom);
                ctx.closePath();
                ctx.fill();

                // Top bun shine
                ctx.fillStyle = 'rgba(255,240,200,0.35)';
                ctx.beginPath();
                ctx.ellipse(cx - 12, tbTop + 3, 15, 5, -0.2, 0, Math.PI * 2);
                ctx.fill();
                ctx.fillStyle = 'rgba(255,240,200,0.2)';
                ctx.beginPath();
                ctx.ellipse(cx + 8, tbTop + 2, 10, 4, 0.15, 0, Math.PI * 2);
                ctx.fill();

                // Sesame seeds
                const seedPositions = [
                    { dx: -0.28, dy: 0.15, rot: 0.3, w: 5, h: 2.5 },
                    { dx: -0.12, dy: -0.05, rot: -0.2, w: 5.5, h: 2.5 },
                    { dx: 0.1, dy: -0.18, rot: 0.4, w: 5, h: 2.2 },
                    { dx: 0.3, dy: 0.05, rot: -0.35, w: 5.5, h: 2.5 },
                    { dx: 0.45, dy: 0.22, rot: 0.25, w: 4.5, h: 2.2 },
                    { dx: 0.05, dy: 0.22, rot: 0.1, w: 4, h: 2 },
                    { dx: -0.42, dy: 0.3, rot: -0.15, w: 4.5, h: 2 },
                    { dx: 0.2, dy: -0.3, rot: 0.5, w: 4, h: 2 },
                ];
                seedPositions.forEach(seed => {
                    const sx = cx + seed.dx * bunWidth;
                    const sy = tbTop + seed.dy * tbH + 4;
                    ctx.save();
                    ctx.translate(sx, sy);
                    ctx.rotate(seed.rot);
                    ctx.fillStyle = '#FFF5E0';
                    ctx.beginPath();
                    ctx.ellipse(0, 0, seed.w / 2, seed.h / 2, 0, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.strokeStyle = 'rgba(200,160,100,0.4)';
                    ctx.lineWidth = 0.5;
                    ctx.stroke();
                    ctx.restore();
                });
            }

            function drawBall() {
                const trailCount = ballTrail.length;
                for (let i = 0; i < trailCount; i++) {
                    const t = ballTrail[i];
                    const alpha = (i / trailCount) * 0.5;
                    const radius = ball.radius * (0.4 + (i / trailCount) * 0.6);
                    ctx.fillStyle = `rgba(255, 120, 160, ${alpha})`;
                    ctx.beginPath();
                    ctx.arc(t.x, t.y, radius, 0, Math.PI * 2);
                    ctx.fill();
                }

                const grad = ctx.createRadialGradient(ball.x - 2, ball.y - 2, 2, ball.x, ball.y, ball.radius + 4);
                grad.addColorStop(0, '#FFE0E8');
                grad.addColorStop(0.5, '#FF6B8A');
                grad.addColorStop(1, '#E84393');
                ctx.fillStyle = grad;
                ctx.beginPath();
                ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
                ctx.fill();

                ctx.fillStyle = 'rgba(255,255,255,0.7)';
                ctx.beginPath();
                ctx.arc(ball.x - 2.5, ball.y - 2.5, 2.5, 0, Math.PI * 2);
                ctx.fill();
            }

            function drawBricks() {
                bricks.forEach(b => {
                    if (!b.alive) return;
                    const grad = ctx.createLinearGradient(b.x, b.y, b.x, b.y + b.h);
                    grad.addColorStop(0, b.colorTop);
                    grad.addColorStop(1, b.colorBottom);
                    ctx.fillStyle = grad;
                    const r = 5;
                    ctx.beginPath();
                    ctx.moveTo(b.x + r, b.y);
                    ctx.lineTo(b.x + b.w - r, b.y);
                    ctx.quadraticCurveTo(b.x + b.w, b.y, b.x + b.w, b.y + r);
                    ctx.lineTo(b.x + b.w, b.y + b.h - r);
                    ctx.quadraticCurveTo(b.x + b.w, b.y + b.h, b.x + b.w - r, b.y + b.h);
                    ctx.lineTo(b.x + r, b.y + b.h);
                    ctx.quadraticCurveTo(b.x, b.y + b.h, b.x, b.y + b.h - r);
                    ctx.lineTo(b.x, b.y + r);
                    ctx.quadraticCurveTo(b.x, b.y, b.x + r, b.y);
                    ctx.fill();

                    ctx.fillStyle = 'rgba(255,255,255,0.25)';
                    ctx.fillRect(b.x + 3, b.y + 2, b.w - 6, 5);

                    ctx.fillStyle = 'rgba(0,0,0,0.08)';
                    ctx.fillRect(b.x + 3, b.y + b.h - 4, b.w - 6, 2);
                });
            }

            function drawHeartsParticles() {
                hearts.forEach(h => {
                    drawHeartShape(h.x, h.y, h.size, h.rotation, h.alpha, h.color);
                });
            }

            function drawBgHearts() {
                bgHearts.forEach(h => {
                    drawBackgroundHeart(h);
                });
            }

            function drawUI() {
                // Score
                ctx.fillStyle = 'rgba(255,255,255,0.85)';
                ctx.beginPath();
                ctx.roundRect(W - 170, 10, 155, 40, 20);
                ctx.fill();

                ctx.fillStyle = '#d6336c';
                ctx.font = 'bold 17px "Segoe UI", Arial, sans-serif';
                ctx.textAlign = 'right';
                ctx.fillText('Score: ' + score, W - 30, 38);

                // Lives
                ctx.fillStyle = 'rgba(255,255,255,0.85)';
                ctx.beginPath();
                ctx.roundRect(15, 10, 55 + lives * 22, 40, 20);
                ctx.fill();

                for (let i = 0; i < lives; i++) {
                    drawHeartShape(45 + i * 22, 31, 7, 0, 1, '#FF4777');
                }

                // Tiny decorative text
                ctx.fillStyle = 'rgba(214,51,108,0.6)';
                ctx.font = '12px "Segoe UI", Arial, sans-serif';
                ctx.textAlign = 'center';
                ctx.fillText('💖 Heartbreak Burger Breakout 💖', W / 2, 28);
            }

            function updateBgHearts() {
                bgHearts.forEach(h => {
                    h.y += h.speedY;
                    h.x += h.speedX + Math.sin(h.driftPhase + h.y * 0.01) * 0.15;
                    h.rotation += h.rotSpeed;
                    if (h.y > H + 30) {
                        h.y = -30;
                        h.x = Math.random() * W;
                    }
                    if (h.x > W + 30) h.x = -30;
                    if (h.x < -30) h.x = W + 30;
                });
            }

            function updateHearts() {
                for (let i = hearts.length - 1; i >= 0; i--) {
                    const h = hearts[i];
                    h.x += h.vx;
                    h.y += h.vy;
                    h.vy += h.gravity;
                    h.vx *= 0.995;
                    h.rotation += h.rotSpeed;
                    h.alpha -= h.fadeSpeed;
                    if (h.alpha <= 0 || h.y > H + 40 || h.x < -50 || h.x > W + 50) {
                        hearts.splice(i, 1);
                    }
                }
            }

            function updateBallTrail() {
                ballTrail.push({ x: ball.x, y: ball.y });
                if (ballTrail.length > 14) {
                    ballTrail.shift();
                }
            }

            function updateBall() {
                if (!ballLaunched) {
                    ball.x = paddle.x + paddle.width / 2;
                    ball.y = PADDLE_Y - BALL_RADIUS - 2;
                    launchDelay--;
                    if (launchDelay <= 0) {
                        ballLaunched = true;
                        const angle = Math.PI * 0.65 + Math.random() * Math.PI * 0.2;
                        const speed = 4.5 + Math.random() * 0.8;
                        ball.vx = Math.cos(angle) * speed * (Math.random() > 0.5 ? 1 : -1);
                        ball.vy = -Math.sin(angle) * speed;
                    }
                    updateBallTrail();
                    return;
                }

                ball.x += ball.vx;
                ball.y += ball.vy;

                // Wall collisions
                if (ball.x - ball.radius <= 0) {
                    ball.x = ball.radius;
                    ball.vx = Math.abs(ball.vx);
                }
                if (ball.x + ball.radius >= W) {
                    ball.x = W - ball.radius;
                    ball.vx = -Math.abs(ball.vx);
                }
                if (ball.y - ball.radius <= 0) {
                    ball.y = ball.radius;
                    ball.vy = Math.abs(ball.vy);
                }

                // Paddle collision
                const paddleTop = PADDLE_Y - PADDLE_HEIGHT + 16;
                const paddleBottom = PADDLE_Y + PADDLE_HEIGHT;
                if (ball.vy > 0 &&
                    ball.y + ball.radius >= paddleTop &&
                    ball.y - ball.radius <= paddleBottom &&
                    ball.x >= paddle.x - 4 &&
                    ball.x <= paddle.x + paddle.width + 4) {
                    const hitX = (ball.x - (paddle.x + paddle.width / 2)) / (paddle.width / 2);
                    const angle = hitX * 0.7;
                    const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
                    const newSpeed = Math.min(speed + 0.15, 6.5);
                    ball.vx = Math.sin(angle) * newSpeed;
                    ball.vy = -Math.abs(Math.cos(angle) * newSpeed);
                    ball.y = paddleTop - ball.radius;
                }

                // Brick collisions
                if (ball.vy < 0) {
                    for (let i = 0; i < bricks.length; i++) {
                        const b = bricks[i];
                        if (!b.alive) continue;
                        if (ball.x + ball.radius > b.x && ball.x - ball.radius < b.x + b.w &&
                            ball.y + ball.radius > b.y && ball.y - ball.radius < b.y + b.h) {
                            b.alive = false;
                            spawnHeartExplosion(b.x + b.w / 2, b.y + b.h / 2);
                            score += 10;

                            const overlapX = Math.min(ball.x + ball.radius - b.x, b.x + b.w - (ball.x - ball
                                .radius));
                            const overlapY = Math.min(ball.y + ball.radius - b.y, b.y + b.h - (ball.y - ball
                                .radius));
                            if (overlapX < overlapY) {
                                ball.vx = -ball.vx;
                            } else {
                                ball.vy = -ball.vy;
                            }
                            break;
                        }
                    }
                }

                // Ball falls below
                if (ball.y - ball.radius > H + 20) {
                    lives--;
                    if (lives <= 0) {
                        if (gameState === 'demo') {
                            demoRestartTimer = 120;
                        } else {
                            showGameOver(false);
                        }
                    } else {
                        resetBall();
                    }
                }

                updateBallTrail();
            }

            function updateAI() {
                if (gameState !== 'demo') return;
                const targetX = ball.x - paddle.width / 2;
                const currentX = paddle.x;
                const dist = targetX - currentX;
                const maxSpeed = 4.8;
                const aiSpeed = Math.min(Math.abs(dist) * 0.08, maxSpeed);
                const direction = dist > 0 ? 1 : -1;

                aiMomentum = aiMomentum * 0.82 + aiSpeed * direction * 0.18;

                paddle.x += aiMomentum;

                // Slight random wobble
                paddle.x += (Math.random() - 0.5) * 0.8;

                // Clamp
                paddle.x = Math.max(0, Math.min(W - paddle.width, paddle.x));
            }

            function updatePlayer() {
                if (gameState !== 'player') return;
                const targetX = mouseX - paddle.width / 2;
                const dist = targetX - paddle.x;
                const maxSpeed = 8;
                const moveAmount = Math.max(-maxSpeed, Math.min(maxSpeed, dist * 0.25));
                paddle.x += moveAmount;
                paddle.x = Math.max(0, Math.min(W - paddle.width, paddle.x));
            }

            function showGameOver(won) {
                if (gameOverShown) return;
                gameOverShown = true;
                gameOverlayEl.classList.add('visible');
                if (won) {
                    gameOverMsg.textContent = '💖 You Win! All Bricks Smashed! 💖';
                } else {
                    gameOverMsg.textContent = '💔 Game Over — Score: ' + score + ' 💔';
                }
                gameOverlayEl.style.pointerEvents = 'auto';
                gameOverlayEl.style.cursor = 'pointer';
            }

            function hideGameOver() {
                gameOverlayEl.classList.remove('visible');
                gameOverlayEl.style.pointerEvents = 'none';
                gameOverShown = false;
            }

            function checkWin() {
                const remaining = bricks.filter(b => b.alive).length;
                if (remaining === 0) {
                    if (gameState === 'demo') {
                        demoRestartTimer = 120;
                    } else {
                        showGameOver(true);
                    }
                }
            }

            function gameLoop(timestamp) {
                const dt = timestamp ? Math.min((timestamp - lastTime) / 16.67, 2) : 1;
                lastTime = timestamp;

                // Update background hearts always
                updateBgHearts();

                // Demo restart
                if (demoRestartTimer > 0) {
                    demoRestartTimer--;
                    if (demoRestartTimer === 0) {
                        resetGame();
                        if (gameState === 'demo') {
                            overlay.classList.remove('hidden');
                        }
                    }
                }

                if (gameState === 'demo') {
                    updateAI();
                    updateBall();
                } else if (gameState === 'player') {
                    updatePlayer();
                    updateBall();
                }

                updateHearts();
                checkWin();

                // Check if all bricks destroyed in demo mode -> restart
                if (gameState === 'demo' && bricks.every(b => !b.alive) && demoRestartTimer === 0) {
                    demoRestartTimer = 100;
                }
                if (gameState === 'demo' && lives <= 0 && demoRestartTimer === 0) {
                    demoRestartTimer = 100;
                }

                // Draw everything
                ctx.clearRect(0, 0, W, H);

                // Background gradient
                const bgGrad = ctx.createLinearGradient(0, 0, 0, H);
                bgGrad.addColorStop(0, '#FFF0F5');
                bgGrad.addColorStop(0.4, '#FFE8EE');
                bgGrad.addColorStop(0.7, '#FFF0F5');
                bgGrad.addColorStop(1, '#FFE4E8');
                ctx.fillStyle = bgGrad;
                ctx.fillRect(0, 0, W, H);

                // Subtle background hearts
                drawBgHearts();

                drawBricks();
                drawHeartsParticles();

                // Draw paddle shadow
                ctx.fillStyle = 'rgba(0,0,0,0.1)';
                ctx.beginPath();
                ctx.ellipse(paddle.x + paddle.width / 2, PADDLE_Y + PADDLE_HEIGHT - 2, paddle.width / 2 + 5, 8, 0,
                    0, Math.PI * 2);
                ctx.fill();

                drawBall();
                drawHamburger(paddle.x, PADDLE_Y - PADDLE_HEIGHT + 16, paddle.width, PADDLE_HEIGHT);

                drawUI();

                requestAnimationFrame(gameLoop);
            }

            function startPlayerMode() {
                gameState = 'player';
                overlay.classList.add('hidden');
                hideGameOver();
                resetGame();
                ballLaunched = true;
                const angle = Math.PI * 0.6 + Math.random() * Math.PI * 0.25;
                const speed = 4.8;
                ball.vx = Math.cos(angle) * speed * (Math.random() > 0.5 ? 1 : -1);
                ball.vy = -Math.sin(angle) * speed;
            }

            function handleStart(event) {
                if (gameState === 'demo') {
                    startPlayerMode();
                } else if (gameOverShown && gameState === 'player') {
                    hideGameOver();
                    resetGame();
                    gameState = 'player';
                }
            }

            // Event listeners
            canvas.addEventListener('click', handleStart);
            overlay.addEventListener('click', handleStart);
            gameOverlayEl.addEventListener('click', handleStart);

            document.addEventListener('keydown', (e) => {
                if (gameState === 'demo') {
                    startPlayerMode();
                } else if (gameOverShown && gameState === 'player') {
                    hideGameOver();
                    resetGame();
                    gameState = 'player';
                }
                keys[e.key] = true;
                if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'A') {
                    if (gameState === 'player') {
                        mouseX = Math.max(0, paddle.x + paddle.width / 2 - 25);
                    }
                }
                if (e.key === 'ArrowRight' || e.key === 'd' || e.key === 'D') {
                    if (gameState === 'player') {
                        mouseX = Math.min(W, paddle.x + paddle.width / 2 + 25);
                    }
                }
            });

            document.addEventListener('keyup', (e) => {
                keys[e.key] = false;
            });

            canvas.addEventListener('mousemove', (e) => {
                const rect = canvas.getBoundingClientRect();
                const scaleX = W / rect.width;
                mouseX = (e.clientX - rect.left) * scaleX;
                mouseX = Math.max(0, Math.min(W, mouseX));
            });

            canvas.addEventListener('touchmove', (e) => {
                e.preventDefault();
                const rect = canvas.getBoundingClientRect();
                const scaleX = W / rect.width;
                const touch = e.touches[0];
                mouseX = (touch.clientX - rect.left) * scaleX;
                mouseX = Math.max(0, Math.min(W, mouseX));
            }, { passive: false });

            canvas.addEventListener('touchstart', (e) => {
                const rect = canvas.getBoundingClientRect();
                const scaleX = W / rect.width;
                const touch = e.touches[0];
                mouseX = (touch.clientX - rect.left) * scaleX;
                mouseX = Math.max(0, Math.min(W, mouseX));
                if (gameState === 'demo') {
                    startPlayerMode();
                }
            }, { passive: true });

            // Add roundRect polyfill if needed
            if (!CanvasRenderingContext2D.prototype.roundRect) {
                CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, radii) {
                    const r = typeof radii === 'number' ? radii : (radii || 0);
                    this.moveTo(x + r, y);
                    this.lineTo(x + w - r, y);
                    this.quadraticCurveTo(x + w, y, x + w, y + r);
                    this.lineTo(x + w, y + h - r);
                    this.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
                    this.lineTo(x + r, y + h);
                    this.quadraticCurveTo(x, y + h, x, y + h - r);
                    this.lineTo(x, y + r);
                    this.quadraticCurveTo(x, y, x + r, y);
                    return this;
                };
            }

            // Initialize
            initBricks();
            initBgHearts();
            resetBall();

            // Start game loop
            lastTime = 0;
            requestAnimationFrame(gameLoop);
        })();
    </script>
</body>
</html>
Reasoning trace (8,856 tokens)
The user wants a breakout game with:
1. A hamburger as the paddle
2. Bricks that explode into tons of little hearts when broken

I need to create a self-contained HTML file with:
- Canvas-based breakout game
- Hamburger paddle (drawn with canvas - bun, lettuce, cheese, patty, bun)
- Bricks that explode into hearts when hit
- Self-playing demo mode with "Click or press a key to start" message
- Responsive design
- No external resources

Let me design this carefully:

1. **Canvas Game**: Breakout-style with a ball bouncing around
2. **Hamburger Paddle**: Draw a burger at the bottom - top bun, lettuce (wavy green), cheese, patty, bottom bun
3. **Heart Explosions**: When a brick is destroyed, spawn many little hearts that fly out with physics
4. **AI Demo Mode**: Simple AI that tracks the ball position
5. **Nice visual style**: Pastel/pink color scheme fitting the heart theme

Let me think about the game structure:
- Ball bounces off walls and the paddle
- Bricks arranged in rows at the top
- When ball hits a brick, brick disappears and hearts explode from it
- Score tracking
- Lives system
- Game over / win conditions

For the hamburger paddle:
- Top bun (rounded top, golden brown)
- Lettuce (wavy green edges)
- Cheese (yellow, melting down)
- Patty (brown)
- Bottom bun (golden brown)

For heart explosions:
- Each brick destroyed spawns 10-20 hearts
- Hearts have random velocities, sizes, rotation
- Hearts are pink/red with the classic heart shape
- They fade out over time

Let me code this up. I'll make the canvas responsive and handle the game loop properly.

For the AI demo mode:
- The AI paddle follows the ball's x position with some delay/imperfection
- Message displayed: "Click or press a key to start"
- Once started, player controls the paddle with mouse/touch

Let me think about the heart drawing. I'll use the classic heart shape with bezier curves or a parametric approach. A simple way is to use two circles and a triangle, or use a bezier curve path.

For drawing hearts on canvas:
```javascript
function drawHeart(ctx, x, y, size, rotation, alpha) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    ctx.scale(size, size);
    ctx.beginPath();
    ctx.moveTo(0, 0.35);
    ctx.bezierCurveTo(0, 0, -0.5, 0, -0.5, -0.35);
    ctx.bezierCurveTo(-0.5, -0.7, 0, -0.7, 0, -0.35);
    ctx.bezierCurveTo(0, -0.7, 0.5, -0.7, 0.5, -0.35);
    ctx.bezierCurveTo(0.5, 0, 0, 0, 0, 0.35);
    ctx.fill();
    ctx.restore();
}
```

Actually, let me use a simpler heart shape. I'll use a scale factor and draw it properly.

Let me design the full game now. I'll make it polished with:
- Gradient background
- Nice colors
- Particle effects
- Sound-free (no audio)
- Score display
- Lives display

Let me structure the code:

```javascript
// Game state
const game = {
    ball: { x, y, vx, vy, radius },
    paddle: { x, y, width, height },
    bricks: [],
    hearts: [], // particle hearts
    score: 0,
    lives: 3,
    state: 'demo' | 'player' | 'gameover',
    demoAI: true
};
```

For the bricks, I'll create a grid of bricks with different colors. When a brick is hit, it spawns hearts.

The hamburger paddle will be drawn as a canvas drawing with:
- A semi-elliptical top bun (golden)
- Sesame seeds on top
- Wavy lettuce sticking out
- A cheese slice
- A burger patty
- Bottom bun

Let me make it visually appealing but not too complex.

For the heart explosion effect:
- 12-20 hearts per brick
- Random size (3-8px)
- Random velocity in all directions
- Gravity pulls them down slightly
- They fade out over 1-2 seconds
- Random rotation
- Random shades of pink/red

I'll also add a trail effect for the ball and maybe some decorative elements.

Let me also think about the game layout:
- Canvas 800x600 or so, responsive
- Bricks at the top (maybe 8 columns, 5 rows)
- Paddle at the bottom
- Score in a corner
- Lives shown as small hearts

For the responsive design:
- Canvas scales with the viewport
- Use CSS to maintain aspect ratio
- Minimum 375px wide support

Let me write the complete code now.

Actually, I need to be careful about the heart drawing. Let me use a nice heart shape. Here's a good parametric heart:

```javascript
function drawHeart(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.beginPath();
    ctx.moveTo(0, s * 0.3);
    ctx.bezierCurveTo(s * 0.5, -s * 0.1, s * 0.5, -s * 0.6, 0, -s * 0.3);
    ctx.bezierCurveTo(-s * 0.5, -s * 0.6, -s * 0.5, -s * 0.1, 0, s * 0.3);
    ctx.fillStyle = color;
    ctx.fill();
    ctx.restore();
}
```

Hmm, let me use a more standard heart shape. The classic heart can be drawn with:
- Two arcs at the top
- Lines converging to a point at the bottom

Or I can use a polygon approach. Let me use a well-known bezier approach.

Actually, I'll use this approach for a nice heart:
```javascript
function drawHeart(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.beginPath();
    ctx.moveTo(0, s * 0.35);
    ctx.bezierCurveTo(0, 0, -s*0.5, 0, -s*0.5, -s*0.35);
    ctx.bezierCurveTo(-s*0.5, -s*0.7, 0, -s*0.8, 0, -s*0.3);
    ctx.bezierCurveTo(0, -s*0.8, s*0.5, -s*0.7, s*0.5, -s*0.35);
    ctx.bezierCurveTo(s*0.5, 0, 0, 0, 0, s*0.35);
    ctx.fillStyle = color;
    ctx.fill();
    ctx.restore();
}
```

Let me simplify this. For the heart shape, I'll use a simple formula drawing two circles and a triangle. Actually, using bezier curves is the cleanest approach. Let me use a well-tested version.

OK, let me just write the whole thing now. I'll make the hamburger look really nice with canvas drawing.

For the hamburger paddle, here's my plan (drawn from bottom to top or top to bottom):
1. Bottom bun: rounded rectangle at the bottom, golden brown
2. Patty: dark brown rectangle/ellipse
3. Cheese: yellow rectangle slightly wider with melting drips
4. Lettuce: wavy green line/area
5. Top bun: dome shape, golden brown, with sesame seeds

The paddle will be maybe 120px wide and 45px tall.

Actually, let me think about sizing. The paddle needs to be wide enough to play but also be recognizable as a burger. Maybe 110px wide, 50px tall. The burger should look like a side-view burger.

Let me draw it properly:

Top bun (dome): ellipse or arc shape, golden brown (#D4A050 or similar)
Sesame seeds: small white/cream dots on the top bun
Lettuce: wavy green ribbon sticking out between buns
Cheese: orange/yellow with a melted drip
Patty: dark brown
Bottom bun: similar to top bun but flatter

I'll draw the paddle centered at its position.

Now let me code the whole game. I'll also add some nice visual touches:
- Background gradient (maybe soft pastel colors)
- Glow effects
- The ball could be a small heart too? Or maybe just a ball. The prompt says "breakout game" so I'll keep it as a ball but maybe make it a cute pink ball.
- Actually, the prompt says hearts explode from bricks, so the ball can be a regular ball or a small heart. Let me make it a cute pink ball.

Let me also think about the demo AI. The AI should:
- Track the ball's x position
- Move the paddle toward the ball with some max speed
- Have some slight randomness to look natural
- The ball bounces around pre-programmed in demo mode? No, I think the game should just run with AI controlling the paddle.

Actually, for the demo mode, I'll have the game running with the AI paddle. The message "Click or press a key to start" will be displayed. Once clicked, the player takes over.

For the AI:
- When the ball is moving toward the bottom (vy > 0), the AI moves the paddle to intercept
- The AI has a max speed and some lag
- It makes the game look playable

Let me finalize the brick layout. I'll use maybe 10 columns × 5 rows = 50 bricks. Each brick is about 60×25 pixels with gaps.

Colors for bricks: various pastel colors - pink, lavender, peach, mint, soft blue, soft yellow.

Alright, let me write the full code now. I need to make sure everything works together smoothly.

One thing to note: the canvas needs to handle the click/keypress to switch from demo to player mode. I'll add a click listener on the canvas and a keydown listener on the document.

Let me think about the canvas size. I'll use 800×600 as the internal resolution and scale it with CSS. For a 1280×800 viewport, this will look fine. For 375px wide, it should scale down properly.

Actually, I'll make the game area responsive. The canvas will maintain its aspect ratio using CSS max-width.

Let me finalize and write the code:

For hearts, I want them to be pretty and plentiful. Each brick will spawn 15-25 hearts. Hearts will have:
- Random velocity (angle and speed)
- Random size (3-8 units)
- Random rotation speed
- Gravity pulling them down
- Fade out over 1-1.5 seconds
- Colors from pink (#FF6B8A) to red (#FF3355) to light pink (#FFB3C1)

For the hamburger paddle drawing, I'll create a detailed canvas drawing. Let me plan the layers from bottom to top:
1. Bottom bun (flat rounded rectangle, golden brown #C8924A)
2. Patty (dark brown #5D3A1A, slightly oval)
3. Cheese (yellow-orange #FFB830, slightly wider with drip)
4. Lettuce (green #4CAF50, wavy pattern)
5. Top bun (dome shape, golden brown #D4A050, with sesame seeds)

The burger sits on the paddle position. The ball bounces off the top of the burger.

For collision detection, I'll treat the paddle as a rectangle (slightly smaller than the visual burger) for simplicity.

OK, I think I have a solid plan. Let me write the code.

Actually, one more thought: the heart explosion should look really nice. I want:
- Lots of hearts (15-25 per brick)
- They fly out in all directions with varying speeds
- They rotate as they fly
- They fade out and shrink slightly over time
- Maybe some are larger and some smaller
- They should leave a trail or just be visible

I'll also add a subtle particle trail for the ball.

Let me also add a slight screen shake or flash when a brick is destroyed? That might be too much. I'll keep it clean.

Here's my complete plan for the HTML file:

```html
<!DOCTYPE html>
<html>
<head>
    <style>...</style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    <div id="overlay">Click or press a key to start</div>
    <script>
        // All game code
    </script>
</body>
</html>
```

The overlay message will be an HTML element positioned over the canvas that disappears when clicked or when a key is pressed.

Let me now write the complete code. I'll be thorough.

For the canvas drawing loop:
1. Clear canvas
2. Draw background
3. Draw bricks
4. Draw hearts (particles)
5. Draw ball
6. Draw paddle (hamburger)
7. Draw score/lives
8. Update physics
9. Handle collisions
10. Request next frame

Let me think about the ball physics:
- Ball starts at the paddle position
- In demo mode, ball launches automatically
- Ball bounces off walls, top, and paddle
- If ball goes below the paddle, lose a life
- In demo mode, ball respawns automatically

For brick collision:
- Check if ball overlaps with any brick
- If so, determine which side was hit (for bounce direction)
- Destroy the brick, spawn hearts, increment score
- Play a visual effect

For the game states:
- 'demo': AI plays, overlay message shown
- 'player': Player controls, no overlay
- 'gameover': Show game over message
- 'win': Show win message

Actually, for simplicity, after game over or win, I'll just restart the game automatically or show a message. Let me show a message and let the player restart by clicking.

Let me code this now. I'll make it clean and well-organized.

I realize I need to carefully handle the demo mode. The AI needs to be decent enough to keep the game going for a while. Here's my AI strategy:
- Calculate where the ball will be when it reaches the paddle's y position
- Move toward that position
- Add some randomness
- If the ball is moving up, just track it loosely

Actually, for simplicity, the AI can just track the ball's x position with some smoothing and max speed. If the ball is moving down, track more aggressively. If moving up, track less aggressively.

The AI should also have a maximum speed to make it look natural and not perfect.

OK, I'm going to write the code now. Let me be careful with all the drawing and physics.

For the heart drawing, I'll use this approach with bezier curves:

```javascript
function drawHeart(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.fillStyle = color;
    ctx.beginPath();
    // Heart shape using bezier curves
    ctx.moveTo(0, -s * 0.3);
    ctx.bezierCurveTo(-s * 0.5, -s * 0.6, -s * 0.6, -s * 0.2, -s * 0.35, s * 0.1);
    ctx.bezierCurveTo(-s * 0.15, s * 0.4, 0, s * 0.55, 0, s * 0.6);
    ctx.bezierCurveTo(0, s * 0.55, s * 0.15, s * 0.4, s * 0.35, s * 0.1);
    ctx.bezierCurveTo(s * 0.6, -s * 0.2, s * 0.5, -s * 0.6, 0, -s * 0.3);
    ctx.fill();
    ctx.restore();
}
```

Hmm, let me try a different heart shape. The classic heart can be drawn with:
- Top two bumps (circles)
- A point at the bottom

Using bezier curves:
```javascript
function drawHeartShape(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.moveTo(0, s * 0.7);
    ctx.bezierCurveTo(-s * 1.2, -s * 0.1, -s * 0.6, -s * 1.1, 0, -s * 0.4);
    ctx.bezierCurveTo(s * 0.6, -s * 1.1, s * 1.2, -s * 0.1, 0, s * 0.7);
    ctx.fill();
    ctx.restore();
}
```

This should create a heart shape. Let me test it conceptually: starting at the bottom point (0, 0.7*s), the first bezier goes up and to the left to create the left bump, then the next bezier goes to the right to create the right bump and back to the bottom point.

Actually, I think a simpler and more reliable heart shape uses the formula where the top of the heart is at y=0 and the point is at the bottom:

The heart is centered with its bumps at the top and point at the bottom.

Let me use a different approach. I'll use a heart shape made with two arcs and two lines:
- Top-left arc: center at (-s*0.25, -s*0.2), radius s*0.3
- Top-right arc: center at (s*0.25, -s*0.2), radius s*0.3
- Bottom point at (0, s*0.6)
- Connect with lines/curves

Actually the bezier approach is cleaner. Let me use this:

```javascript
function drawHeartShape(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.moveTo(0, s * 0.6);
    ctx.bezierCurveTo(-s, -s * 0.1, -s * 0.5, -s * 1.0, 0, -s * 0.35);
    ctx.bezierCurveTo(s * 0.5, -s * 1.0, s, -s * 0.1, 0, s * 0.6);
    ctx.fill();
    ctx.restore();
}
```

This starts at the bottom point of the heart (0, 0.6s), curves up to the left to create the left side of the heart, reaching the top of the left bump, then comes back down. The second bezier creates the right bump and returns to the bottom point.

Let me think... Starting at (0, 0.6s) [bottom point], the first bezier with control points at (-s, -0.1s) and (-0.5s, -1.0s) ends at (0, -0.35s) [top center, between the bumps]. Then the second bezier with control points at (0.5s, -1.0s) and (s, -0.1s) ends back at (0, 0.6s) [bottom point].

This should create a heart where the middle dip is at the top. But it might make the heart look more like a diamond/chalice shape. Let me adjust.

Actually, I realize the issue - the heart needs two bumps at the top. The bezier curve starting from the bottom point should go up the left side, curve to form the left bump, then dip in the middle. Then from the dip, it should form the right bump and go back down.

Here's a better approach:

```javascript
function drawHeartShape(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.fillStyle = color;
    ctx.beginPath();
    // Start at bottom point
    ctx.moveTo(0, s * 0.6);
    // Left side up to left bump
    ctx.bezierCurveTo(-s * 0.7, s * 0.2, -s * 0.8, -s * 0.2, -s * 0.35, -s * 0.5);
    // Left bump to middle dip
    ctx.bezierCurveTo(-s * 0.2, -s * 0.65, -s * 0.1, -s * 0.55, 0, -s * 0.35);
    // Middle dip to right bump
    ctx.bezierCurveTo(s * 0.1, -s * 0.55, s * 0.2, -s * 0.65, s * 0.35, -s * 0.5);
    // Right side down to bottom
    ctx.bezierCurveTo(s * 0.8, -s * 0.2, s * 0.7, s * 0.2, 0, s * 0.6);
    ctx.fill();
    ctx.restore();
}
```

This creates a proper heart with two bumps and a bottom point. The values might need some tweaking but this should give a recognizable heart shape.

Actually, I think a simpler approach is to use the classic heart from canvas tutorials. Here's a well-known heart shape:

```javascript
function drawHeartShape(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.moveTo(0, s * 0.3);
    ctx.bezierCurveTo(-s * 0.5, 0, -s * 0.5, -s * 0.8, 0, -s * 0.4);
    ctx.bezierCurveTo(s * 0.5, -s * 0.8, s * 0.5, 0, 0, s * 0.3);
    ctx.fill();
    ctx.restore();
}
```

This one starts at the bottom point (0, 0.3s), curves up to create the left bump (going to -0.5s in x and -0.8s in y for the control points), meets at the top dip (0, -0.4s), then creates the right bump and returns to the bottom point.

This should create a nice heart shape. Let me use this.

For the heart colors, I'll use various shades of pink, red, and rose:
- #FF6B8A (bright pink)
- #FF3355 (red)
- #FF8FA3 (soft pink)
- #FF4777 (rose)
- #FFB3C1 (light pink)

OK, I have enough planning. Let me write the complete code now.

I also want to make the bricks look nice. They'll be colorful rounded rectangles with a slight gradient. Maybe they can have a subtle glow or shadow.

For the ball, I'll make it a cute pink/orange ball with a slight glow.

Let me also think about the overall aesthetics:
- Background: soft gradient from cream to light pink
- Bricks: pastel colors with rounded corners
- Hamburger: vibrant colors
- Hearts: pink/red with glow
- Score display: cute font (use a generic sans-serif since we can't load fonts)

I'll also add some decorative hearts floating in the background for atmosphere.

One thing I want to make sure is that the game area is properly sized. The canvas will be 800×600. The bricks are at the top (from y=40 to y=200ish), the paddle is at the bottom (y=540ish), and the ball bounces between.

Let me define:
- Canvas: 800×600
- Brick area: starting at y=60, 10 columns × 5 rows
- Brick width: 65px, height: 22px, gap: 5px
- Total brick width: 10*65 + 9*5 = 650 + 45 = 695px, centered → starts at x=(800-695)/2 ≈ 52
- Paddle: y=540, width=110, height=50
- Ball radius: 8px

For the brick colors, I'll use a nice palette:
Row 0: #FF8FA3 (pink)
Row 1: #FFB3C1 (light pink)  
Row 2: #FFD4DC (very light pink)
Row 3: #FF6B8A (darker pink)
Row 4: #FF4777 (rose)

Or maybe a more diverse palette that goes well with hearts. Let me use:
Row 0: #FF6B8A
Row 1: #FF8FA3  
Row 2: #FFB3C1
Row 3: #FF4777
Row 4: #E84393

All pink/rose tones, fitting the heart theme.

OK I'm going to write the full code now. Let me be careful and thorough.

Actually, for the hamburger to really look like a hamburger, I need to spend effort on the drawing. Let me plan the layers carefully:

The hamburger paddle (side view, drawn centered at paddle.x, paddle.y):

From bottom to top:
1. Bottom bun: rounded rectangle, golden brown (#C68C3C), width ~110, height ~14, at the bottom
2. Patty: dark brown (#5D3A1A), width ~100, height ~10, oval-ish
3. Cheese: yellow-orange (#FFB830), width ~105, height ~6, with a small drip on one side
4. Lettuce: green (#5CB85C), wavy pattern, width ~110, height ~8
5. Top bun: dome shape, golden brown (#D4A050), width ~110, height ~18, dome on top
6. Sesame seeds: small cream dots on the top bun

Total height: ~56px, width ~110px

The paddle's collision area will be a simple rectangle at the top of the burger (the top bun area), about 110 wide and 20 tall.

For drawing the burger on the canvas, I'll use various shapes:
- Bottom bun: rounded rect
- Patty: oval/rounded rect
- Cheese: rect with a drippy edge
- Lettuce: wavy path
- Top bun: semi-circle/arc

I'll make the burger look good but keep the drawing code manageable.

Actually, for the collision, I'll just use a rectangle approximation. The visual burger will be centered on the paddle position.

For the demo AI, here's a simple but effective strategy:
- Track the ball's x position
- When the ball is moving downward (vy > 0), move toward the ball's x position with some max speed
- When the ball is moving upward, just follow loosely
- Add some random noise
- Cap the AI's speed so it can miss

Let me also handle the case where the ball goes out of bounds in demo mode. In demo mode, the ball will automatically relaunch.

For the "Click or press a key to start" message, I'll use an HTML overlay that sits on top of the canvas. It'll show the message and a small heart icon.

Let me think about what happens after the game starts:
- Player controls the paddle with mouse/touch
- Ball launches after a short delay
- Score accumulates
- Lives are lost when the ball goes below the paddle
- Game over when lives = 0
- Win when all bricks are destroyed

For the demo mode, the AI plays and shows off the heart explosions.

For the demo mode to look good in the screenshot (taken after 3 seconds), I want:
- The ball to be actively bouncing
- Some bricks already destroyed with hearts flying
- The hamburger paddle visible
- The overlay message visible

So in demo mode, the game should start immediately with the AI playing. A few bricks should have been destroyed within 3 seconds.

Let me adjust the initial ball speed and paddle position so the game is active from the start.

Also, I should make the heart explosion really dramatic. "Tons of little hearts" means a lot of hearts. I'll go with 18-30 hearts per brick.

For the hearts to be visible and impactful, they should:
- Have a good range of sizes (2-8px)
- Fly in all directions
- Have some that fly far and some that stay close
- Rotate for visual interest
- Fade out over time with different fade durations

I'll also add a slight sparkle/twinkle effect to some hearts - maybe they pulse or change opacity slightly.

OK, I think I've planned enough. Let me write the code now. I'll write it all in one go.

One more thing: I need to handle the responsive canvas. The canvas has a fixed internal resolution (800×600), and I'll use CSS to scale it down on smaller screens while maintaining the aspect ratio.

I'll wrap the canvas in a container that centers it and limits its size.

Let me also think about the touch controls. On mobile (375px wide), the player can touch and drag to move the paddle. I'll handle touch events.

For the overlay, when it's shown, clicking it should start the game (switch from demo to player mode). I'll add a click event listener.

Alright, I'm going to write the full HTML file now.

One more consideration: the game should run smoothly at 60fps. I'll use requestAnimationFrame and delta time calculations (or fixed timestep for simplicity). For simplicity, I'll use fixed timestep with requestAnimationFrame.

Actually, for a breakout game, I can just use the frame rate directly. The physics will be simple enough that small variations in frame timing won't matter much.

Let me also think about the heart drawing. I want hearts that are visually recognizable. The bezier curve approach should work well. For small hearts (2-3px), they might be hard to see, so I'll make the minimum size around 3-4px.

For the "tons of hearts" effect, I might also add slightly larger hearts (8-10px) mixed with smaller ones. This creates visual variety.

OK, final plan and then code:

1. Canvas 800×600
2. Background: gradient with subtle pattern
3. Bricks: 10×5 grid of pink/rose colored rounded rectangles
4. Ball: pink circle with glow
5. Paddle: detailed hamburger drawing
6. Hearts: pink/red bezier curve hearts with physics
7. UI: score and lives display
8. Overlay: "Click or press a key to start" message
9. AI demo mode runs automatically
10. Click/keypress switches to player mode
11. Mouse/touch controls the paddle
12. Game over/win conditions with overlays

Now I'll write it all. This is going to be a comprehensive implementation.

Actually, one last thing - the hamburger should look like a hamburger from the side, not from above. It's the paddle in a breakout game, so the player sees it from the side/front. The burger layers should be visible: top bun, lettuce, patty, cheese, bottom bun.

Let me make the burger look great. Here's my drawing plan for the hamburger paddle (centered at x, y, with the collision point at the top of the burger):

The burger is drawn with its center at (paddleX, paddleY). The overall burger dimensions are approximately 110px wide and 50px tall. The collision zone is the top 15px of the burger.

I'll draw from bottom to top:
1. Bottom bun (y+20 to y+35): rounded rect, golden brown
2. Patty (y+14 to y+24): dark brown, slightly narrower
3. Cheese (y+10 to y+18): yellow, slightly wider with drip
4. Lettuce (y+6 to y+14): green, wavy
5. Top bun (y-8 to y+10): dome shape, golden brown
6. Sesame seeds on top bun

This is just a rough plan. I'll fine-tune when coding.

OK, writing the code now. Here it goes. I want to make sure the code is complete and working.

Another thing: for the win condition (all bricks destroyed), I'll show a "You Win! 💖" overlay. For game over (lives = 0), I'll show "Game Over 💔" overlay.

In demo mode, if the game ends (game over or win), it should automatically restart after a brief delay.

For the initial demo, I want the ball to be in motion and hitting bricks within the first 3 seconds. I'll set the ball speed to be relatively fast and place it so it hits bricks quickly.

Let me also add a subtle trail effect for the ball. I'll track the last few positions and draw fading circles.

For the background, I'll add some subtle floating tiny hearts in the background for atmosphere. These will be very faint and slow-moving.

OK, starting to code now. This will be long but complete.

I realize I should also consider: the hamburger needs to look appetizing and recognizable in the screenshot. I'll make the colors vibrant and the layers distinct.

For the sesame seeds on the top bun, I'll draw small white/cream ellipses arranged on the dome.

Let me finalize the heart drawing function. I'll use the bezier curve approach and test it mentally:

```javascript
function drawHeartShape(ctx, x, y, size, rotation, alpha, color) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rotation);
    ctx.globalAlpha = alpha;
    const s = size;
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.moveTo(0, s * 0.35);
    ctx.bezierCurveTo(-s * 0.6, -s * 0.15, -s * 0.4, -s * 0.7, 0, -s * 0.25);
    ctx.bezierCurveTo(s * 0.4, -s * 0.7, s * 0.6, -s * 0.15, 0, s * 0.35);
    ctx.fill();
    ctx.restore();
}
```

Starting at (0, 0.35s) - bottom point of heart
First bezier: control points at (-0.6s, -0.15s) and (-0.4s, -0.7s), ending at (0, -0.25s) - creates left bump
Second bezier: control points at (0.4s, -0.7s) and (0.6s, -0.15s), ending at (0, 0.35s) - creates right bump and returns to bottom

This should create a heart shape. The point is at the bottom (positive y), and the bumps are at the top (negative y). The middle has a slight dip.

For sizing: a heart of size 5 would extend from about -3.5 to +1.75 in the y direction, which seems reasonable.

Now, for the heart particles, I'll have:
- Position (x, y)
- Velocity (vx, vy)
- Size (random 3-8)
- Rotation (random 0-2π)
- Rotation speed (random)
- Gravity (pulls down)
- Alpha (starts at 1, fades to 0)
- Fade speed (random)
- Color (random pink/red shade)

The hearts will update each frame:
- Position += velocity
- Velocity.y += gravity
- Rotation += rotation speed
- Alpha -= fade speed

When alpha <= 0 or position is off screen, remove the heart.

I'll also add a slight horizontal drag to the hearts.

For the brick destruction effect, when a brick is hit:
1. Remove the brick from the array
2. Spawn 18-30 hearts at the brick's position
3. Give hearts random velocities in all directions (360°)
4. Some hearts get more upward velocity, some more sideways
5. Increment score

The hearts should look like they're exploding from the brick. The initial velocities should be symmetric but random.

I'll set the heart speed range from 1-6 pixels per frame, with gravity of about 0.15 pixels per frame².

For the fade, hearts should fade over about 1-2 seconds (60-120 frames). So fade rate = 1/60 to 1/120 per frame.

I want the heart explosion to be really impressive. "Tons of little hearts" - I'll go with about 22 hearts per brick.

Let me also think about what happens when multiple bricks are destroyed in quick succession. The hearts from different bricks should not interfere with each other. Since they're just particles, they won't affect gameplay.

Alright, NOW I'm writing the code. For real this time.

Actually, one thing about the demo mode and the "Click or press a key to start" message: I need to make sure the message is visible in the screenshot (taken after ~3 seconds). The demo AI will be playing, and the message should be prominently displayed. I'll use a semi-transparent overlay with the message.

Also, the game should look polished in the screenshot. The bricks should be colorful, the burger should be visible, and hopefully some hearts will be flying around.

To make sure some bricks are destroyed within 3 seconds, I'll give the ball a good initial trajectory and speed. The demo AI won't prevent the ball from hitting bricks.

Actually, in demo mode, I want the AI to be playing so the game is actively running. The ball will be bouncing, hitting bricks, and hearts will be exploding. This will show off the game mechanics.

The ball should be fast enough to hit some bricks quickly. I'll set the ball speed to about 5-6 pixels per frame.

At 60fps, that's 300-360 pixels per second. Given the game area is 800×600, the ball can cross the screen in about 2-3 seconds. Starting from the paddle at the bottom, it'll take about 1-2 seconds to reach the bricks at the top.

So within 3 seconds, the ball should have hit at least a few bricks, causing heart explosions.

OK, truly writing the code now. I'll aim for a complete, polished game.

Let me also consider: should I use the internal canvas resolution of 800×600 and scale with CSS? Yes. The canvas element will have width=800 and height=600 attributes, and CSS will handle the display size.

For very small screens (375px), the canvas will scale down. The internal resolution stays the same, so the game logic is unaffected.

I also want to prevent the page from scrolling on mobile when touching the canvas. I'll use `touch-action: none` on the canvas.

And for the game to work well on mobile, I'll handle touchmove events to move the paddle.

Final code structure:
1. HTML with canvas and overlay
2. CSS for layout and overlay styling
3. JavaScript:
   - Game constants
   - Game state object
   - Brick initialization
   - Heart particle system
   - Hamburger drawing function
   - Heart drawing function
   - Ball drawing function
   - Game update logic
   - Collision detection
   - AI logic
   - Input handling
   - Rendering loop
   - Event listeners

OK, here we go.