Models 44

DeepSeek V4.1 Flash 1017

by DeepSeekReasoningClean
Open in new window

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

How it went

Total time
18.9 s
Cost
$0.0035
Output tokens
6,977
Spent thinking
0 tokens
Speed
376.2 tok/s
Console errors
0
First token after
330 ms
First visible text after
330 ms
Prompt tokens
360
Code size
22.3 KB
Finish reason
stop
Served by
Ionstream
Model id
deepseek/deepseek-v4.1-flash-1017
Reasoning sent
{"effort":"none"}
Max output
64,000 tokens
Generated
Nov 10, 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 · 729 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hamburger Breakout · Heart Explosion</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }
    body {
      background: #1a0e0b;
      display: flex;
      align-items: center;
      justify-content: center;
      min-height: 100vh;
      font-family: system-ui, 'Segoe UI', Roboto, sans-serif;
      padding: 16px;
    }
    .game-container {
      position: relative;
      max-width: 1000px;
      width: 100%;
      border-radius: 28px;
      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.7), 0 0 0 4px #f5e7d9, 0 0 0 8px #b45f3b;
      background: #2b1a12;
      aspect-ratio: 4 / 3;
      overflow: hidden;
    }
    canvas {
      display: block;
      width: 100%;
      height: 100%;
      object-fit: contain;
      cursor: none;
      background: #2d1b12;
    }
    .overlay {
      position: absolute;
      inset: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      background: rgba(25, 8, 4, 0.82);
      backdrop-filter: blur(4px);
      color: #ffefdd;
      text-align: center;
      padding: 24px;
      transition: opacity 0.3s ease;
      pointer-events: none;
      z-index: 10;
      font-weight: 600;
      letter-spacing: 1px;
    }
    .overlay.hidden {
      opacity: 0;
      pointer-events: none;
    }
    .overlay .big {
      font-size: clamp(1.8rem, 8vw, 3.5rem);
      text-shadow: 0 6px 0 #8b3f1f, 0 10px 20px black;
      margin-bottom: 12px;
      background: linear-gradient(135deg, #ffe6b0, #ffb07c);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
    }
    .overlay .sub {
      font-size: clamp(1rem, 4vw, 1.5rem);
      background: #3f2a1e;
      padding: 12px 26px;
      border-radius: 100px;
      box-shadow: inset 0 -4px 0 #1f110b, 0 6px 0 #1f110b;
      color: #ffe0b9;
      font-weight: 500;
      display: inline-flex;
      align-items: center;
      gap: 10px;
    }
    .overlay .sub span {
      font-size: 1.6rem;
      line-height: 1;
    }
    .hint {
      margin-top: 28px;
      font-size: 0.9rem;
      opacity: 0.8;
      font-weight: 400;
      letter-spacing: 0.5px;
    }
    @media (max-width: 500px) {
      .game-container {
        border-radius: 18px;
      }
    }
  </style>
</head>
<body>
  <div class="game-container">
    <canvas id="gameCanvas" width="800" height="600"></canvas>
    <div class="overlay" id="overlay">
      <div class="big">🍔 HAMBURGER BREAKOUT 🍔</div>
      <div class="sub"><span>❤️</span> Click or press a key to start <span>❤️</span></div>
      <div class="hint">Move mouse / arrow keys · Every brick explodes into hearts</div>
    </div>
  </div>
  <script>
    (function() {
      const canvas = document.getElementById('gameCanvas');
      const ctx = canvas.getContext('2d');
      const overlay = document.getElementById('overlay');

      // ---------- FIXED DIMENSIONS ----------
      const W = 800, H = 600;
      canvas.width = W;
      canvas.height = H;

      // ---------- GAME STATE ----------
      let gameActive = false;
      let demoMode = true;          // self-playing AI
      let gameStarted = false;      // user already started
      let gameOver = false;
      let gameWin = false;

      // ---------- PADDLE (hamburger) ----------
      const paddle = {
        x: W / 2 - 60,
        y: H - 40,
        w: 120,
        h: 30,
        speed: 9,
        targetX: W / 2 - 60,
      };

      // ---------- BALL ----------
      const ball = {
        x: W / 2,
        y: H - 80,
        vx: 0,
        vy: 0,
        r: 8,
        speed: 6.8,
        active: true,
      };

      // ---------- BRICKS ----------
      const BRICK_ROWS = 5;
      const BRICK_COLS = 9;
      const BRICK_W = 72;
      const BRICK_H = 24;
      const BRICK_GAP = 10;
      const BRICK_OFFSET_X = (W - (BRICK_COLS * (BRICK_W + BRICK_GAP) - BRICK_GAP)) / 2;
      const BRICK_OFFSET_Y = 60;

      let bricks = [];
      let hearts = [];          // particle hearts
      let score = 0;
      let totalBricks = 0;

      // ---------- COLORS ----------
      const brickColors = [
        '#f9c74f', // yellow
        '#f9844a', // orange
        '#f25c5c', // red
        '#e56b6f', // pink
        '#b5838d', // mauve
      ];

      // ---------- INIT BRICKS ----------
      function initBricks() {
        bricks = [];
        totalBricks = 0;
        for (let row = 0; row < BRICK_ROWS; row++) {
          for (let col = 0; col < BRICK_COLS; col++) {
            const bx = BRICK_OFFSET_X + col * (BRICK_W + BRICK_GAP);
            const by = BRICK_OFFSET_Y + row * (BRICK_H + BRICK_GAP);
            // maybe skip some for pattern? keep all for more hearts
            bricks.push({
              x: bx, y: by, w: BRICK_W, h: BRICK_H,
              color: brickColors[row % brickColors.length],
              alive: true,
            });
            totalBricks++;
          }
        }
      }
      initBricks();

      // ---------- HEART PARTICLES ----------
      function spawnHearts(x, y, count = 12) {
        for (let i = 0; i < count; i++) {
          const angle = Math.random() * Math.PI * 2;
          const speed = 1.5 + Math.random() * 5.5;
          const vx = Math.cos(angle) * speed;
          const vy = Math.sin(angle) * speed - 1.5; // slight upward bias
          const size = 6 + Math.random() * 10;
          const life = 0.8 + Math.random() * 0.9;
          const rot = Math.random() * Math.PI * 2;
          const rotSpeed = (Math.random() - 0.5) * 0.2;
          hearts.push({
            x, y,
            vx, vy,
            size,
            life,
            maxLife: life,
            rot,
            rotSpeed,
            alpha: 1,
            color: Math.random() > 0.3 ? '#ff4d6d' : '#ff8fa3',
          });
        }
      }

      // ---------- RESET BALL ----------
      function resetBall() {
        ball.x = paddle.x + paddle.w / 2;
        ball.y = paddle.y - 20;
        const angle = (Math.random() * 0.8 - 0.4) + Math.PI / 2; // mostly upwards
        ball.vx = Math.cos(angle) * ball.speed * 0.7;
        ball.vy = -Math.sin(angle) * ball.speed * 0.7;
        ball.active = true;
      }

      // ---------- RESET GAME ----------
      function resetGame() {
        initBricks();
        score = 0;
        hearts = [];
        paddle.x = W / 2 - paddle.w / 2;
        paddle.targetX = paddle.x;
        ball.speed = 6.8;
        ball.x = W / 2;
        ball.y = H - 80;
        ball.vx = (Math.random() > 0.5 ? 1 : -1) * 4.5;
        ball.vy = -5.5;
        ball.active = true;
        gameOver = false;
        gameWin = false;
        demoMode = true;
        gameStarted = false;
        gameActive = false;
        overlay.classList.remove('hidden');
      }

      // ---------- START ----------
      function startGame() {
        if (gameStarted) return;
        overlay.classList.add('hidden');
        gameStarted = true;
        gameActive = true;
        demoMode = false; // disable AI
        // reset ball/ paddle for a clean start
        resetGame();
        // small delay for ball movement? no, start immediately
        gameActive = true;
        demoMode = false;
        ball.x = W / 2;
        ball.y = H - 100;
        ball.vx = (Math.random() > 0.5 ? 1 : -1) * 4.8;
        ball.vy = -6.2;
        ball.active = true;
      }

      // ---------- TRIGGER START ----------
      function handleStart() {
        if (!gameStarted && (gameOver || gameWin || demoMode || !gameActive)) {
          // If game over, reset and then start
          if (gameOver || gameWin) {
            resetGame();
            gameStarted = false;
            demoMode = true;
            gameActive = false;
            gameOver = false;
            gameWin = false;
            // immediately start after short delay
            setTimeout(() => {
              if (!gameStarted) startGame();
            }, 100);
          } else {
            startGame();
          }
        } else if (!gameActive && !gameOver && !gameWin) {
          startGame();
        } else if (gameOver || gameWin) {
          resetGame();
          gameStarted = false;
          demoMode = true;
          gameActive = false;
          gameOver = false;
          gameWin = false;
          overlay.classList.remove('hidden');
        }
      }

      // ---------- INPUT ----------
      canvas.addEventListener('mousemove', (e) => {
        if (!gameActive) return;
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        let mouseX = (e.clientX - rect.left) * scaleX;
        paddle.targetX = Math.min(Math.max(mouseX - paddle.w / 2, 0), W - paddle.w);
      });

      window.addEventListener('keydown', (e) => {
        if (!gameStarted) {
          // start on any key
          e.preventDefault();
          handleStart();
          return;
        }
        if (!gameActive) return;
        if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'A') {
          e.preventDefault();
          paddle.targetX = Math.max(0, paddle.targetX - 35);
        } else if (e.key === 'ArrowRight' || e.key === 'd' || e.key === 'D') {
          e.preventDefault();
          paddle.targetX = Math.min(W - paddle.w, paddle.targetX + 35);
        }
      });

      canvas.addEventListener('mousedown', (e) => {
        e.preventDefault();
        if (!gameStarted) {
          handleStart();
        } else if (gameOver || gameWin) {
          handleStart();
        }
      });

      overlay.addEventListener('mousedown', (e) => {
        e.preventDefault();
        if (!gameStarted) {
          handleStart();
        }
      });

      // also click on canvas if game over
      canvas.addEventListener('click', (e) => {
        if (gameOver || gameWin) handleStart();
      });

      // ---------- UPDATE ----------
      function update() {
        // demo AI: move paddle toward ball
        if (demoMode) {
          // simple AI: follow the ball x position, but with some error
          const target = ball.x - paddle.w / 2;
          paddle.targetX = Math.min(Math.max(target, 0), W - paddle.w);
          // shift ball a bit for fun
        }

        // smooth paddle movement
        const dx = paddle.targetX - paddle.x;
        paddle.x += dx * 0.25;
        paddle.x = Math.max(0, Math.min(W - paddle.w, paddle.x));

        if (!gameActive) return;

        // ball movement
        if (!ball.active) return;
        ball.x += ball.vx;
        ball.y += ball.vy;

        // walls
        if (ball.x - ball.r < 0) {
          ball.x = ball.r;
          ball.vx = -ball.vx;
        }
        if (ball.x + ball.r > W) {
          ball.x = W - ball.r;
          ball.vx = -ball.vx;
        }
        if (ball.y - ball.r < 0) {
          ball.y = ball.r;
          ball.vy = -ball.vy;
        }

        // paddle collision
        if (ball.y + ball.r > paddle.y && ball.y - ball.r < paddle.y + paddle.h &&
            ball.x + ball.r > paddle.x && ball.x - ball.r < paddle.x + paddle.w) {
          // only bounce if moving down
          if (ball.vy > 0) {
            ball.y = paddle.y - ball.r;
            // reflect based on hit position
            const hit = (ball.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2);
            const maxAngle = 1.2;
            const angle = hit * maxAngle;
            const spd = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
            ball.vx = Math.sin(angle) * spd;
            ball.vy = -Math.cos(angle) * spd;
            // slightly increase speed
            if (spd < 12) {
              ball.vx *= 1.02;
              ball.vy *= 1.02;
            }
          }
        }

        // bottom - lose
        if (ball.y - ball.r > H) {
          ball.active = false;
          gameActive = false;
          gameOver = true;
          overlay.classList.remove('hidden');
          overlay.innerHTML = `<div class="big">💔 GAME OVER 💔</div>
            <div class="sub" style="cursor:pointer">Click or press a key to restart</div>
            <div class="hint">Score: ${score} hearts</div>`;
          overlay.style.pointerEvents = 'auto';
          return;
        }

        // brick collision
        for (let brick of bricks) {
          if (!brick.alive) continue;
          if (ball.x + ball.r > brick.x && ball.x - ball.r < brick.x + brick.w &&
              ball.y + ball.r > brick.y && ball.y - ball.r < brick.y + brick.h) {
            // destroy brick
            brick.alive = false;
            score++;
            // spawn hearts
            spawnHearts(brick.x + brick.w / 2, brick.y + brick.h / 2, 18);
            // bounce ball
            // determine direction
            const overlapLeft = (ball.x + ball.r) - brick.x;
            const overlapRight = (brick.x + brick.w) - (ball.x - ball.r);
            const overlapTop = (ball.y + ball.r) - brick.y;
            const overlapBottom = (brick.y + brick.h) - (ball.y - ball.r);
            const minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom);
            if (minOverlap === overlapLeft) {
              ball.vx = -Math.abs(ball.vx);
              ball.x = brick.x - ball.r;
            } else if (minOverlap === overlapRight) {
              ball.vx = Math.abs(ball.vx);
              ball.x = brick.x + brick.w + ball.r;
            } else if (minOverlap === overlapTop) {
              ball.vy = -Math.abs(ball.vy);
              ball.y = brick.y - ball.r;
            } else if (minOverlap === overlapBottom) {
              ball.vy = Math.abs(ball.vy);
              ball.y = brick.y + brick.h + ball.r;
            }
            break; // only one brick per frame
          }
        }

        // check win
        const aliveBricks = bricks.filter(b => b.alive).length;
        if (aliveBricks === 0) {
          gameActive = false;
          gameWin = true;
          overlay.classList.remove('hidden');
          overlay.innerHTML = `<div class="big">🍔 YOU WIN! 🍔</div>
            <div class="sub" style="cursor:pointer">Click or press a key to play again</div>
            <div class="hint">Hearts scattered: ${score * 18}</div>`;
          overlay.style.pointerEvents = 'auto';
        }

        // update hearts
        for (let i = hearts.length - 1; i >= 0; i--) {
          const h = hearts[i];
          h.x += h.vx;
          h.y += h.vy;
          h.vy += 0.12; // gravity
          h.vx *= 0.99;
          h.rot += h.rotSpeed;
          h.life -= 0.012;
          if (h.life <= 0) {
            hearts.splice(i, 1);
          }
        }
      }

      // ---------- DRAW ----------
      function draw() {
        ctx.clearRect(0, 0, W, H);

        // background gradient
        const bgGrad = ctx.createLinearGradient(0, 0, 0, H);
        bgGrad.addColorStop(0, '#2d1b12');
        bgGrad.addColorStop(1, '#1f0f08');
        ctx.fillStyle = bgGrad;
        ctx.fillRect(0, 0, W, H);

        // subtle tiles
        ctx.fillStyle = '#3e241b';
        for (let i = 0; i < W; i += 40) {
          for (let j = 0; j < H; j += 40) {
            if ((i / 40 + j / 40) % 2 === 0) {
              ctx.fillRect(i, j, 40, 40);
            }
          }
        }

        // bricks
        for (let brick of bricks) {
          if (!brick.alive) continue;
          // rounded rect with shine
          ctx.shadowColor = 'rgba(0,0,0,0.5)';
          ctx.shadowBlur = 8;
          ctx.shadowOffsetY = 4;
          ctx.fillStyle = brick.color;
          ctx.beginPath();
          ctx.roundRect(brick.x, brick.y, brick.w, brick.h, 8);
          ctx.fill();
          // highlight
          ctx.shadowBlur = 0;
          ctx.shadowOffsetY = 0;
          ctx.fillStyle = 'rgba(255,255,255,0.25)';
          ctx.beginPath();
          ctx.roundRect(brick.x + 4, brick.y + 4, brick.w - 8, 6, 4);
          ctx.fill();
        }
        ctx.shadowBlur = 0;
        ctx.shadowOffsetY = 0;

        // draw hearts
        for (let h of hearts) {
          const alpha = Math.min(1, h.life / 0.6);
          ctx.save();
          ctx.translate(h.x, h.y);
          ctx.rotate(h.rot);
          ctx.globalAlpha = alpha;
          ctx.fillStyle = h.color;
          ctx.shadowColor = '#ff4d6d';
          ctx.shadowBlur = 18;
          // heart shape
          const s = h.size;
          ctx.beginPath();
          ctx.moveTo(0, s * 0.3);
          ctx.bezierCurveTo(-s * 0.5, -s * 0.3, -s * 0.2, -s * 0.8, 0, -s * 0.4);
          ctx.bezierCurveTo(s * 0.2, -s * 0.8, s * 0.5, -s * 0.3, 0, s * 0.3);
          ctx.closePath();
          ctx.fill();
          ctx.restore();
        }
        ctx.globalAlpha = 1;
        ctx.shadowBlur = 0;

        // draw paddle (hamburger)
        drawHamburger(paddle.x, paddle.y, paddle.w, paddle.h);

        // draw ball
        if (ball.active) {
          ctx.shadowColor = '#ffdd44';
          ctx.shadowBlur = 24;
          ctx.fillStyle = '#ffe066';
          ctx.beginPath();
          ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
          ctx.fill();
          // inner shine
          ctx.shadowBlur = 0;
          ctx.fillStyle = '#fff9c4';
          ctx.beginPath();
          ctx.arc(ball.x - 2, ball.y - 2, ball.r * 0.4, 0, Math.PI * 2);
          ctx.fill();
        }

        // score
        ctx.font = 'bold 22px "Segoe UI", system-ui, sans-serif';
        ctx.fillStyle = '#ffe0b9';
        ctx.shadowColor = '#3a1e0e';
        ctx.shadowBlur = 8;
        ctx.shadowOffsetY = 3;
        ctx.fillText(`❤️ ${score}`, 24, 44);
        ctx.shadowBlur = 0;
        ctx.shadowOffsetY = 0;

        // demo hint
        if (demoMode && !gameStarted && gameActive) {
          ctx.font = 'bold 18px system-ui, sans-serif';
          ctx.fillStyle = '#d9b38c';
          ctx.shadowBlur = 10;
          ctx.fillText('🍔 DEMO MODE 🍔', W - 230, 44);
        }
      }

      function drawHamburger(x, y, w, h) {
        const bunTop = y + 2;
        const bunBottom = y + h - 4;
        const midY = y + h * 0.45;
        const pattyY = y + h * 0.62;

        // bottom bun
        ctx.fillStyle = '#c97b3d';
        ctx.shadowColor = '#4a2a10';
        ctx.shadowBlur = 12;
        ctx.shadowOffsetY = 5;
        ctx.beginPath();
        ctx.roundRect(x, y + h * 0.55, w, h * 0.45, [12, 12, 24, 24]);
        ctx.fill();

        // patty
        ctx.fillStyle = '#5a2e16';
        ctx.shadowBlur = 10;
        ctx.beginPath();
        ctx.roundRect(x + 4, y + h * 0.45, w - 8, h * 0.25, 8);
        ctx.fill();

        // cheese
        ctx.fillStyle = '#ffb703';
        ctx.shadowBlur = 8;
        ctx.beginPath();
        ctx.ellipse(x + w * 0.28, y + h * 0.42, w * 0.18, h * 0.08, 0.2, 0, 2 * Math.PI);
        ctx.ellipse(x + w * 0.72, y + h * 0.42, w * 0.18, h * 0.08, -0.2, 0, 2 * Math.PI);
        ctx.fill();

        // lettuce
        ctx.fillStyle = '#6a994e';
        ctx.shadowBlur = 8;
        ctx.beginPath();
        ctx.ellipse(x + w * 0.2, y + h * 0.35, w * 0.14, h * 0.06, 0.4, 0, 2 * Math.PI);
        ctx.ellipse(x + w * 0.5, y + h * 0.3, w * 0.16, h * 0.07, 0.2, 0, 2 * Math.PI);
        ctx.ellipse(x + w * 0.8, y + h * 0.35, w * 0.14, h * 0.06, -0.3, 0, 2 * Math.PI);
        ctx.fill();

        // top bun
        ctx.fillStyle = '#d68c4e';
        ctx.shadowBlur = 14;
        ctx.shadowOffsetY = 6;
        ctx.beginPath();
        ctx.roundRect(x, y, w, h * 0.55, [16, 16, 8, 8]);
        ctx.fill();

        // sesame seeds
        ctx.fillStyle = '#fff6e0';
        ctx.shadowBlur = 4;
        ctx.shadowOffsetY = 2;
        const seedPositions = [
          [0.3, 0.22], [0.5, 0.1], [0.7, 0.18], [0.4, 0.35], [0.6, 0.32], [0.2, 0.4], [0.8, 0.38]
        ];
        for (let [sx, sy] of seedPositions) {
          ctx.beginPath();
          ctx.ellipse(x + w * sx, y + h * sy, 4, 2.5, 0.4, 0, 2 * Math.PI);
          ctx.fill();
        }
        ctx.shadowBlur = 0;
        ctx.shadowOffsetY = 0;
      }

      // helper for roundRect
      CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
        if (w < 2 * r) r = w / 2;
        if (h < 2 * r) r = h / 2;
        if (typeof r === 'number') {
          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);
        } else {
          // array of radii
          const [tl, tr, br, bl] = r;
          this.moveTo(x + tl, y);
          this.lineTo(x + w - tr, y);
          this.quadraticCurveTo(x + w, y, x + w, y + tr);
          this.lineTo(x + w, y + h - br);
          this.quadraticCurveTo(x + w, y + h, x + w - br, y + h);
          this.lineTo(x + bl, y + h);
          this.quadraticCurveTo(x, y + h, x, y + h - bl);
          this.lineTo(x, y + tl);
          this.quadraticCurveTo(x, y, x + tl, y);
        }
        return this;
      };

      // ---------- GAME LOOP ----------
      let lastTime = 0;
      function gameLoop(now) {
        // fixed update rate ~60fps
        update();
        draw();
        requestAnimationFrame(gameLoop);
      }

      // ---------- INITIAL STATE FOR DEMO ----------
      function initDemo() {
        resetGame();
        // demo mode: ball active & AI plays
        gameActive = true;
        demoMode = true;
        gameStarted = false;
        gameOver = false;
        gameWin = false;
        overlay.classList.remove('hidden');
        // overlay content back to default
        overlay.innerHTML = `<div class="big">🍔 HAMBURGER BREAKOUT 🍔</div>
          <div class="sub"><span>❤️</span> Click or press a key to start <span>❤️</span></div>
          <div class="hint">Move mouse / arrow keys · Every brick explodes into hearts</div>`;
        overlay.style.pointerEvents = 'none';
        // reset ball
        ball.x = W / 2;
        ball.y = H - 100;
        ball.vx = 4.5;
        ball.vy = -5.5;
        ball.active = true;
        paddle.x = W / 2 - paddle.w / 2;
        paddle.targetX = paddle.x;
      }

      initDemo();

      // start loop
      requestAnimationFrame(gameLoop);

      // handle click on overlay to start
      overlay.addEventListener('click', (e) => {
        e.stopPropagation();
        if (!gameStarted) {
          handleStart();
        }
      });

      // ensure pointer events
      window.addEventListener('load', () => {
        overlay.style.pointerEvents = 'none';
      });

    })();
  </script>
</body>
</html>