Models 44

GPT-6 Luna

by OpenAIReasoningClean
Open in new window

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

How it went

Total time
1 min 37 s
Cost
$0.0063
Output tokens
12,581
Spent thinking
4,985 tokens
Speed
134.1 tok/s
Console errors
0
First token after
3.5 s
First visible text after
50.1 s
Prompt tokens
357
Code size
21.5 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-6-luna
Reasoning sent
{"effort":"high"}
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 · 641 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Buns & Breakout</title>
<style>
  :root {
    color-scheme: dark;
    --ink: #f7f0ff;
    --muted: #a49bbd;
    --pink: #ff75ae;
    --purple: #a78bfa;
  }
  * { box-sizing: border-box; }
  html, body { margin: 0; min-height: 100%; }
  body {
    min-height: 100svh;
    overflow-x: hidden;
    display: grid;
    place-items: center;
    padding: 18px 22px;
    color: var(--ink);
    background:
      radial-gradient(ellipse at 14% 10%, rgba(111, 70, 155, .21), transparent 37%),
      radial-gradient(ellipse at 88% 82%, rgba(213, 68, 129, .13), transparent 35%),
      #100e1c;
    font-family: ui-rounded, "Trebuchet MS", system-ui, sans-serif;
  }
  body::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    opacity: .2;
    background-image: radial-gradient(#bda6e8 1px, transparent 1px);
    background-size: 42px 42px;
    mask-image: linear-gradient(135deg, black, transparent 68%);
  }
  .shell { width: min(1080px, 100%); position: relative; }
  .topbar {
    min-height: 72px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    margin-bottom: 12px;
  }
  .brand { display: flex; align-items: center; gap: 13px; min-width: 0; }
  .logo {
    width: 48px; height: 48px; flex: 0 0 auto;
    position: relative;
    border-radius: 15px;
    background: linear-gradient(145deg, #342448, #201b32);
    border: 1px solid rgba(255,255,255,.13);
    box-shadow: inset 0 1px rgba(255,255,255,.12), 0 8px 24px #08071188;
  }
  .logo span { position: absolute; left: 10px; width: 27px; border-radius: 9px; }
  .logo .bun-top {
    top: 10px; height: 9px;
    background: #ffbb68;
    border-radius: 13px 13px 5px 5px;
    box-shadow: inset 0 2px #ffe0a2;
  }
  .logo .lettuce { top: 21px; height: 4px; background: #84df8e; }
  .logo .patty { top: 27px; height: 5px; background: #9a5947; }
  .logo .bun-bottom { top: 34px; height: 5px; background: #f2a958; }
  .eyebrow {
    color: #b59bcc;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: .2em;
    text-transform: uppercase;
    margin-bottom: 3px;
  }
  h1 {
    margin: 0;
    font-size: clamp(23px, 3vw, 34px);
    line-height: 1;
    font-weight: 950;
    letter-spacing: -.055em;
    text-transform: uppercase;
    white-space: nowrap;
  }
  h1 .amp { color: #ff79ab; }
  .stats { display: flex; gap: 8px; flex: 0 0 auto; }
  .stat {
    min-width: 93px;
    padding: 9px 12px 8px;
    border: 1px solid rgba(255,255,255,.1);
    border-radius: 12px;
    background: linear-gradient(145deg, rgba(43,36,60,.9), rgba(27,23,40,.94));
    box-shadow: inset 0 1px rgba(255,255,255,.04);
  }
  .stat-label {
    display: block;
    margin-bottom: 2px;
    color: #a69bb9;
    font-size: 9px;
    letter-spacing: .16em;
    font-weight: 800;
  }
  .stat-value { font-size: 19px; font-weight: 900; line-height: 1; font-variant-numeric: tabular-nums; }
  #lives { color: #ff79ad; letter-spacing: 2px; font-size: 17px; }
  .stage {
    height: clamp(390px, calc(100svh - 250px), 560px);
    position: relative;
    overflow: hidden;
    border-radius: 18px;
    border: 1px solid rgba(224,204,255,.2);
    background: #17142a;
    box-shadow:
      0 25px 70px rgba(0,0,0,.45),
      0 0 0 5px rgba(255,255,255,.025),
      inset 0 1px rgba(255,255,255,.08);
    touch-action: none;
  }
  canvas { width: 100%; height: 100%; display: block; }
  .prompt {
    position: absolute;
    z-index: 2;
    left: 50%; top: 49%;
    transform: translate(-50%, -50%);
    width: min(304px, calc(100% - 30px));
    padding: 13px 16px 12px;
    text-align: center;
    border: 1px solid rgba(255,255,255,.2);
    border-radius: 15px;
    background: rgba(27, 22, 44, .88);
    box-shadow: 0 12px 40px #08061177, inset 0 1px rgba(255,255,255,.1);
    backdrop-filter: blur(8px);
    pointer-events: none;
    transition: opacity .3s ease, transform .3s ease;
  }
  .prompt.hide { opacity: 0; transform: translate(-50%, -44%); }
  .prompt-kicker {
    color: #ffc1d9;
    font-size: 9px;
    font-weight: 900;
    letter-spacing: .2em;
    margin-bottom: 5px;
  }
  .prompt-action {
    font-size: 12px;
    font-weight: 950;
    letter-spacing: .045em;
  }
  .prompt-action i {
    display: inline-block;
    width: 6px; height: 6px; margin: 0 5px 1px 0;
    border-radius: 50%;
    background: #83eea1;
    box-shadow: 0 0 10px #83eea1;
  }
  .footer {
    min-height: 42px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 4px 0;
    color: #a69db8;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
  }
  .footer strong { color: #e8d7fa; }
  .controls { display: flex; gap: 14px; align-items: center; }
  .key {
    display: inline-flex;
    justify-content: center;
    min-width: 21px;
    padding: 3px 5px;
    margin-right: 4px;
    color: #eee5fa;
    border-radius: 5px;
    background: #29243b;
    border: 1px solid #514763;
    font-size: 9px;
    letter-spacing: 0;
  }
  @media (max-width: 620px) {
    body { padding: 13px 14px; }
    .topbar { display: block; margin-bottom: 11px; }
    .brand { gap: 10px; }
    .logo { width: 42px; height: 42px; border-radius: 13px; transform: scale(.94); transform-origin: left center; margin-right: -2px; }
    .logo span { left: 8px; }
    .eyebrow { font-size: 8px; }
    h1 { font-size: clamp(23px, 7vw, 29px); }
    .stats { margin-top: 11px; gap: 7px; }
    .stat { min-width: 0; flex: 1; padding: 7px 9px; }
    .stat-label { font-size: 8px; }
    .stat-value { font-size: 17px; }
    #lives { font-size: 15px; }
    .stage { height: clamp(390px, calc(100svh - 273px), 560px); border-radius: 15px; }
    .footer { font-size: 9px; letter-spacing: .07em; }
    .controls { gap: 7px; }
    .controls > span:last-child { display: none; }
  }
  @media (max-height: 650px) and (min-width: 621px) {
    body { padding-top: 10px; padding-bottom: 10px; }
    .stage { height: calc(100svh - 205px); min-height: 360px; }
  }
</style>
</head>
<body>
  <main class="shell">
    <header class="topbar">
      <div class="brand">
        <div class="logo" aria-hidden="true">
          <span class="bun-top"></span><span class="lettuce"></span>
          <span class="patty"></span><span class="bun-bottom"></span>
        </div>
        <div>
          <div class="eyebrow">The Diner Arcade · Est. 1986</div>
          <h1>Buns <span class="amp">&amp;</span> Breakout</h1>
        </div>
      </div>
      <div class="stats" aria-label="Game stats">
        <div class="stat"><span class="stat-label">SCORE</span><span class="stat-value" id="score">00000</span></div>
        <div class="stat"><span class="stat-label">LEVEL</span><span class="stat-value" id="level">01</span></div>
        <div class="stat"><span class="stat-label">LIVES</span><span class="stat-value" id="lives">♥♥♥</span></div>
      </div>
    </header>

    <section class="stage" id="stage" aria-label="Breakout game">
      <canvas id="game"></canvas>
      <div class="prompt" id="prompt">
        <div class="prompt-kicker">✦ THE DEMO IS LIVE ✦</div>
        <div class="prompt-action"><i></i>CLICK OR PRESS A KEY TO START</div>
      </div>
    </section>

    <footer class="footer">
      <div><strong>Smash bricks.</strong> Make hearts.</div>
      <div class="controls">
        <span><b class="key">←</b><b class="key">→</b> MOVE</span>
        <span><b class="key">A</b><b class="key">D</b> TOO</span>
      </div>
    </footer>
  </main>

<script>
(() => {
  const canvas = document.getElementById('game');
  const ctx = canvas.getContext('2d');
  const stage = document.getElementById('stage');
  const prompt = document.getElementById('prompt');
  const scoreEl = document.getElementById('score');
  const levelEl = document.getElementById('level');
  const livesEl = document.getElementById('lives');

  let W = 0, H = 0, dpr = 1, scale = 1;
  let bricks = [], particles = [], sparks = [], stars = [], trail = [];
  let score = 0, lives = 3, level = 1, manual = false;
  let last = 0, serveTimer = 0, pointerX = null;
  const ball = { x: 0, y: 0, vx: 0, vy: 0, r: 7, prevX: 0, prevY: 0 };
  const paddle = { x: 0, w: 104, y: 0, h: 31 };

  const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
  const rand = (a, b) => a + Math.random() * (b - a);

  function resize() {
    const rect = canvas.getBoundingClientRect();
    if (!rect.width || !rect.height) return;
    W = rect.width; H = rect.height;
    dpr = Math.min(window.devicePixelRatio || 1, 2);
    canvas.width = Math.round(W * dpr);
    canvas.height = Math.round(H * dpr);
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    scale = clamp(Math.min(W / 900, H / 520), .63, 1.18);
    ball.r = clamp(W * .008, 5.5, 8);
    paddle.w = clamp(W * .108, 72, 112);
    paddle.y = H - 43;
    paddle.x = clamp(paddle.x || W / 2 - paddle.w / 2, 4, W - paddle.w - 4);
    stars = Array.from({ length: Math.round(W * H / 9500) }, () => ({
      x: Math.random() * W, y: Math.random() * H,
      r: rand(.5, 1.35), a: rand(.08, .3), phase: rand(0, 6)
    }));
    makeBricks();
    if (!ball.x) resetBall();
  }

  function makeBricks() {
    bricks = [];
    const cols = 9, rows = 6;
    const margin = Math.max(17, W * .055);
    const gap = clamp(W * .009, 3, 9);
    const bw = (W - 2 * margin - gap * (cols - 1)) / cols;
    const bh = clamp(H * .044, 18, 26);
    const top = clamp(H * .135, 56, 82);
    const palette = ['#ff758f', '#ff9b74', '#ffbf70', '#f2d879', '#a8df93', '#9fd5ff'];
    for (let row = 0; row < rows; row++) {
      for (let col = 0; col < cols; col++) {
        bricks.push({
          x: margin + col * (bw + gap), y: top + row * (bh + gap),
          w: bw, h: bh, color: palette[row],
          alive: true, row, col
        });
      }
    }
  }

  function resetBall() {
    ball.x = W * .5;
    ball.y = H * .73;
    const speed = 4.1 * scale;
    ball.vx = (Math.random() < .5 ? -1 : 1) * speed * .62;
    ball.vy = -speed * .82;
    ball.prevX = ball.x;
    ball.prevY = ball.y;
    trail.length = 0;
  }

  function takeOver() {
    if (!manual) {
      manual = true;
      prompt.classList.add('hide');
    }
  }

  function setPointer(e) {
    const r = canvas.getBoundingClientRect();
    pointerX = e.clientX - r.left;
  }

  stage.addEventListener('pointerdown', e => {
    takeOver();
    setPointer(e);
    if (e.pointerType === 'touch') stage.setPointerCapture?.(e.pointerId);
  });
  stage.addEventListener('pointermove', e => {
    if (manual) setPointer(e);
  });
  window.addEventListener('keydown', e => {
    if (['ArrowLeft', 'ArrowRight', 'a', 'A', 'd', 'D', ' '].includes(e.key)) e.preventDefault();
    takeOver();
    if (e.key === 'ArrowLeft' || e.key.toLowerCase() === 'a') pointerX = paddle.x + paddle.w / 2 - 48;
    if (e.key === 'ArrowRight' || e.key.toLowerCase() === 'd') pointerX = paddle.x + paddle.w / 2 + 48;
  });
  window.addEventListener('resize', resize);
  if ('ResizeObserver' in window) new ResizeObserver(resize).observe(stage);

  function heartPath(c, size) {
    c.beginPath();
    c.moveTo(0, size * .38);
    c.bezierCurveTo(-size * .15, size * .22, -size * .55, -size * .08, -size * .47, -size * .38);
    c.bezierCurveTo(-size * .41, -size * .68, -size * .08, -size * .65, 0, -size * .39);
    c.bezierCurveTo(size * .12, -size * .67, size * .45, -size * .64, size * .49, -size * .36);
    c.bezierCurveTo(size * .55, -size * .06, size * .16, size * .23, 0, size * .38);
    c.closePath();
  }

  function burst(x, y) {
    const colors = ['#ff75ad', '#ff9bc4', '#ffd0e3', '#d9a7ff', '#ffffff', '#ff5f9c'];
    for (let i = 0; i < 31; i++) {
      const angle = Math.random() * Math.PI * 2;
      const speed = rand(1.1, 4.6) * scale;
      particles.push({
        x, y, vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed - rand(.2, 1.3) * scale,
        size: rand(3.4, 7.7) * scale, life: rand(.8, 1.45),
        max: 1.45, color: colors[(Math.random() * colors.length) | 0],
        rot: rand(-1, 1), spin: rand(-3, 3)
      });
    }
    for (let i = 0; i < 10; i++) {
      const a = Math.random() * Math.PI * 2;
      sparks.push({
        x, y, vx: Math.cos(a) * rand(1.4, 4.3) * scale,
        vy: Math.sin(a) * rand(1.4, 4.3) * scale,
        life: rand(.22, .48), max: .48, r: rand(1, 2.3) * scale
      });
    }
  }

  function drawHeart(x, y, size, color, alpha, rot) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(rot);
    ctx.globalAlpha = alpha;
    ctx.fillStyle = color;
    ctx.shadowColor = color;
    ctx.shadowBlur = size * 1.7;
    heartPath(ctx, size);
    ctx.fill();
    ctx.restore();
  }

  function drawBurger() {
    const x = paddle.x, y = paddle.y, w = paddle.w;
    ctx.save();
    ctx.shadowColor = '#ff9b58aa';
    ctx.shadowBlur = 15;
    // Bottom bun and juicy patty.
    ctx.fillStyle = '#c7793e';
    ctx.beginPath(); ctx.roundRect(x + 3, y - 11, w - 6, 15, 8); ctx.fill();
    ctx.fillStyle = '#61362e';
    ctx.beginPath(); ctx.roundRect(x + 1, y - 15, w - 2, 9, 5); ctx.fill();
    // Cheese peeking out.
    ctx.fillStyle = '#ffd45e';
    ctx.beginPath();
    ctx.moveTo(x + 12, y - 16); ctx.lineTo(x + w - 8, y - 16);
    ctx.lineTo(x + w - 17, y - 8); ctx.lineTo(x + w * .62, y - 13);
    ctx.lineTo(x + w * .44, y - 7); ctx.lineTo(x + w * .27, y - 14);
    ctx.lineTo(x + 12, y - 12); ctx.closePath(); ctx.fill();
    // Wavy lettuce.
    ctx.fillStyle = '#7bdd72';
    ctx.beginPath();
    ctx.moveTo(x + 3, y - 20);
    for (let i = 0; i <= 8; i++) {
      const px = x + 4 + (w - 8) * i / 8;
      ctx.lineTo(px, y - 19 + (i % 2 ? 3 : 0));
    }
    ctx.lineTo(x + w - 4, y - 15);
    for (let i = 8; i >= 0; i--) {
      const px = x + 4 + (w - 8) * i / 8;
      ctx.lineTo(px, y - 17 + (i % 2 ? 2 : 0));
    }
    ctx.closePath(); ctx.fill();
    // Golden domed top bun.
    const g = ctx.createLinearGradient(0, y - 34, 0, y - 17);
    g.addColorStop(0, '#ffe29a'); g.addColorStop(.5, '#ffbd66'); g.addColorStop(1, '#ec914a');
    ctx.fillStyle = g;
    ctx.beginPath();
    ctx.moveTo(x + 4, y - 18);
    ctx.quadraticCurveTo(x + 5, y - 34, x + w / 2, y - 34);
    ctx.quadraticCurveTo(x + w - 5, y - 34, x + w - 4, y - 18);
    ctx.quadraticCurveTo(x + w / 2, y - 14, x + 4, y - 18);
    ctx.fill();
    ctx.shadowBlur = 0;
    ctx.fillStyle = '#fff0bf';
    for (const [sx, sy] of [[.27,.27],[.4,.14],[.57,.18],[.72,.3],[.49,.31]]) {
      ctx.save(); ctx.translate(x + w * sx, y - 34 + 17 * sy); ctx.rotate(-.35);
      ctx.beginPath(); ctx.ellipse(0, 0, 2.1 * scale, .85 * scale, 0, 0, Math.PI * 2); ctx.fill();
      ctx.restore();
    }
    ctx.restore();
  }

  function drawBackground(t) {
    const bg = ctx.createLinearGradient(0, 0, 0, H);
    bg.addColorStop(0, '#1b1730'); bg.addColorStop(.52, '#17152a'); bg.addColorStop(1, '#21182e');
    ctx.fillStyle = bg; ctx.fillRect(0, 0, W, H);
    const glow = ctx.createRadialGradient(W * .5, H * .34, 10, W * .5, H * .34, W * .72);
    glow.addColorStop(0, 'rgba(117,83,156,.13)'); glow.addColorStop(1, 'rgba(117,83,156,0)');
    ctx.fillStyle = glow; ctx.fillRect(0, 0, W, H);
    ctx.save();
    for (const s of stars) {
      ctx.globalAlpha = s.a * (.72 + Math.sin(t * 1.4 + s.phase) * .28);
      ctx.fillStyle = '#e5d2ff';
      ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); ctx.fill();
    }
    ctx.restore();
    ctx.strokeStyle = 'rgba(209,187,246,.035)';
    ctx.lineWidth = 1;
    for (let y = 30; y < H; y += 42) {
      ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke();
    }
    const floor = ctx.createLinearGradient(0, H - 17, 0, H);
    floor.addColorStop(0, 'rgba(255,129,181,.04)'); floor.addColorStop(1, 'rgba(255,129,181,.13)');
    ctx.fillStyle = floor; ctx.fillRect(0, H - 16, W, 16);
    ctx.strokeStyle = 'rgba(255,153,197,.22)';
    ctx.beginPath(); ctx.moveTo(0, H - 16); ctx.lineTo(W, H - 16); ctx.stroke();
  }

  function drawBricks() {
    for (const b of bricks) {
      if (!b.alive) continue;
      ctx.save();
      ctx.shadowColor = b.color + '55';
      ctx.shadowBlur = 9;
      const grad = ctx.createLinearGradient(0, b.y, 0, b.y + b.h);
      grad.addColorStop(0, b.color);
      grad.addColorStop(1, shade(b.color, -.17));
      ctx.fillStyle = grad;
      ctx.beginPath(); ctx.roundRect(b.x, b.y, b.w, b.h, Math.min(6, b.h * .28)); ctx.fill();
      ctx.shadowBlur = 0;
      ctx.fillStyle = 'rgba(255,255,255,.24)';
      ctx.beginPath(); ctx.roundRect(b.x + 3, b.y + 2, b.w - 6, Math.max(2, b.h * .19), 3); ctx.fill();
      ctx.fillStyle = 'rgba(255,255,255,.23)';
      ctx.beginPath(); ctx.arc(b.x + b.w - 7, b.y + b.h / 2, 1.25, 0, Math.PI * 2); ctx.fill();
      ctx.restore();
    }
  }

  function shade(hex, amount) {
    const n = parseInt(hex.slice(1), 16);
    const r = clamp((n >> 16) + Math.round(255 * amount), 0, 255);
    const g = clamp(((n >> 8) & 255) + Math.round(255 * amount), 0, 255);
    const b = clamp((n & 255) + Math.round(255 * amount), 0, 255);
    return `rgb(${r},${g},${b})`;
  }

  function update(dt) {
    const frame = Math.min(dt, .032) * 60;
    const seconds = Math.min(dt, .032);
    const target = manual && pointerX !== null
      ? pointerX - paddle.w / 2
      : ball.x - paddle.w / 2;
    const ease = manual ? 1 : .17;
    paddle.x += (clamp(target, 4, W - paddle.w - 4) - paddle.x) * ease;
    paddle.x = clamp(paddle.x, 4, W - paddle.w - 4);

    if (serveTimer > 0) {
      serveTimer -= seconds;
      if (serveTimer <= 0) resetBall();
    } else {
      ball.prevX = ball.x; ball.prevY = ball.y;
      ball.x += ball.vx * frame;
      ball.y += ball.vy * frame;
      trail.push({ x: ball.x, y: ball.y, life: 1 });
      if (trail.length > 10) trail.shift();

      if (ball.x < ball.r) { ball.x = ball.r; ball.vx = Math.abs(ball.vx); }
      if (ball.x > W - ball.r) { ball.x = W - ball.r; ball.vx = -Math.abs(ball.vx); }
      if (ball.y < 13 + ball.r) { ball.y = 13 + ball.r; ball.vy = Math.abs(ball.vy); }

      for (const b of bricks) {
        if (!b.alive) continue;
        const cx = clamp(ball.x, b.x, b.x + b.w);
        const cy = clamp(ball.y, b.y, b.y + b.h);
        const dx = ball.x - cx, dy = ball.y - cy;
        if (dx * dx + dy * dy < ball.r * ball.r) {
          b.alive = false;
          score += 100;
          scoreEl.textContent = String(score).padStart(5, '0');
          burst(cx, cy);
          if (ball.prevY + ball.r <= b.y || ball.prevY - ball.r >= b.y + b.h) ball.vy *= -1;
          else ball.vx *= -1;
          // Nudge clear of the brick to prevent repeat collisions.
          ball.x += Math.sign(ball.vx) * 1.5;
          ball.y += Math.sign(ball.vy) * 1.5;
          break;
        }
      }

      const paddleTop = paddle.y - 23;
      if (ball.vy > 0 && ball.y + ball.r >= paddleTop &&
          ball.y - ball.r <= paddle.y + 3 &&
          ball.x >= paddle.x - 2 && ball.x <= paddle.x + paddle.w + 2) {
        const hit = clamp((ball.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2), -1, 1);
        const speed = Math.max(Math.hypot(ball.vx, ball.vy), 3.3 * scale);
        ball.vx = hit * speed * .83;
        ball.vy = -Math.sqrt(Math.max(1, speed * speed - ball.vx * ball.vx));
        ball.y = paddleTop - ball.r - 1;
        sparks.push({ x: ball.x, y: paddleTop, vx: rand(-1, 1), vy: -2 * scale, life: .3, max: .3, r: 3 * scale });
      }

      if (ball.y > H + 15) {
        lives--;
        if (lives <= 0) {
          lives = 3;
          score = 0;
          scoreEl.textContent = '00000';
        }
        livesEl.textContent = '♥'.repeat(lives) + '♡'.repeat(3 - lives);
        serveTimer = .62;
        ball.x = paddle.x + paddle.w / 2;
        ball.y = paddle.y - 35;
      }
    }

    for (const p of particles) {
      p.life -= seconds;
      p.x += p.vx * frame; p.y += p.vy * frame;
      p.vy += .075 * scale * frame;
      p.vx *= Math.pow(.994, frame);
      p.rot += p.spin * seconds;
    }
    particles = particles.filter(p => p.life > 0);
    for (const s of sparks) {
      s.life -= seconds; s.x += s.vx * frame; s.y += s.vy * frame;
      s.vy += .06 * frame;
    }
    sparks = sparks.filter(s => s.life > 0);
    for (const t of trail) t.life -= .1 * frame;

    if (bricks.length && bricks.every(b => !b.alive)) {
      level++;
      levelEl.textContent = String(level).padStart(2, '0');
      makeBricks();
      resetBall();
    }
  }

  function render(t) {
    drawBackground(t);
    drawBricks();

    for (const tr of trail) {
      if (tr.life <= 0) continue;
      const a = tr.life * .16;
      ctx.beginPath();
      ctx.fillStyle = `rgba(255,174,211,${a})`;
      ctx.arc(tr.x, tr.y, ball.r * tr.life * .85, 0, Math.PI * 2);
      ctx.fill();
    }

    for (const p of particles) {
      const alpha = clamp(p.life / p.max, 0, 1);
      drawHeart(p.x, p.y, p.size * (.68 + alpha * .32), p.color, alpha, p.rot);
    }
    for (const s of sparks) {
      ctx.globalAlpha = clamp(s.life / s.max, 0, 1);
      ctx.fillStyle = '#fff0c6';
      ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); ctx.fill();
    }
    ctx.globalAlpha = 1;

    // Glowing arcade ball.
    ctx.save();
    ctx.shadowColor = '#fff4cf'; ctx.shadowBlur = 18;
    const ballGrad = ctx.createRadialGradient(ball.x - ball.r * .32, ball.y - ball.r * .4, 1, ball.x, ball.y, ball.r * 1.25);
    ballGrad.addColorStop(0, '#ffffff');
    ballGrad.addColorStop(.48, '#fff1bd');
    ballGrad.addColorStop(1, '#ffb982');
    ctx.fillStyle = ballGrad;
    ctx.beginPath(); ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2); ctx.fill();
    ctx.restore();

    drawBurger();
  }

  function loop(now) {
    if (!last) last = now;
    const dt = Math.min((now - last) / 1000, .04);
    last = now;
    update(dt);
    render(now / 1000);
    requestAnimationFrame(loop);
  }

  resize();
  resetBall();
  requestAnimationFrame(loop);
})();
</script>
</body>
</html>
Reasoning trace (4,985 tokens)
**Creating a self-playing game demo**

I need to create precise code for a self-playing demo that activates when clicking or pressing a key. The initial action should be visible for the first three seconds. I want to develop a polished breakout game with a fun hamburger paddle and bricks that explode into hearts. Canvas size should be 1280x800. It should have a stylish retro neon arcade design with animated hearts around the bricks. The overlay will prompt users to start the game while the AI plays automatically.**Designing a visually compelling game**

I’m working on a game interface that features a self-playing AI. The prompt will be "WATCH THE DEMO • CLICK / PRESS ANY KEY TO TAKE OVER," and I might use arrow keys for control. I need to ensure the click overlay doesn’t interfere with gameplay. The design should be captivating, with a dark plum navy background and custom gradients for a rounded brick layout. The game cabinet will be centered in a 1280x800 shell. I want the main game area canvas to be about 960x570, with a HUD for scores and levels at the top.**Adjusting the game layout**

I'm fine-tuning the screen layout, set at 1280 pixels with a 100-pixel margin. The header will be about 90 pixels, and the canvas will be 1000x550, with a footer of 50 pixels, totaling 710. That looks good! At a width of 375 pixels, I'll need to adapt the header to be more compact, making the game canvas width 100% and potentially adjusting the height to around 560. I want to ensure the aspect ratio stays appropriate for portrait displays.**Evaluating canvas settings**

I'm thinking about a canvas height of 540 and ensuring there’s no horizontal scroll. The main page padding is set to 14, while on mobile, the game width is 347. I need to make sure the internal canvas coordinates are fixed and logical, at 1000x560, and rendered correctly. The aspect ratio is 1000/560, which equals about 1.786. However, with a mobile height of 194, it's too small. It might help to adjust dimensions based on CSS size and set resolution according to Device Pixel Ratio (DPR).**Adjusting canvas dimensions**

I'm considering the canvas's CSS, which has a dynamic height clamped between 390 and 560, with mobile set to 410. The world coordinates need to update based on the canvas's client width and height. I think keeping a simpler canvas size of 1000 by 560 and adjusting CSS to clamp between certain values will help. There’s an issue with stretching and distortion, especially for mobile. I need to ensure the drawing resolution matches the pixel density and set the canvas dynamically for each resize. I'll recalculate everything based on the CSS dimensions.**Designing mobile layout**

For mobile screens sized around 347 by 430, I want to use relative placements for the grid. I'll create a brick layout with 10 columns, where the width is calculated based on the screen dimensions minus margins and gaps. The brick height should be roughly 4% of the screen height, and the ball size is about 8. If the paddle width scales based on the screen width, it would go from 100 at 1000 down to 62 at 347. I plan for 6 rows of bricks. Overall, it’s looking good!**Considering game overlay options**

I'm thinking about adding an overlay to the game center that says "AUTO-PILOT DEMO," along with a sub-message, "CLICK / PRESS ANY KEY TO TAKE OVER," positioned at the center bottom over gameplay. I imagine it could have a dark semi-transparent background, but I’m unsure if that might obstruct things too much. Alternatively, placing a narrower message in the top-left corner could work, like "DEMO • click/key to take over." A button overlay in the top bar could also be a viable option!**Designing game UI**

I'm thinking about creating a centered overlay for the game that shows a prompt like "CLICK OR PRESS A KEY TO START". This will have a minimal visual impact, perhaps with a dark translucent panel so that the game is still visible behind it. The overlay should ideally appear for around 5 seconds before the user can take control. I want to make sure the title "DEMO IS LIVE" is clear and that the floating label is positioned well at the upper canvas center.**Adjusting overlay positioning**

I'm determining that the overlay should be placed near the bottom of the game, around 70%, and I want to ensure that it doesn't block important elements like the ball and paddle. For visibility, I might set dimensions of 280x52 and center it at around a certain height that doesn't obstruct too much. The overlay could have a message like "DEMO LIVE · CLICK OR PRESS A KEY TO START". Also, I'll need to manage user interactions correctly for keyboard events, ensuring the overlay reacts appropriately.**Designing game overlay and aesthetics**

I'm thinking about how to set up a button for the call-to-action on the overlay, ensuring that pointer clicks are recognized by the canvas. For the game's background, I want to use a deep indigo gradient with vertical strips, maybe adding stars and grid lines for visual interest. The score will be displayed in an external HTML element to keep the canvas clean. I also want to ensure the canvas adapts to resizing while keeping the game elements accessible and visually appealing. Using a CSS-styled burger icon for the header could be a fun touch!**Refining game mechanics**

I’m figuring out the scales for actual dimensions, adjusting based on width and mobile settings. The ball's movements involve bounces off walls and paddle interactions. For the AI paddle, I want it to follow the ball simply without overly complex predictions, moving smoothly towards the target. On collisions with bricks, if the ball misses, I’m considering a reset to the center and reducing lives. I need it to reflect realistic bounces based on hit location too.**Balancing game mechanics**

I’m thinking about how to handle game restarts. If the player completes all the bricks, I want to end the game and reset everything—bricks, levels, and maybe even the score. It seems important to make the transition smooth so players can jump right back in without feeling lost. I want each restart to feel fresh but familiar. That way, it's engaging and encourages continued play!**Evaluating collision mechanics**

I'm looking into how to determine the overlap axis for a ball and rectangle collision. If the ball overlaps, I need to figure out if it can pass by implementing collision directions. I’ll decide based on the previous position and velocity. When the ball hits a brick, it should be destroyed, creating heart particles and a score update. I’ll also make sure particles have randomized colors and sizes, and implement a nice glowing trail for visual effects.**Designing the paddle and burger**

I’m focusing on drawing the paddle as a hamburger, starting with the shadow and the top bun in a nice orange gradient. I’ll give it some sesame seeds and bright green zigzag lettuce for effect. The paddle dimensions will be around 106 wide and 28 tall, with a rounded bottom bun look. I also need to add layers of meat and cheese and make sure the burger evokes that classic shape horizontally. The ball might hit this paddle, so its positioning is crucial.**Creating the HUD layout**

I'm thinking about designing the HUD with three pink hearts for lives, using CSS and possibly HTML. I’ll include a score display and a combo counter as well. The header panel should have a maximum width of 1120 pixels to ensure it looks good on the screen. I want to keep everything visually appealing and organized, so the layout feels intuitive and engaging for the user. This setup will help enhance the overall gaming experience!**Considering mobile game design**

I'm thinking about the mobile game's weird yet dynamic coordinates. For the body background, I can use CSS for decorative dotted patterns without external resources. The header will include a score UI with a status card, kicker, logo, and an `h1` title alongside a small tagline. The stats should flexibly display three items while keeping them in line on mobile. 

I'll have a footer with controls like "MOVE" and "LAUNCH," with a simple progress bar and an overlay message that prompts gameplay.**Designing user interactions**

I need to keep the prompt visible until the user interacts with it. This instruction should be behind a "Click or press a key to start" message. The prompt will say, "CLICK OR PRESS A KEY TO START," even if the demo is auto-active, with supertext indicating "AUTOPILOT IS PLAYING." This doesn't start the game yet but prompts the user to take over. Upon clicking, it switches to manual control and hides the prompt. I should also consider using the space key as a trigger for this.