Models 44

Gemini 4 Argon

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
2 min 11 s
Cost
$0.105
Output tokens
10,444
Spent thinking
0 tokens
Speed
82.3 tok/s
Console errors
0
First token after
4.0 s
First visible text after
4.0 s
Prompt tokens
402
Code size
24.5 KB
Finish reason
stop
Served by
Google
Model id
google/gemini-4-argon
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 · 909 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Burger Love Breakout</title>
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; user-select: none; }
  body {
    background: radial-gradient(circle at center, #2a082e 0%, #110214 100%);
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    overflow: hidden;
    color: #fff;
  }
  #game-wrapper {
    position: relative;
    width: 100%;
    max-width: 920px;
    padding: 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .hud {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
    padding: 10px 20px;
    background: rgba(255, 255, 255, 0.07);
    border: 2px solid rgba(255, 105, 180, 0.35);
    border-radius: 16px;
    backdrop-filter: blur(8px);
    box-shadow: 0 8px 25px rgba(0,0,0,0.4);
  }
  .hud-title {
    font-weight: 900;
    font-size: clamp(1rem, 2.5vw, 1.5rem);
    background: linear-gradient(90deg, #ff7eb3, #ff758c, #ffb347);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    letter-spacing: 1px;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .hud-stats {
    display: flex;
    gap: 24px;
    font-weight: 800;
    font-size: clamp(0.9rem, 2vw, 1.2rem);
  }
  .stat span {
    color: #ff7eb3;
  }
  .canvas-box {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 10;
    background: linear-gradient(180deg, #1b0624 0%, #2d0b38 60%, #3d0e3b 100%);
    border-radius: 20px;
    border: 3px solid #ff6b9d;
    box-shadow: 0 0 35px rgba(255, 65, 130, 0.35), inset 0 0 40px rgba(0,0,0,0.6);
    overflow: hidden;
  }
  canvas {
    width: 100%;
    height: 100%;
    display: block;
    cursor: none;
  }
  #overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    transition: opacity 0.35s ease;
    background: radial-gradient(circle, rgba(27,6,36,0.45) 0%, rgba(17,2,20,0.75) 100%);
  }
  #overlay.hidden {
    opacity: 0;
  }
  .banner {
    background: rgba(35, 8, 48, 0.88);
    border: 3px solid #ff758c;
    border-radius: 24px;
    padding: 24px 38px;
    text-align: center;
    box-shadow: 0 15px 40px rgba(0,0,0,0.7), 0 0 25px rgba(255, 117, 140, 0.45);
    animation: floatBanner 2.4s ease-in-out infinite;
  }
  @keyframes floatBanner {
    0%, 100% { transform: translateY(0px) scale(1); }
    50% { transform: translateY(-7px) scale(1.02); }
  }
  .banner h1 {
    font-size: clamp(1.5rem, 4vw, 2.5rem);
    margin-bottom: 8px;
    color: #fff;
    text-shadow: 0 3px 12px rgba(255, 60, 130, 0.8);
  }
  .banner p {
    font-size: clamp(0.95rem, 2vw, 1.2rem);
    color: #ffd1df;
    font-weight: 600;
    margin-bottom: 12px;
  }
  .pill {
    display: inline-block;
    background: linear-gradient(90deg, #ff416c, #ff4b2b);
    color: #fff;
    font-weight: 800;
    padding: 8px 20px;
    border-radius: 999px;
    font-size: 0.95rem;
    letter-spacing: 0.5px;
    box-shadow: 0 4px 15px rgba(255, 65, 108, 0.5);
  }
</style>
</head>
<body>

<div id="game-wrapper">
  <div class="hud">
    <div class="hud-title">🍔 BURGER LOVE BREAKOUT 💖</div>
    <div class="hud-stats">
      <div class="stat">HEARTS: <span id="hearts-count">0</span></div>
      <div class="stat">SCORE: <span id="score">0</span></div>
      <div class="stat">LIVES: <span id="lives">🍔🍔🍔</span></div>
    </div>
  </div>

  <div class="canvas-box" id="canvas-box">
    <canvas id="game" width="960" height="600"></canvas>
    <div id="overlay">
      <div class="banner">
        <h1 id="overlay-title">🍔 BURGER CRUSH 💖</h1>
        <p id="overlay-sub">Auto-Demo Running • Move with Mouse, Touch, or Arrow Keys</p>
        <div class="pill">CLICK OR PRESS ANY KEY TO PLAY</div>
      </div>
    </div>
  </div>
</div>

<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');
const overlayTitle = document.getElementById('overlay-title');
const overlaySub = document.getElementById('overlay-sub');
const scoreEl = document.getElementById('score');
const heartsCountEl = document.getElementById('hearts-count');
const livesEl = document.getElementById('lives');

const W = canvas.width;
const H = canvas.height;

let audioCtx = null;
function playPopSound(freq = 440, type = 'sine', duration = 0.12) {
  if (!audioCtx) return;
  try {
    const osc = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(freq * 1.45, audioCtx.currentTime + duration);
    gain.gain.setValueAtTime(0.12, audioCtx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration);
    osc.connect(gain);
    gain.connect(audioCtx.destination);
    osc.start();
    osc.stop(audioCtx.currentTime + duration);
  } catch (e) {}
}

// Game state
let demoMode = true;
let score = 0;
let totalHeartsSpawned = 0;
let lives = 3;
let screenShake = 0;
let combo = 0;

// Paddle (Hamburger)
const paddle = {
  w: 155,
  h: 48,
  x: W / 2 - 77.5,
  y: H - 68,
  vx: 0,
  targetX: W / 2 - 77.5,
  squish: 1,
  tilt: 0,
  seeds: [
    { rx: -0.32, ry: -0.25, rot: -0.3 },
    { rx: -0.18, ry: -0.38, rot: 0.1 },
    { rx: -0.05, ry: -0.22, rot: -0.1 },
    { rx: 0.08,  ry: -0.36, rot: 0.2 },
    { rx: 0.22,  ry: -0.24, rot: -0.2 },
    { rx: 0.34,  ry: -0.30, rot: 0.35 },
    { rx: -0.25, ry: -0.12, rot: 0.15 },
    { rx: 0.14,  ry: -0.14, rot: -0.25 }
  ]
};

// Balls
let balls = [];

function createBall(x, y, vx, vy) {
  return {
    x, y,
    vx, vy,
    r: 10,
    trail: []
  };
}

// Bricks
let bricks = [];
const BRICK_ROWS = 6;
const BRICK_COLS = 10;
const BRICK_W = 80;
const BRICK_H = 30;
const BRICK_GAP = 8;
const BRICK_OFFSET_TOP = 65;
const BRICK_OFFSET_LEFT = (W - (BRICK_COLS * (BRICK_W + BRICK_GAP) - BRICK_GAP)) / 2;

const ROW_PALETTES = [
  { fill: '#ff2a85', border: '#ff85b3', hearts: ['#ff2a85', '#ff66a3', '#ffb3d1', '#fff0f5'] },
  { fill: '#ff4757', border: '#ff8f99', hearts: ['#ff4757', '#ff6b81', '#ffa4b0', '#ffffff'] },
  { fill: '#ff6348', border: '#ffa596', hearts: ['#ff6348', '#ff7f50', '#ffbe76', '#ffeaa7'] },
  { fill: '#ff7eb3', border: '#ffc2dc', hearts: ['#ff7eb3', '#ff758c', '#ffb8d1', '#ffffff'] },
  { fill: '#e056fd', border: '#f09cff', hearts: ['#e056fd', '#be2edd', '#f3a6ff', '#ffe6ff'] },
  { fill: '#ff9ff3', border: '#ffd6fa', hearts: ['#ff9ff3', '#f368e0', '#ffccf9', '#ffffff'] }
];

// Heart Particles & Shockwaves
let hearts = [];
let floatingTexts = [];
let shockwaves = [];
let bgHearts = [];

// Populate ambient background hearts
for (let i = 0; i < 28; i++) {
  bgHearts.push({
    x: Math.random() * W,
    y: Math.random() * H,
    size: 8 + Math.random() * 18,
    vy: -0.3 - Math.random() * 0.6,
    alpha: 0.06 + Math.random() * 0.12,
    phase: Math.random() * Math.PI * 2
  });
}

function initBricks() {
  bricks = [];
  for (let r = 0; r < BRICK_ROWS; r++) {
    for (let c = 0; c < BRICK_COLS; c++) {
      const bx = BRICK_OFFSET_LEFT + c * (BRICK_W + BRICK_GAP);
      const by = BRICK_OFFSET_TOP + r * (BRICK_H + BRICK_GAP);
      bricks.push({
        x: bx,
        y: by,
        w: BRICK_W,
        h: BRICK_H,
        row: r,
        alive: true,
        palette: ROW_PALETTES[r % ROW_PALETTES.length],
        pulse: Math.random() * Math.PI * 2
      });
    }
  }
}

function spawnHeartExplosion(cx, cy, palette, count = 42) {
  totalHeartsSpawned += count;
  heartsCountEl.textContent = totalHeartsSpawned.toLocaleString();

  shockwaves.push({
    x: cx,
    y: cy,
    r: 8,
    maxR: 75,
    alpha: 0.85,
    color: palette.fill
  });

  for (let i = 0; i < count; i++) {
    const angle = Math.random() * Math.PI * 2;
    const speed = 1.5 + Math.random() * 8.5;
    const size = 6 + Math.random() * 13;
    hearts.push({
      x: cx + (Math.random() - 0.5) * 35,
      y: cy + (Math.random() - 0.5) * 14,
      vx: Math.cos(angle) * speed,
      vy: Math.sin(angle) * speed - (1.5 + Math.random() * 2.5),
      gravity: 0.14 + Math.random() * 0.08,
      drag: 0.985,
      size: size,
      color: palette.hearts[Math.floor(Math.random() * palette.hearts.length)],
      alpha: 1,
      decay: 0.011 + Math.random() * 0.014,
      rot: (Math.random() - 0.5) * 1.2,
      vRot: (Math.random() - 0.5) * 0.14,
      wobble: Math.random() * Math.PI * 2,
      wobbleSpeed: 0.08 + Math.random() * 0.12
    });
  }
}

function resetBallAndLaunch(aimImmediateHit = false) {
  balls = [];
  const startX = paddle.x + paddle.w / 2;
  const startY = paddle.y - 16;
  if (aimImmediateHit) {
    // Launch fast right into the lower bricks so the screenshot at ~3s has exploding hearts
    balls.push(createBall(startX, startY - 90, 4.2, -7.8));
    balls.push(createBall(startX - 40, startY - 120, -4.6, -7.5));
  } else {
    balls.push(createBall(startX, startY, (Math.random() > 0.5 ? 1 : -1) * 4.8, -7.2));
  }
}

function resetGame(isDemo = false) {
  demoMode = isDemo;
  score = 0;
  lives = 3;
  combo = 0;
  updateHUD();
  initBricks();
  resetBallAndLaunch(isDemo);

  if (isDemo) {
    // Trigger an immediate celebratory brick pop right at start so first seconds are super vibrant
    const centerBrick = bricks[bricks.length - 5];
    if (centerBrick) {
      centerBrick.alive = false;
      spawnHeartExplosion(centerBrick.x + centerBrick.w / 2, centerBrick.y + centerBrick.h / 2, centerBrick.palette, 50);
    }
  }
}

function updateHUD() {
  scoreEl.textContent = score.toLocaleString();
  heartsCountEl.textContent = totalHeartsSpawned.toLocaleString();
  livesEl.textContent = '🍔'.repeat(Math.max(0, lives));
}

function startHumanGame() {
  if (!audioCtx) {
    const AudioContext = window.AudioContext || window.webkitAudioContext;
    if (AudioContext) audioCtx = new AudioContext();
  }
  if (demoMode || lives <= 0) {
    overlay.classList.add('hidden');
    resetGame(false);
  }
}

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

window.addEventListener('keydown', (e) => {
  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 (demoMode || lives <= 0) startHumanGame();
});

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;
});

function handlePointerMove(clientX) {
  const rect = canvas.getBoundingClientRect();
  const relX = ((clientX - rect.left) / rect.width) * W;
  paddle.targetX = Math.max(12, Math.min(W - paddle.w - 12, relX - paddle.w / 2));
}

canvas.addEventListener('mousemove', (e) => {
  if (!demoMode) handlePointerMove(e.clientX);
});

canvas.addEventListener('mousedown', (e) => {
  handlePointerMove(e.clientX);
  startHumanGame();
});

canvas.addEventListener('touchstart', (e) => {
  if (e.touches.length > 0) {
    handlePointerMove(e.touches[0].clientX);
    startHumanGame();
  }
}, { passive: true });

canvas.addEventListener('touchmove', (e) => {
  if (!demoMode && e.touches.length > 0) {
    handlePointerMove(e.touches[0].clientX);
  }
}, { passive: true });

// Draw a vector heart centered at (x, y)
function drawHeartPath(ctx, size) {
  const s = size * 0.5;
  ctx.beginPath();
  ctx.moveTo(0, s * 0.35);
  ctx.bezierCurveTo(0, -s * 0.45, -s * 1.15, -s * 0.45, -s * 1.15, s * 0.25);
  ctx.bezierCurveTo(-s * 1.15, s * 0.85, 0, s * 1.25, 0, s * 1.6);
  ctx.bezierCurveTo(0, s * 1.25, s * 1.15, s * 0.85, s * 1.15, s * 0.25);
  ctx.bezierCurveTo(s * 1.15, -s * 0.45, 0, -s * 0.45, 0, s * 0.35);
  ctx.closePath();
}

// Draw detailed juicy hamburger paddle
function drawHamburger(p) {
  ctx.save();
  const cx = p.x + p.w / 2;
  const cy = p.y + p.h / 2;
  ctx.translate(cx, cy);
  ctx.rotate(p.tilt);
  ctx.scale(2 - p.squish, p.squish);

  const hw = p.w / 2;
  const hh = p.h / 2;

  // Drop shadow
  ctx.fillStyle = 'rgba(0, 0, 0, 0.38)';
  ctx.beginPath();
  ctx.ellipse(0, hh + 6, hw * 0.92, 7, 0, 0, Math.PI * 2);
  ctx.fill();

  // 1. Bottom Bun
  const botGrad = ctx.createLinearGradient(0, 6, 0, hh);
  botGrad.addColorStop(0, '#f0a34b');
  botGrad.addColorStop(1, '#c47420');
  ctx.fillStyle = botGrad;
  ctx.strokeStyle = '#6e3805';
  ctx.lineWidth = 2.5;
  ctx.beginPath();
  ctx.roundRect(-hw + 4, 8, p.w - 8, hh - 6, [4, 4, 14, 14]);
  ctx.fill();
  ctx.stroke();

  // 2. Juicy Beef Patty
  const pattyGrad = ctx.createLinearGradient(-hw, 0, hw, 0);
  pattyGrad.addColorStop(0, '#54230e');
  pattyGrad.addColorStop(0.5, '#783519');
  pattyGrad.addColorStop(1, '#4a1e0b');
  ctx.fillStyle = pattyGrad;
  ctx.strokeStyle = '#2e1003';
  ctx.lineWidth = 2.5;
  ctx.beginPath();
  ctx.roundRect(-hw - 3, -2, p.w + 6, 13, 7);
  ctx.fill();
  ctx.stroke();

  // Grill sear marks on patty
  ctx.strokeStyle = 'rgba(30, 8, 0, 0.65)';
  ctx.lineWidth = 2.5;
  for (let gx = -hw + 22; gx <= hw - 22; gx += 22) {
    ctx.beginPath();
    ctx.moveTo(gx, 0);
    ctx.lineTo(gx - 4, 9);
    ctx.stroke();
  }

  // 3. Melted Cheddar Cheese (with triangles dripping over patty)
  ctx.fillStyle = '#ffcc00';
  ctx.strokeStyle = '#d48800';
  ctx.lineWidth = 1.8;
  ctx.beginPath();
  ctx.moveTo(-hw + 2, -3);
  ctx.lineTo(hw - 2, -3);
  ctx.lineTo(hw - 4, 1);
  ctx.lineTo(hw * 0.55, 1);
  ctx.lineTo(hw * 0.45, 9);
  ctx.lineTo(hw * 0.32, 1);
  ctx.lineTo(0, 1);
  ctx.lineTo(-hw * 0.12, 10);
  ctx.lineTo(-hw * 0.25, 1);
  ctx.lineTo(-hw * 0.58, 1);
  ctx.lineTo(-hw * 0.68, 8);
  ctx.lineTo(-hw * 0.78, 1);
  ctx.lineTo(-hw + 2, 1);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();

  // 4. Tomato Slices
  ctx.fillStyle = '#ff3838';
  ctx.strokeStyle = '#a81313';
  ctx.lineWidth = 1.8;
  ctx.beginPath();
  ctx.roundRect(-hw + 12, -8, p.w * 0.42, 6, 3);
  ctx.roundRect(4, -8, p.w * 0.42, 6, 3);
  ctx.fill();
  ctx.stroke();

  // 5. Ruffled Crispy Lettuce
  ctx.fillStyle = '#5cd833';
  ctx.strokeStyle = '#257d0b';
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.moveTo(-hw - 4, -6);
  const waves = 11;
  const step = (p.w + 8) / waves;
  for (let i = 0; i <= waves; i++) {
    const wx = -hw - 4 + i * step;
    const wy = -6 + (i % 2 === 0 ? -4 : 3);
    ctx.quadraticCurveTo(wx - step / 2, -12, wx, wy);
  }
  ctx.lineTo(hw + 4, -4);
  ctx.lineTo(-hw - 4, -4);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();

  // 6. Top Sesame Bun Dome
  const topGrad = ctx.createRadialGradient(-hw * 0.25, -hh * 0.6, 5, 0, -4, hw);
  topGrad.addColorStop(0, '#ffcb7d');
  topGrad.addColorStop(0.55, '#eb983b');
  topGrad.addColorStop(1, '#b86814');
  ctx.fillStyle = topGrad;
  ctx.strokeStyle = '#6e3805';
  ctx.lineWidth = 2.5;
  ctx.beginPath();
  ctx.moveTo(-hw + 2, -7);
  ctx.bezierCurveTo(-hw + 2, -hh - 12, hw - 2, -hh - 12, hw - 2, -7);
  ctx.quadraticCurveTo(0, -4, -hw + 2, -7);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();

  // Gloss highlight on top bun
  ctx.fillStyle = 'rgba(255, 255, 255, 0.28)';
  ctx.beginPath();
  ctx.ellipse(-hw * 0.28, -hh * 0.55, hw * 0.36, 4.5, -0.12, 0, Math.PI * 2);
  ctx.fill();

  // Sesame Seeds
  ctx.fillStyle = '#fff5d6';
  ctx.strokeStyle = '#bd8b46';
  ctx.lineWidth = 1;
  for (const s of p.seeds) {
    ctx.save();
    ctx.translate(s.rx * p.w, s.ry * p.h);
    ctx.rotate(s.rot);
    ctx.beginPath();
    ctx.ellipse(0, 0, 4.2, 2.1, 0, 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.restore();
  }

  ctx.restore();
}

function update() {
  // Screen shake decay
  if (screenShake > 0) screenShake *= 0.85;
  if (screenShake < 0.2) screenShake = 0;

  // Keyboard movement
  const keySpeed = 10.5;
  if (!demoMode) {
    if (keys.left) paddle.targetX = Math.max(12, paddle.targetX - keySpeed);
    if (keys.right) paddle.targetX = Math.min(W - paddle.w - 12, paddle.targetX + keySpeed);
  } else {
    // AI Demo logic: track lowest downward-moving ball
    let targetBall = balls[0];
    let maxScore = -Infinity;
    for (const b of balls) {
      const weight = b.y + (b.vy > 0 ? 250 : 0);
      if (weight > maxScore) {
        maxScore = weight;
        targetBall = b;
      }
    }
    if (targetBall) {
      const offset = Math.sin(performance.now() * 0.003) * 28;
      paddle.targetX = Math.max(12, Math.min(W - paddle.w - 12, targetBall.x - paddle.w / 2 + offset));
    }
  }

  // Smooth paddle follow & tilt physics
  const prevX = paddle.x;
  paddle.x += (paddle.targetX - paddle.x) * 0.28;
  paddle.vx = paddle.x - prevX;
  paddle.tilt = Math.max(-0.14, Math.min(0.14, paddle.vx * 0.008));
  paddle.squish += (1 - paddle.squish) * 0.18;

  // Update balls
  for (let bi = balls.length - 1; bi >= 0; bi--) {
    const b = balls[bi];
    b.trail.push({ x: b.x, y: b.y });
    if (b.trail.length > 9) b.trail.shift();

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

    // Wall collisions
    if (b.x - b.r < 0) {
      b.x = b.r;
      b.vx = Math.abs(b.vx);
      playPopSound(320, 'triangle', 0.07);
    } else if (b.x + b.r > W) {
      b.x = W - b.r;
      b.vx = -Math.abs(b.vx);
      playPopSound(320, 'triangle', 0.07);
    }
    if (b.y - b.r < 0) {
      b.y = b.r;
      b.vy = Math.abs(b.vy);
      playPopSound(360, 'triangle', 0.07);
    }

    // Hamburger Paddle collision
    if (
      b.vy > 0 &&
      b.y + b.r >= paddle.y - 4 &&
      b.y - b.r <= paddle.y + paddle.h &&
      b.x + b.r >= paddle.x - 6 &&
      b.x - b.r <= paddle.x + paddle.w + 6
    ) {
      const hitFactor = ((b.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2));
      const clampedHit = Math.max(-0.95, Math.min(0.95, hitFactor));
      const speed = Math.min(11.5, Math.hypot(b.vx, b.vy) * 1.015);
      const angle = clampedHit * (Math.PI * 0.36);

      b.vx = Math.sin(angle) * speed;
      b.vy = -Math.cos(angle) * speed;
      b.y = paddle.y - b.r - 4;
      paddle.squish = 0.72; // Squish the burger!
      combo = 0;
      playPopSound(240, 'sine', 0.14);

      // Tiny sesame/heart burst on burger bounce
      spawnHeartExplosion(b.x, paddle.y, ROW_PALETTES[3], 10);
    }

    // Brick collisions
    for (const br of bricks) {
      if (!br.alive) continue;
      if (
        b.x + b.r > br.x &&
        b.x - b.r < br.x + br.w &&
        b.y + b.r > br.y &&
        b.y - b.r < br.y + br.h
      ) {
        br.alive = false;

        const overlapLeft = (b.x + b.r) - br.x;
        const overlapRight = (br.x + br.w) - (b.x - b.r);
        const overlapTop = (b.y + b.r) - br.y;
        const overlapBottom = (br.y + br.h) - (b.y - b.r);
        const minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom);

        if (minOverlap === overlapLeft || minOverlap === overlapRight) {
          b.vx = -b.vx;
        } else {
          b.vy = -b.vy;
        }

        combo++;
        const pts = 100 * combo;
        score += pts;
        screenShake = 5.5;

        const cx = br.x + br.w / 2;
        const cy = br.y + br.h / 2;

        // Explode into tons of little hearts!
        spawnHeartExplosion(cx, cy, br.palette, 48);

        floatingTexts.push({
          x: cx,
          y: cy,
          text: combo > 1 ? `💖 +${pts} (${combo}x)` : `💖 +${pts}`,
          alpha: 1,
          vy: -1.6
        });

        playPopSound(420 + Math.min(combo * 45, 500), 'sine', 0.14);
        updateHUD();
        break;
      }
    }

    // Bottom out
    if (b.y - b.r > H) {
      balls.splice(bi, 1);
    }
  }

  // Check if all balls lost
  if (balls.length === 0) {
    if (demoMode) {
      resetBallAndLaunch(false);
    } else {
      lives--;
      updateHUD();
      if (lives <= 0) {
        overlayTitle.textContent = '💔 BURGER OVERCOOKED!';
        overlaySub.textContent = `Final Score: ${score.toLocaleString()} • Hearts Unleashed: ${totalHeartsSpawned.toLocaleString()}`;
        overlay.classList.remove('hidden');
        demoMode = true;
        resetBallAndLaunch(false);
      } else {
        resetBallAndLaunch(false);
      }
    }
  }

  // Check if level cleared
  if (bricks.every(b => !b.alive)) {
    initBricks();
    resetBallAndLaunch(false);
    spawnHeartExplosion(W / 2, H / 2, ROW_PALETTES[0], 90);
  }

  // Update Heart Particles
  for (let i = hearts.length - 1; i >= 0; i--) {
    const h = hearts[i];
    h.vx *= h.drag;
    h.vy = h.vy * h.drag + h.gravity;
    h.wobble += h.wobbleSpeed;
    h.x += h.vx + Math.sin(h.wobble) * 0.65;
    h.y += h.vy;
    h.rot += h.vRot;
    h.alpha -= h.decay;
    if (h.alpha <= 0 || h.y > H + 30) {
      hearts.splice(i, 1);
    }
  }

  // Update Shockwaves
  for (let i = shockwaves.length - 1; i >= 0; i--) {
    const sw = shockwaves[i];
    sw.r += (sw.maxR - sw.r) * 0.18;
    sw.alpha -= 0.045;
    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.y += ft.vy;
    ft.alpha -= 0.022;
    if (ft.alpha <= 0) floatingTexts.splice(i, 1);
  }

  // Update Background Ambient Hearts
  for (const bh of bgHearts) {
    bh.y += bh.vy;
    bh.phase += 0.02;
    if (bh.y < -30) {
      bh.y = H + 30;
      bh.x = Math.random() * W;
    }
  }
}

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

  ctx.save();
  if (screenShake > 0) {
    ctx.translate((Math.random() - 0.5) * screenShake, (Math.random() - 0.5) * screenShake);
  }

  // Ambient floating background hearts
  for (const bh of bgHearts) {
    ctx.save();
    ctx.translate(bh.x + Math.sin(bh.phase) * 12, bh.y);
    ctx.fillStyle = `rgba(255, 120, 180, ${bh.alpha})`;
    drawHeartPath(ctx, bh.size);
    ctx.fill();
    ctx.restore();
  }

  // Draw Bricks with cute embedded heart emblems
  const now = performance.now() * 0.004;
  for (const br of bricks) {
    if (!br.alive) continue;

    ctx.save();
    // Brick body
    const grad = ctx.createLinearGradient(br.x, br.y, br.x, br.y + br.h);
    grad.addColorStop(0, br.palette.border);
    grad.addColorStop(0.25, br.palette.fill);
    grad.addColorStop(1, '#590832');

    ctx.fillStyle = grad;
    ctx.strokeStyle = br.palette.border;
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.roundRect(br.x, br.y, br.w, br.h, 8);
    ctx.fill();
    ctx.stroke();

    // Top shine
    ctx.fillStyle = 'rgba(255,255,255,0.28)';
    ctx.beginPath();
    ctx.roundRect(br.x + 4, br.y + 3, br.w - 8, 6, 4);
    ctx.fill();

    // Center pulsing heart icon on brick
    const pulseScale = 1 + Math.sin(now + br.pulse) * 0.1;
    ctx.translate(br.x + br.w / 2, br.y + br.h / 2 - 2);
    ctx.scale(pulseScale, pulseScale);
    ctx.fillStyle = 'rgba(255, 255, 255, 0.85)';
    drawHeartPath(ctx, 11);
    ctx.fill();
    ctx.restore();
  }

  // Draw Shockwaves
  for (const sw of shockwaves) {
    ctx.save();
    ctx.strokeStyle = sw.color;
    ctx.globalAlpha = Math.max(0, sw.alpha);
    ctx.lineWidth = 3;
    ctx.beginPath();
    ctx.arc(sw.x, sw.y, sw.r, 0, Math.PI * 2);
    ctx.stroke();
    ctx.restore();
  }

  // Draw Exploding Heart Particles
  for (const h of hearts) {
    ctx.save();
    ctx.translate(h.x, h.y);
    ctx.rotate(h.rot);
    ctx.globalAlpha = Math.max(0, h.alpha);
    ctx.fillStyle = h.color;
    ctx.shadowColor = h.color;
    ctx.shadowBlur = 8;
    drawHeartPath(ctx, h.size);
    ctx.fill();
    ctx.restore();
  }

  // Draw Hamburger Paddle
  drawHamburger(paddle);

  // Draw Balls with Heart Trails
  for (const b of balls) {
    for (let i = 0; i < b.trail.length; i++) {
      const t = b.trail[i];
      const ratio = (i + 1) / b.trail.length;
      ctx.save();
      ctx.translate(t.x, t.y);
      ctx.fillStyle = `rgba(255, 105, 180, ${ratio * 0.45})`;
      drawHeartPath(ctx, b.r * 1.4 * ratio);
      ctx.fill();
      ctx.restore();
    }

    ctx.save();
    const ballGrad = ctx.createRadialGradient(b.x - 3, b.y - 3, 2, b.x, b.y, b.r);
    ballGrad.addColorStop(0, '#ffffff');
    ballGrad.addColorStop(0.5, '#ff9ebb');
    ballGrad.addColorStop(1, '#ff1f6a');
    ctx.fillStyle = ballGrad;
    ctx.shadowColor = '#ff2a85';
    ctx.shadowBlur = 15;
    ctx.beginPath();
    ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.restore();
  }

  // Draw Floating Score Popups
  for (const ft of floatingTexts) {
    ctx.save();
    ctx.font = '900 18px "Segoe UI", sans-serif';
    ctx.textAlign = 'center';
    ctx.globalAlpha = Math.max(0, ft.alpha);
    ctx.fillStyle = '#fff';
    ctx.shadowColor = '#ff1f6a';
    ctx.shadowBlur = 8;
    ctx.fillText(ft.text, ft.x, ft.y);
    ctx.restore();
  }

  ctx.restore();
}

function loop() {
  update();
  draw();
  requestAnimationFrame(loop);
}

// Start in lively self-playing demo mode
resetGame(true);
requestAnimationFrame(loop);
</script>
</body>
</html>