Models 44

Gemini 4 Hydrogen

by Googlereasoning defaultClean
Open in new window

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

How it went

Total time
1 min 33 s
Cost
$0.114
Output tokens
15,068
Spent thinking
1,285 tokens
Speed
164.3 tok/s
Console errors
0
First token after
1.5 s
First visible text after
8.6 s
Prompt tokens
402
Code size
38.3 KB
Finish reason
stop
Served by
Google
Model id
google/gemini-4-hydrogen
Reasoning sent
model default
Max output
40,340 tokens
Generated
Nov 13, 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 · 1256 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  <title>Burger Love Breakout</title>
  <style>
    *, *::before, *::after {
      box-sizing: border-box;
      user-select: none;
      -webkit-user-select: none;
    }

    html, body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
      background: radial-gradient(circle at 50% 25%, #381845 0%, #1c0d26 55%, #100717 100%);
      font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
      color: #fff5f8;
    }

    .app {
      width: 100%;
      height: 100dvh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 10px 12px;
      gap: 10px;
    }

    .hud {
      width: 100%;
      max-width: 820px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      padding: 8px 14px;
      background: rgba(255, 255, 255, 0.08);
      border: 1px solid rgba(255, 160, 200, 0.24);
      border-radius: 14px;
      backdrop-filter: blur(8px);
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
      flex-shrink: 0;
    }

    .brand {
      display: flex;
      align-items: center;
      gap: 8px;
      font-weight: 800;
      font-size: clamp(14px, 2.2vw, 18px);
      letter-spacing: 0.3px;
      color: #ffd480;
      text-shadow: 0 2px 8px rgba(255, 110, 160, 0.4);
      white-space: nowrap;
    }

    .stats {
      display: flex;
      align-items: center;
      gap: clamp(10px, 2.5vw, 22px);
      font-size: clamp(12px, 1.9vw, 15px);
      font-weight: 700;
    }

    .stat {
      display: flex;
      align-items: center;
      gap: 5px;
      white-space: nowrap;
    }

    .stat-label {
      color: #e8b8d0;
      font-weight: 600;
      font-size: 0.85em;
    }

    .stat-val {
      color: #ffffff;
      font-variant-numeric: tabular-nums;
    }

    .stat-hearts .stat-val {
      color: #ff77aa;
    }

    .controls {
      display: flex;
      align-items: center;
      gap: 6px;
    }

    .btn {
      appearance: none;
      border: 1px solid rgba(255, 170, 210, 0.35);
      background: rgba(255, 110, 165, 0.18);
      color: #fff5f8;
      font-weight: 700;
      font-size: clamp(11px, 1.6vw, 13px);
      padding: 5px 10px;
      border-radius: 9px;
      cursor: pointer;
      transition: transform 0.12s ease, background 0.15s ease;
    }

    .btn:hover {
      background: rgba(255, 110, 165, 0.32);
    }

    .btn:active {
      transform: scale(0.95);
    }

    .stage-wrap {
      position: relative;
      width: 100%;
      max-width: 820px;
      flex: 1 1 auto;
      min-height: 0;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    canvas {
      display: block;
      width: 100%;
      height: 100%;
      max-height: 100%;
      object-fit: contain;
      border-radius: 18px;
      border: 2px solid rgba(255, 155, 200, 0.3);
      box-shadow:
        0 18px 50px rgba(0, 0, 0, 0.55),
        inset 0 0 40px rgba(255, 90, 160, 0.08);
      background: linear-gradient(180deg, #241033 0%, #1a0b26 60%, #2a102b 100%);
      touch-action: none;
      cursor: pointer;
    }

    .overlay-prompt {
      position: absolute;
      left: 50%;
      bottom: 20%;
      transform: translateX(-50%);
      pointer-events: none;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 6px;
      padding: 12px 22px;
      border-radius: 999px;
      background: rgba(20, 8, 28, 0.78);
      border: 1.5px solid rgba(255, 140, 195, 0.65);
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5), 0 0 24px rgba(255, 90, 160, 0.35);
      backdrop-filter: blur(6px);
      text-align: center;
      transition: opacity 0.25s ease, transform 0.25s ease;
    }

    .overlay-prompt.hidden {
      opacity: 0;
      transform: translateX(-50%) translateY(8px);
    }

    .overlay-title {
      font-size: clamp(13px, 2.2vw, 18px);
      font-weight: 800;
      color: #fff;
      letter-spacing: 0.4px;
      animation: pulseGlow 1.8s ease-in-out infinite;
    }

    .overlay-sub {
      font-size: clamp(10px, 1.5vw, 12px);
      color: #ffc2dc;
      font-weight: 600;
    }

    .modal-card {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      background: rgba(24, 10, 34, 0.9);
      border: 2px solid rgba(255, 140, 195, 0.6);
      border-radius: 20px;
      padding: 22px 28px;
      text-align: center;
      box-shadow: 0 20px 50px rgba(0, 0, 0, 0.65);
      backdrop-filter: blur(10px);
      display: none;
      flex-direction: column;
      align-items: center;
      gap: 10px;
      z-index: 5;
      min-width: 240px;
    }

    .modal-card.visible {
      display: flex;
    }

    .modal-card h2 {
      margin: 0;
      font-size: clamp(20px, 3vw, 26px);
      color: #ffd480;
    }

    .modal-card p {
      margin: 0;
      font-size: clamp(13px, 2vw, 15px);
      color: #ffd4e5;
    }

    .modal-card .btn-primary {
      margin-top: 6px;
      padding: 9px 20px;
      font-size: 14px;
      border-radius: 12px;
      background: linear-gradient(135deg, #ff5d9e, #ff8566);
      border: none;
      color: #fff;
      box-shadow: 0 6px 18px rgba(255, 93, 158, 0.45);
    }

    @keyframes pulseGlow {
      0%, 100% {
        text-shadow: 0 0 10px rgba(255, 120, 180, 0.6);
        transform: scale(1);
      }
      50% {
        text-shadow: 0 0 18px rgba(255, 180, 210, 0.95);
        transform: scale(1.03);
      }
    }

    @media (max-width: 560px) {
      .app {
        padding: 6px 8px;
        gap: 6px;
      }
      .hud {
        padding: 6px 10px;
        flex-wrap: wrap;
        justify-content: center;
      }
      .brand {
        display: none;
      }
    }
  </style>
</head>
<body>
  <div class="app">
    <header class="hud">
      <div class="brand">
        <span>🍔</span>
        <span>BURGER LOVE BREAKOUT</span>
      </div>
      <div class="stats">
        <div class="stat">
          <span class="stat-label">SCORE</span>
          <span class="stat-val" id="scoreVal">0</span>
        </div>
        <div class="stat stat-hearts">
          <span class="stat-label">💖 HEARTS</span>
          <span class="stat-val" id="heartsVal">0</span>
        </div>
        <div class="stat">
          <span class="stat-label">LVL</span>
          <span class="stat-val" id="levelVal">1</span>
        </div>
        <div class="stat">
          <span class="stat-label">LIVES</span>
          <span class="stat-val" id="livesVal">🍔🍔🍔</span>
        </div>
      </div>
      <div class="controls">
        <button class="btn" id="soundBtn" type="button" aria-label="Toggle sound">🔊 SFX</button>
        <button class="btn" id="resetBtn" type="button">↻ Demo</button>
      </div>
    </header>

    <div class="stage-wrap" id="stageWrap">
      <canvas id="gameCanvas"></canvas>

      <div class="overlay-prompt" id="demoPrompt">
        <div class="overlay-title">Click or press a key to start</div>
        <div class="overlay-sub">Move the Hamburger with Mouse, Touch, or ← → / A D</div>
      </div>

      <div class="modal-card" id="endModal">
        <h2 id="modalTitle">Sweet Victory!</h2>
        <p id="modalMsg">You burst 0 hearts!</p>
        <button class="btn btn-primary" id="modalBtn" type="button">Play Again</button>
      </div>
    </div>
  </div>

  <script>
    (() => {
      const canvas = document.getElementById('gameCanvas');
      const ctx = canvas.getContext('2d');
      const stageWrap = document.getElementById('stageWrap');
      const demoPrompt = document.getElementById('demoPrompt');
      const endModal = document.getElementById('endModal');
      const modalTitle = document.getElementById('modalTitle');
      const modalMsg = document.getElementById('modalMsg');
      const modalBtn = document.getElementById('modalBtn');
      const scoreVal = document.getElementById('scoreVal');
      const heartsVal = document.getElementById('heartsVal');
      const levelVal = document.getElementById('levelVal');
      const livesVal = document.getElementById('livesVal');
      const soundBtn = document.getElementById('soundBtn');
      const resetBtn = document.getElementById('resetBtn');

      // Logical game coordinate space
      const W = 800;
      const H = 600;

      // Resize canvas cleanly to fit container while maintaining 4:3 aspect ratio
      function resizeCanvas() {
        const rect = stageWrap.getBoundingClientRect();
        const aspect = W / H;
        let cw = rect.width;
        let ch = rect.height;
        if (cw / ch > aspect) {
          cw = ch * aspect;
        } else {
          ch = cw / aspect;
        }
        const dpr = Math.min(window.devicePixelRatio || 1, 2);
        canvas.style.width = `${Math.floor(cw)}px`;
        canvas.style.height = `${Math.floor(ch)}px`;
        canvas.width = Math.floor(W * dpr);
        canvas.height = Math.floor(H * dpr);
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      }

      window.addEventListener('resize', resizeCanvas);
      resizeCanvas();

      // Web Audio synthesized SFX (no external files)
      let audioCtx = null;
      let muted = false;

      function ensureAudio() {
        if (!audioCtx) {
          const AC = window.AudioContext || window.webkitAudioContext;
          if (AC) audioCtx = new AC();
        }
        if (audioCtx && audioCtx.state === 'suspended') {
          audioCtx.resume().catch(() => {});
        }
      }

      function playTone(freq, endFreq, duration, type = 'sine', vol = 0.12) {
        if (muted || !audioCtx || audioCtx.state !== 'running') return;
        const now = audioCtx.currentTime;
        const osc = audioCtx.createOscillator();
        const gain = audioCtx.createGain();
        osc.type = type;
        osc.frequency.setValueAtTime(freq, now);
        osc.frequency.exponentialRampToValueAtTime(Math.max(30, endFreq), now + duration);
        gain.gain.setValueAtTime(vol, now);
        gain.gain.exponentialRampToValueAtTime(0.001, now + duration);
        osc.connect(gain);
        gain.connect(audioCtx.destination);
        osc.start(now);
        osc.stop(now + duration + 0.01);
      }

      function playBurgerBounce() {
        playTone(190, 110, 0.09, 'triangle', 0.15);
      }

      function playWallBounce() {
        playTone(320, 260, 0.05, 'sine', 0.07);
      }

      function playHeartBurst(combo = 1) {
        const base = 440 + Math.min(combo, 12) * 45;
        playTone(base, base * 1.45, 0.14, 'sine', 0.11);
        setTimeout(() => playTone(base * 1.25, base * 1.85, 0.12, 'triangle', 0.07), 25);
      }

      function playBrickChip() {
        playTone(360, 240, 0.07, 'square', 0.05);
      }

      // Game State
      let mode = 'demo'; // 'demo' | 'playing' | 'paused' | 'gameover' | 'won'
      let score = 0;
      let totalHeartsBurst = 0;
      let level = 1;
      let lives = 3;
      let combo = 0;
      let shakeTimer = 0;
      let shakeIntensity = 0;

      // Ambient background floating hearts
      const bgHearts = Array.from({ length: 22 }, (_, i) => ({
        x: Math.random() * W,
        y: Math.random() * H,
        size: 6 + Math.random() * 14,
        speedY: 12 + Math.random() * 20,
        wobblePhase: Math.random() * Math.PI * 2,
        alpha: 0.04 + Math.random() * 0.08,
        hue: i % 2 === 0 ? 335 : 350
      }));

      // Hamburger Paddle
      const paddle = {
        x: W / 2,
        y: H - 46,
        width: 126,
        height: 36,
        vx: 0,
        targetX: W / 2,
        squash: 0, // spring value for juicy bounce animation
        squashVel: 0,
        tilt: 0
      };

      // Ball
      const ball = {
        x: W / 2,
        y: H - 84,
        vx: 210,
        vy: -370,
        radius: 10,
        speed: 430,
        attached: false,
        trail: []
      };

      // Bricks
      let bricks = [];
      const brickPalettes = [
        { fill: '#ff4f87', light: '#ff8eb3', dark: '#c9285d', heartColors: ['#ff3b7f', '#ff7aa8', '#ffd1e3', '#ff1f5a'] },
        { fill: '#ff784f', light: '#ffa88c', dark: '#cf4821', heartColors: ['#ff6b4a', '#ff477e', '#ffb3a7', '#ffe0eb'] },
        { fill: '#ffbe3b', light: '#ffda85', dark: '#cc8814', heartColors: ['#ff598f', '#ff94b8', '#ffe38f', '#ff3d68'] },
        { fill: '#57d188', light: '#95ebb7', dark: '#2d9959', heartColors: ['#ff4f8b', '#ff85b3', '#b8ffd6', '#ffffff'] },
        { fill: '#63b8ff', light: '#a3d6ff', dark: '#2b7dc4', heartColors: ['#ff5996', '#ff9cc4', '#a8dbff', '#ffffff'] },
        { fill: '#c66bff', light: '#e0abff', dark: '#8d36c7', heartColors: ['#ff4da6', '#e68aff', '#ffc4e1', '#ffffff'] }
      ];

      // Heart Explosion Particles & Rings & Floating Text
      let heartParticles = [];
      let shockwaves = [];
      let floatingTexts = [];

      function buildLevel(lvl) {
        bricks = [];
        const cols = 10;
        const rows = Math.min(6, 5 + Math.floor((lvl - 1) / 2));
        const padX = 36;
        const topY = 58;
        const gap = 8;
        const totalGap = gap * (cols - 1);
        const bw = (W - padX * 2 - totalGap) / cols;
        const bh = 26;

        for (let r = 0; r < rows; r++) {
          for (let c = 0; c < cols; c++) {
            // Leave a cute heart-ish or scalloped spacing on higher levels, full rich wall on lvl 1
            const palette = brickPalettes[r % brickPalettes.length];
            const isTough = (r === 0 && lvl >= 2); // Bottom rows always 1-hit so hearts explode immediately!
            bricks.push({
              x: padX + c * (bw + gap),
              y: topY + r * (bh + gap),
              w: bw,
              h: bh,
              hp: isTough ? 2 : 1,
              maxHp: isTough ? 2 : 1,
              palette,
              hitFlash: 0,
              wobble: 0
            });
          }
        }
      }

      function resetBall(launchImmediately = true) {
        ball.radius = 10;
        ball.speed = Math.min(540, 420 + (level - 1) * 28);
        ball.trail = [];
        if (launchImmediately) {
          ball.attached = false;
          ball.x = paddle.x;
          ball.y = paddle.y - paddle.height / 2 - ball.radius - 2;
          const angle = -Math.PI / 2 + (Math.random() * 0.55 - 0.275);
          ball.vx = Math.cos(angle) * ball.speed;
          ball.vy = Math.sin(angle) * ball.speed;
        } else {
          ball.attached = true;
          ball.x = paddle.x;
          ball.y = paddle.y - paddle.height / 2 - ball.radius - 2;
          ball.vx = 0;
          ball.vy = 0;
        }
      }

      function syncHud() {
        scoreVal.textContent = score.toLocaleString();
        heartsVal.textContent = totalHeartsBurst.toLocaleString();
        levelVal.textContent = level;
        livesVal.textContent = mode === 'demo' ? '♾️' : '🍔'.repeat(Math.max(0, lives));
      }

      function startDemo() {
        mode = 'demo';
        score = 0;
        totalHeartsBurst = 0;
        level = 1;
        lives = 3;
        combo = 0;
        heartParticles = [];
        shockwaves = [];
        floatingTexts = [];
        paddle.x = W / 2;
        paddle.targetX = W / 2;
        buildLevel(1);
        resetBall(true);
        // Aim demo shot straight toward a brick cluster for quick heart explosions!
        ball.vx = 135;
        ball.vy = -415;
        demoPrompt.classList.remove('hidden');
        endModal.classList.remove('visible');
        syncHud();
      }

      function startGame() {
        ensureAudio();
        if (mode === 'playing') return;
        mode = 'playing';
        score = 0;
        totalHeartsBurst = 0;
        level = 1;
        lives = 3;
        combo = 0;
        heartParticles = [];
        shockwaves = [];
        floatingTexts = [];
        paddle.x = W / 2;
        paddle.targetX = W / 2;
        buildLevel(1);
        resetBall(true);
        demoPrompt.classList.add('hidden');
        endModal.classList.remove('visible');
        syncHud();
      }

      // Input handling
      const keys = { left: false, right: false };

      function pointerToGameX(clientX) {
        const rect = canvas.getBoundingClientRect();
        return ((clientX - rect.left) / rect.width) * W;
      }

      canvas.addEventListener('pointerdown', (e) => {
        ensureAudio();
        if (mode === 'demo') {
          startGame();
        }
        if (mode === 'playing') {
          paddle.targetX = pointerToGameX(e.clientX);
          if (ball.attached) {
            resetBall(true);
          }
        }
      });

      canvas.addEventListener('pointermove', (e) => {
        if (mode === 'playing') {
          paddle.targetX = pointerToGameX(e.clientX);
        }
      });

      window.addEventListener('keydown', (e) => {
        if (['ArrowLeft', 'ArrowRight', 'Space', ' '].includes(e.key)) {
          e.preventDefault();
        }
        ensureAudio();
        if (mode === 'demo') {
          startGame();
          return;
        }
        if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'A') keys.left = true;
        if (e.key === 'ArrowRight' || e.key === 'd' || e.key === 'D') keys.right = true;
        if (e.key === ' ' && mode === 'playing' && ball.attached) {
          resetBall(true);
        }
      });

      window.addEventListener('keyup', (e) => {
        if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'A') keys.left = false;
        if (e.key === 'ArrowRight' || e.key === 'd' || e.key === 'D') keys.right = false;
      });

      soundBtn.addEventListener('click', () => {
        ensureAudio();
        muted = !muted;
        soundBtn.textContent = muted ? '🔇 Muted' : '🔊 SFX';
      });

      resetBtn.addEventListener('click', () => {
        startDemo();
      });

      modalBtn.addEventListener('click', () => {
        startGame();
      });

      // Spawn TONS of little hearts when a brick breaks!
      function explodeBrickIntoHearts(brick) {
        const cx = brick.x + brick.w / 2;
        const cy = brick.y + brick.h / 2;
        const colors = brick.palette.heartColors;

        // 1. Bursting bouncing/floating mini-hearts (30-38 per brick!)
        const count = 34;
        totalHeartsBurst += count;

        for (let i = 0; i < count; i++) {
          const angle = Math.random() * Math.PI * 2;
          const speed = 45 + Math.random() * 240;
          const floaty = i % 3 === 0; // Some float gently up like love bubbles!
          heartParticles.push({
            x: cx + (Math.random() - 0.5) * brick.w * 0.7,
            y: cy + (Math.random() - 0.5) * brick.h * 0.7,
            vx: Math.cos(angle) * speed,
            vy: Math.sin(angle) * speed - (floaty ? 70 : 40),
            size: 5 + Math.random() * 10,
            rot: (Math.random() - 0.5) * 1.2,
            vRot: (Math.random() - 0.5) * 7,
            gravity: floaty ? -55 : 290,
            drag: floaty ? 0.96 : 0.985,
            life: 0,
            maxLife: 0.75 + Math.random() * 0.95,
            color: colors[i % colors.length],
            wobbleFreq: 4 + Math.random() * 6,
            wobblePhase: Math.random() * Math.PI * 2,
            floaty
          });
        }

        // 2. Heart-shaped shockwave outline
        shockwaves.push({
          x: cx,
          y: cy,
          scale: 0.4,
          maxScale: 2.6,
          alpha: 0.9,
          color: colors[0]
        });

        // 3. Cute floating score / combo callout
        const label = combo >= 3 ? `💖 x${combo}!` : `+${100 * Math.max(1, combo)}`;
        floatingTexts.push({
          x: cx,
          y: cy,
          text: label,
          life: 0,
          maxLife: 0.75,
          color: '#fff2f7'
        });

        shakeTimer = 0.08;
        shakeIntensity = Math.min(4.5, 1.8 + combo * 0.35);
      }

      function chipBrickHearts(brick) {
        const cx = brick.x + brick.w / 2;
        const cy = brick.y + brick.h / 2;
        const count = 8;
        totalHeartsBurst += count;
        for (let i = 0; i < count; i++) {
          const angle = Math.random() * Math.PI * 2;
          const speed = 40 + Math.random() * 130;
          heartParticles.push({
            x: cx,
            y: cy,
            vx: Math.cos(angle) * speed,
            vy: Math.sin(angle) * speed - 30,
            size: 4 + Math.random() * 5,
            rot: 0,
            vRot: (Math.random() - 0.5) * 6,
            gravity: 250,
            drag: 0.98,
            life: 0,
            maxLife: 0.5 + Math.random() * 0.4,
            color: brick.palette.heartColors[i % 4],
            wobbleFreq: 5,
            wobblePhase: 0,
            floaty: false
          });
        }
      }

      // Draw a crisp symmetric heart centered at (0, 0)
      function drawHeartPath(size) {
        const s = size * 0.55;
        ctx.beginPath();
        ctx.moveTo(0, s * 0.9);
        ctx.bezierCurveTo(-s * 1.4, s * 0.1, -s * 1.4, -s * 1.1, 0, -s * 0.35);
        ctx.bezierCurveTo(s * 1.4, -s * 1.1, s * 1.4, s * 0.1, 0, s * 0.9);
        ctx.closePath();
      }

      // Draw the delicious Hamburger Paddle!
      function drawHamburgerPaddle() {
        const halfW = paddle.width / 2;
        const sq = paddle.squash;
        const scaleX = 1 + sq * 0.14;
        const scaleY = 1 - sq * 0.24;

        ctx.save();
        ctx.translate(paddle.x, paddle.y + sq * 5);
        ctx.rotate(paddle.tilt);
        ctx.scale(scaleX, scaleY);

        // Drop shadow
        ctx.fillStyle = 'rgba(0, 0, 0, 0.38)';
        ctx.beginPath();
        ctx.ellipse(0, 20, halfW * 0.95, 7, 0, 0, Math.PI * 2);
        ctx.fill();

        // 1. Bottom Bun
        ctx.fillStyle = '#d98b36';
        ctx.beginPath();
        ctx.roundRect(-halfW + 4, 8, paddle.width - 8, 12, [4, 4, 10, 10]);
        ctx.fill();
        ctx.fillStyle = '#f0ab54';
        ctx.beginPath();
        ctx.roundRect(-halfW + 7, 8, paddle.width - 14, 5, 3);
        ctx.fill();

        // 2. Juicy Grilled Patty
        ctx.fillStyle = '#63281b';
        ctx.beginPath();
        ctx.roundRect(-halfW + 1, 0, paddle.width - 2, 10, 5);
        ctx.fill();
        // Grill highlight
        ctx.fillStyle = '#853c2b';
        ctx.beginPath();
        ctx.roundRect(-halfW + 5, 1, paddle.width - 10, 3, 2);
        ctx.fill();

        // 3. Melted Cheddar Cheese (with appetizing drips!)
        ctx.fillStyle = '#ffc928';
        ctx.beginPath();
        ctx.moveTo(-halfW + 5, -2);
        ctx.lineTo(halfW - 5, -2);
        ctx.lineTo(halfW - 4, 3);
        ctx.lineTo(halfW - 18, 3);
        ctx.lineTo(halfW - 26, 9);
        ctx.lineTo(halfW - 35, 3);
        ctx.lineTo(-10, 3);
        ctx.lineTo(-20, 10);
        ctx.lineTo(-30, 3);
        ctx.lineTo(-halfW + 4, 3);
        ctx.closePath();
        ctx.fill();

        // 4. Tomato Slices peeking out
        ctx.fillStyle = '#f03a3a';
        ctx.beginPath();
        ctx.roundRect(-halfW + 8, -6, 34, 5, 2.5);
        ctx.roundRect(halfW - 42, -6, 34, 5, 2.5);
        ctx.fill();
        ctx.fillStyle = '#ff7b7b';
        ctx.fillRect(-halfW + 14, -5, 16, 1.5);
        ctx.fillRect(halfW - 36, -5, 16, 1.5);

        // 5. Crisp Wavy Green Lettuce
        ctx.fillStyle = '#4ad156';
        ctx.beginPath();
        const scallops = 9;
        const step = (paddle.width - 4) / scallops;
        ctx.moveTo(-halfW + 2, -6);
        for (let i = 0; i < scallops; i++) {
          const sx = -halfW + 2 + i * step;
          ctx.quadraticCurveTo(sx + step * 0.5, -12, sx + step, -6);
        }
        for (let i = scallops - 1; i >= 0; i--) {
          const sx = -halfW + 2 + i * step;
          ctx.quadraticCurveTo(sx + step * 0.5, -2, sx, -6);
        }
        ctx.closePath();
        ctx.fill();

        // 6. Golden Domed Top Bun
        const bunGrad = ctx.createLinearGradient(0, -24, 0, -6);
        bunGrad.addColorStop(0, '#f7ba63');
        bunGrad.addColorStop(0.65, '#e89b3c');
        bunGrad.addColorStop(1, '#c97a24');
        ctx.fillStyle = bunGrad;
        ctx.beginPath();
        ctx.moveTo(-halfW + 4, -7);
        ctx.bezierCurveTo(-halfW + 6, -26, halfW - 6, -26, halfW - 4, -7);
        ctx.closePath();
        ctx.fill();

        // Glossy bun highlight
        ctx.fillStyle = 'rgba(255, 244, 214, 0.35)';
        ctx.beginPath();
        ctx.ellipse(-halfW * 0.2, -16, halfW * 0.42, 3.2, -0.05, 0, Math.PI * 2);
        ctx.fill();

        // Sesame seeds
        ctx.fillStyle = '#fff5d6';
        const seeds = [
          [-38, -13, -0.3], [-20, -17, 0.2], [-4, -14, -0.15],
          [14, -17, 0.35], [32, -13, -0.25], [-12, -11, 0.4], [22, -11, -0.4]
        ];
        for (const [sx, sy, rot] of seeds) {
          ctx.save();
          ctx.translate(sx, sy);
          ctx.rotate(rot);
          ctx.beginPath();
          ctx.ellipse(0, 0, 3.2, 1.6, 0, 0, Math.PI * 2);
          ctx.fill();
          ctx.restore();
        }

        ctx.restore();
      }

      // Physics & Update Loop
      let lastTime = performance.now();
      let demoAimOffset = 0;
      let demoAimTimer = 0;

      function update(dt) {
        // Ambient background hearts
        for (const bh of bgHearts) {
          bh.y -= bh.speedY * dt;
          bh.wobblePhase += dt * 1.4;
          if (bh.y < -30) {
            bh.y = H + 30;
            bh.x = Math.random() * W;
          }
        }

        // Paddle squash spring
        const springForce = -paddle.squash * 220 - paddle.squashVel * 18;
        paddle.squashVel += springForce * dt;
        paddle.squash += paddle.squashVel * dt;

        // Paddle movement
        const prevPaddleX = paddle.x;
        if (mode === 'demo') {
          demoAimTimer -= dt;
          if (demoAimTimer <= 0) {
            demoAimTimer = 0.7 + Math.random() * 0.8;
            demoAimOffset = (Math.random() - 0.5) * 44; // varied angles for exciting brick breaks
          }
          const desired = ball.vy > 0 ? ball.x + demoAimOffset : W / 2 + Math.sin(performance.now() * 0.0012) * 80;
          const maxSpeed = 620;
          const diff = desired - paddle.x;
          paddle.x += Math.max(-maxSpeed * dt, Math.min(maxSpeed * dt, diff * 10 * dt));
        } else if (mode === 'playing') {
          const keySpeed = 640;
          if (keys.left) paddle.targetX -= keySpeed * dt;
          if (keys.right) paddle.targetX += keySpeed * dt;
          paddle.targetX = Math.max(paddle.width / 2 + 10, Math.min(W - paddle.width / 2 - 10, paddle.targetX));
          paddle.x += (paddle.targetX - paddle.x) * Math.min(1, 22 * dt);
        }

        paddle.x = Math.max(paddle.width / 2 + 10, Math.min(W - paddle.width / 2 - 10, paddle.x));
        paddle.vx = (paddle.x - prevPaddleX) / Math.max(dt, 0.001);
        paddle.tilt = Math.max(-0.08, Math.min(0.08, paddle.vx * 0.00014));

        // Ball motion
        if (ball.attached) {
          ball.x = paddle.x;
          ball.y = paddle.y - paddle.height / 2 - ball.radius - 2;
        } else if (mode === 'demo' || mode === 'playing') {
          ball.trail.push({ x: ball.x, y: ball.y });
          if (ball.trail.length > 10) ball.trail.shift();

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

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

          // Hamburger Paddle collision
          const pTop = paddle.y - 18;
          const pLeft = paddle.x - paddle.width / 2;
          const pRight = paddle.x + paddle.width / 2;
          if (
            ball.vy > 0 &&
            ball.y + ball.radius >= pTop &&
            ball.y - ball.radius <= paddle.y + 12 &&
            ball.x + ball.radius >= pLeft &&
            ball.x - ball.radius <= pRight
          ) {
            ball.y = pTop - ball.radius;
            const hitNorm = Math.max(-1, Math.min(1, (ball.x - paddle.x) / (paddle.width / 2)));
            const bounceAngle = -Math.PI / 2 + hitNorm * (Math.PI * 0.36);
            ball.vx = Math.cos(bounceAngle) * ball.speed;
            ball.vy = Math.sin(bounceAngle) * ball.speed;
            paddle.squash = 1.0;
            combo = 0;
            playBurgerBounce();

            // Little sesame/love sparks on burger bounce!
            for (let i = 0; i < 6; i++) {
              heartParticles.push({
                x: ball.x,
                y: pTop,
                vx: (Math.random() - 0.5) * 140,
                vy: -40 - Math.random() * 110,
                size: 4 + Math.random() * 4,
                rot: 0,
                vRot: (Math.random() - 0.5) * 6,
                gravity: 200,
                drag: 0.98,
                life: 0,
                maxLife: 0.45,
                color: i % 2 === 0 ? '#ffd480' : '#ff77aa',
                wobbleFreq: 4,
                wobblePhase: 0,
                floaty: false
              });
            }
          }

          // Brick collisions
          for (let i = bricks.length - 1; i >= 0; i--) {
            const b = bricks[i];
            if (b.hitFlash > 0) b.hitFlash = Math.max(0, b.hitFlash - dt * 5);

            const closestX = Math.max(b.x, Math.min(ball.x, b.x + b.w));
            const closestY = Math.max(b.y, Math.min(ball.y, b.y + b.h));
            const dx = ball.x - closestX;
            const dy = ball.y - closestY;

            if (dx * dx + dy * dy <= ball.radius * ball.radius) {
              // Reflect along shallowest overlap axis
              const overlapLeft = (ball.x + ball.radius) - b.x;
              const overlapRight = (b.x + b.w) - (ball.x - ball.radius);
              const overlapTop = (ball.y + ball.radius) - b.y;
              const overlapBottom = (b.y + b.h) - (ball.y - ball.radius);
              const minOverlapX = Math.min(overlapLeft, overlapRight);
              const minOverlapY = Math.min(overlapTop, overlapBottom);

              if (minOverlapX < minOverlapY) {
                ball.vx = -ball.vx;
              } else {
                ball.vy = -ball.vy;
              }

              // Prevent nearly-horizontal infinite loops
              if (Math.abs(ball.vy) < 110) {
                ball.vy = ball.vy >= 0 ? 110 : -110;
                const len = Math.hypot(ball.vx, ball.vy);
                ball.vx = (ball.vx / len) * ball.speed;
                ball.vy = (ball.vy / len) * ball.speed;
              }

              b.hp -= 1;
              b.hitFlash = 1;
              if (b.hp <= 0) {
                combo += 1;
                score += 100 * combo;
                explodeBrickIntoHearts(b);
                playHeartBurst(combo);
                bricks.splice(i, 1);
              } else {
                score += 35;
                chipBrickHearts(b);
                playBrickChip();
              }
              syncHud();
              break; // one brick per step
            }
          }

          // Level cleared!
          if (bricks.length === 0) {
            level += 1;
            if (mode === 'demo') {
              buildLevel(level);
              resetBall(true);
            } else {
              buildLevel(level);
              resetBall(false);
              floatingTexts.push({
                x: W / 2,
                y: H / 2,
                text: `LEVEL ${level}! 💖`,
                life: 0,
                maxLife: 1.4,
                color: '#ffd480'
              });
            }
            syncHud();
          }

          // Ball fell below bottom
          if (ball.y - ball.radius > H + 12) {
            if (mode === 'demo') {
              resetBall(true);
            } else {
              lives -= 1;
              combo = 0;
              syncHud();
              if (lives <= 0) {
                mode = 'gameover';
                modalTitle.textContent = 'Burger Dropped!';
                modalMsg.textContent = `Score: ${score.toLocaleString()} • Burst ${totalHeartsBurst.toLocaleString()} 💖 hearts!`;
                endModal.classList.add('visible');
              } else {
                resetBall(false);
              }
            }
          }
        }

        // Update Heart Particles
        for (let i = heartParticles.length - 1; i >= 0; i--) {
          const p = heartParticles[i];
          p.life += dt;
          if (p.life >= p.maxLife) {
            heartParticles.splice(i, 1);
            continue;
          }
          p.vy += p.gravity * dt;
          p.vx *= Math.pow(p.drag, dt * 60);
          p.vy *= Math.pow(p.drag, dt * 60);
          if (p.floaty) {
            p.x += Math.sin(p.life * p.wobbleFreq + p.wobblePhase) * 28 * dt;
          }
          p.x += p.vx * dt;
          p.y += p.vy * dt;
          p.rot += p.vRot * dt;
        }

        // Update Shockwaves
        for (let i = shockwaves.length - 1; i >= 0; i--) {
          const sw = shockwaves[i];
          sw.scale += dt * 7.2;
          sw.alpha -= dt * 2.2;
          if (sw.alpha <= 0) shockwaves.splice(i, 1);
        }

        // Update Floating Texts
        for (let i = floatingTexts.length - 1; i >= 0; i--) {
          const ft = floatingTexts[i];
          ft.life += dt;
          ft.y -= 36 * dt;
          if (ft.life >= ft.maxLife) floatingTexts.splice(i, 1);
        }

        if (shakeTimer > 0) {
          shakeTimer = Math.max(0, shakeTimer - dt);
        }
      }

      function draw() {
        ctx.save();

        // Screen shake on big heart bursts
        if (shakeTimer > 0) {
          const mag = shakeIntensity * (shakeTimer / 0.08);
          ctx.translate((Math.random() - 0.5) * mag * 2, (Math.random() - 0.5) * mag * 2);
        }

        // Background
        const bgGrad = ctx.createLinearGradient(0, 0, 0, H);
        bgGrad.addColorStop(0, '#230f33');
        bgGrad.addColorStop(0.6, '#1a0a26');
        bgGrad.addColorStop(1, '#2b102d');
        ctx.fillStyle = bgGrad;
        ctx.fillRect(0, 0, W, H);

        // Soft ambient floating hearts in backdrop
        for (const bh of bgHearts) {
          ctx.save();
          ctx.translate(bh.x + Math.sin(bh.wobblePhase) * 8, bh.y);
          ctx.fillStyle = `hsla(${bh.hue}, 90%, 72%, ${bh.alpha})`;
          drawHeartPath(bh.size);
          ctx.fill();
          ctx.restore();
        }

        // Playfield inner border frame
        ctx.strokeStyle = 'rgba(255, 160, 205, 0.18)';
        ctx.lineWidth = 2;
        ctx.strokeRect(10, 10, W - 20, H - 10);

        // Draw Bricks
        for (const b of bricks) {
          const pal = b.palette;
          ctx.save();
          ctx.beginPath();
          ctx.roundRect(b.x, b.y, b.w, b.h, 7);
          ctx.fillStyle = b.hitFlash > 0 ? '#ffffff' : pal.fill;
          ctx.fill();

          // Glossy top highlight
          ctx.fillStyle = pal.light;
          ctx.beginPath();
          ctx.roundRect(b.x + 3, b.y + 3, b.w - 6, 6, 4);
          ctx.fill();

          // Bottom shade
          ctx.fillStyle = pal.dark;
          ctx.beginPath();
          ctx.roundRect(b.x + 2, b.y + b.h - 6, b.w - 4, 4, 3);
          ctx.fill();

          // Cute tiny heart emblem etched in the center of each brick
          ctx.save();
          ctx.translate(b.x + b.w / 2, b.y + b.h / 2 + 1);
          ctx.fillStyle = b.hp > 1 ? 'rgba(255, 255, 255, 0.85)' : 'rgba(255, 255, 255, 0.35)';
          drawHeartPath(b.hp > 1 ? 10 : 7.5);
          ctx.fill();
          ctx.restore();

          if (b.hp > 1) {
            ctx.strokeStyle = 'rgba(255,255,255,0.65)';
            ctx.lineWidth = 1.5;
            ctx.beginPath();
            ctx.roundRect(b.x + 1, b.y + 1, b.w - 2, b.h - 2, 6);
            ctx.stroke();
          }

          ctx.restore();
        }

        // Heart-shaped Shockwaves
        for (const sw of shockwaves) {
          ctx.save();
          ctx.translate(sw.x, sw.y);
          ctx.scale(sw.scale, sw.scale);
          ctx.globalAlpha = Math.max(0, sw.alpha);
          ctx.strokeStyle = sw.color;
          ctx.lineWidth = 2.5 / sw.scale;
          drawHeartPath(26);
          ctx.stroke();
          ctx.restore();
        }

        // Ball Trail
        for (let i = 0; i < ball.trail.length; i++) {
          const t = ball.trail[i];
          const frac = (i + 1) / ball.trail.length;
          ctx.beginPath();
          ctx.arc(t.x, t.y, ball.radius * frac * 0.78, 0, Math.PI * 2);
          ctx.fillStyle = `rgba(255, 130, 185, ${frac * 0.28})`;
          ctx.fill();
        }

        // Ball
        ctx.save();
        ctx.shadowColor = '#ff70aa';
        ctx.shadowBlur = 14;
        const ballGrad = ctx.createRadialGradient(
          ball.x - 3, ball.y - 3, 2,
          ball.x, ball.y, ball.radius
        );
        ballGrad.addColorStop(0, '#ffffff');
        ballGrad.addColorStop(0.55, '#ffe0ec');
        ballGrad.addColorStop(1, '#ff5c9e');
        ctx.fillStyle = ballGrad;
        ctx.beginPath();
        ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
        ctx.fill();
        ctx.restore();

        // Hamburger Paddle
        drawHamburgerPaddle();

        // TONS OF LITTLE HEARTS!
        for (const p of heartParticles) {
          const t = p.life / p.maxLife;
          const alpha = t < 0.15 ? t / 0.15 : 1 - Math.pow((t - 0.15) / 0.85, 1.5);
          const popScale = t < 0.12 ? 0.5 + (t / 0.12) * 0.75 : 1.0;

          ctx.save();
          ctx.translate(p.x, p.y);
          ctx.rotate(p.rot);
          ctx.scale(popScale, popScale);
          ctx.globalAlpha = Math.max(0, alpha);
          ctx.fillStyle = p.color;
          drawHeartPath(p.size);
          ctx.fill();

          // Tiny glossy glint on larger hearts
          if (p.size > 8) {
            ctx.fillStyle = 'rgba(255,255,255,0.5)';
            ctx.beginPath();
            ctx.arc(-p.size * 0.2, -p.size * 0.12, p.size * 0.1, 0, Math.PI * 2);
            ctx.fill();
          }
          ctx.restore();
        }

        // Floating Score / Combo Text
        for (const ft of floatingTexts) {
          const alpha = 1 - ft.life / ft.maxLife;
          ctx.save();
          ctx.globalAlpha = Math.max(0, alpha);
          ctx.font = '800 16px "Segoe UI", sans-serif';
          ctx.textAlign = 'center';
          ctx.fillStyle = ft.color;
          ctx.shadowColor = 'rgba(255, 70, 140, 0.8)';
          ctx.shadowBlur = 8;
          ctx.fillText(ft.text, ft.x, ft.y);
          ctx.restore();
        }

        ctx.restore();
      }

      function loop(now) {
        const dt = Math.min(0.033, (now - lastTime) / 1000);
        lastTime = now;
        update(dt);
        draw();
        requestAnimationFrame(loop);
      }

      // Start immediately in live self-playing demo mode!
      startDemo();
      requestAnimationFrame(loop);
    })();
  </script>
</body>
</html>
Reasoning trace (1,285 tokens)
**Designing Delightful Breakout**

I'm envisioning a single HTML file to host a Breakout game. The core focus is on crafting a visually appealing and fluid experience, particularly with the animated hamburger paddle and dynamic brick destruction.

**Visualizing Breakout Enhancements**

I'm now focusing on the finer visual details. Specifically, I'm planning to implement a dynamic, particle-based brick destruction effect that showers the screen with colorful hearts. The background will be a warm, pastel-sunset gradient with subtle bokeh.

**Crafting Animated Elements**

I'm meticulously detailing the hamburger paddle's procedural drawing, from its crusty buns to its juicy patty and melted cheese drips. I'm also planning for its spring-like squash and stretch animation, and a subtle tilt to enhance its game feel.

**Detailing Heart Explosions**

I'm now refining the mechanics for brick destruction, aiming for around 24-36 heart particles per brick. These will include fast-bursting mini-hearts with physics, floating, wobbling hearts, and expanding shockwave rings, all colored to match the brick's hue with Valentine tones. I'm prioritizing immediate, abundant heart effects from the start by making lower bricks one-hit.