Models 44

Claude Haiku 5.5

by AnthropicReasoningClean
Open in new window

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

How it went

Total time
42.3 s
Cost
$0.0057
Output tokens
11,244
Spent thinking
2,411 tokens
Speed
278.8 tok/s
Console errors
0
First token after
1.9 s
First visible text after
13.7 s
Prompt tokens
542
Code size
17.3 KB
Finish reason
stop
Served by
Claude Platform on AWS
Model id
anthropic/claude-haiku-5.5
Reasoning sent
{"effort":"low"}
Max output
32,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 · 576 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hamburger Breakout</title>
<style>
  * { box-sizing: border-box; }
  html, body {
    margin: 0; height: 100%; overflow: hidden;
    background: radial-gradient(ellipse at top, #ffe3ef 0%, #ffc1d9 45%, #f48fb1 100%);
    font-family: "Trebuchet MS", "Segoe UI", Helvetica, Arial, sans-serif;
    color: #5a1830;
  }
  body { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 12px; }
  .hud {
    width: min(100%, 800px);
    display: flex; justify-content: space-between; align-items: center;
    font-weight: bold; font-size: clamp(14px, 2.4vw, 20px);
    margin-bottom: 8px; padding: 0 4px;
    text-shadow: 0 1px 0 rgba(255,255,255,.6);
  }
  .hud .lives { color: #d81b60; letter-spacing: 2px; }
  .stage {
    position: relative;
    width: min(100%, 800px);
    aspect-ratio: 4 / 3;
    border-radius: 18px;
    overflow: hidden;
    box-shadow: 0 12px 40px rgba(160, 20, 80, .35), inset 0 0 0 4px rgba(255,255,255,.6);
    background: #fff3f8;
    cursor: pointer;
    touch-action: none;
  }
  canvas { width: 100%; height: 100%; display: block; }
  .overlay {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    pointer-events: none;
    transition: opacity .4s ease;
  }
  .overlay.hidden { opacity: 0; }
  .msg {
    background: rgba(255,255,255,.88);
    padding: 14px 26px; border-radius: 999px;
    font-size: clamp(16px, 3vw, 24px); font-weight: bold; color: #b0124f;
    box-shadow: 0 6px 20px rgba(176,18,79,.25);
    animation: pulse 1.6s ease-in-out infinite;
    text-align: center;
  }
  .sub { margin-top: 10px; font-size: 14px; color: #7a2a4a; background: rgba(255,255,255,.7); padding: 4px 12px; border-radius: 10px; }
  @keyframes pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.05); } }
  .footer { margin-top: 8px; font-size: 12px; color: #7a2a4a; opacity: .8; text-align: center; }
  @media (max-width: 420px) {
    .footer { display: none; }
  }
</style>
</head>
<body>
  <div class="hud">
    <div>Score: <span id="score">0</span></div>
    <div>Level: <span id="level">1</span></div>
    <div class="lives" id="lives">❤❤❤</div>
  </div>
  <div class="stage" id="stage">
    <canvas id="c" width="800" height="600"></canvas>
    <div class="overlay" id="overlay">
      <div class="msg" id="msg">Click or press a key to start</div>
      <div class="sub" id="sub">Watch the hamburger play itself</div>
    </div>
  </div>
  <div class="footer">Move the mouse / finger or use ← → (A / D) to steer the burger.</div>

<script>
(() => {
  const W = 800, H = 600;
  const canvas = document.getElementById('c');
  const ctx = canvas.getContext('2d');
  const overlay = document.getElementById('overlay');
  const msgEl = document.getElementById('msg');
  const subEl = document.getElementById('sub');
  const scoreEl = document.getElementById('score');
  const levelEl = document.getElementById('level');
  const livesEl = document.getElementById('lives');
  const stage = document.getElementById('stage');

  const COLORS = ['#ff4d8d', '#ff7aa8', '#ff2f6e', '#ff9ec4', '#e8385f', '#ffb3d1'];

  let mode = 'demo';      // 'demo' | 'play' | 'over'
  let started = false;
  let score = 0, level = 1, lives = 3;
  let bricks = [], hearts = [], ballTrail = [];
  let keys = {};
  let mouseX = null;
  let levelTimer = 0;
  let launchTimer = 0;
  let flash = 0;
  let shake = 0;

  const paddle = { w: 130, h: 44, x: W / 2 - 65, y: H - 50, speed: 0 };
  const ball = { x: W / 2, y: H - 120, r: 9, vx: 0, vy: 0, attached: true };

  // ambient floating hearts in background
  const ambient = Array.from({ length: 16 }, () => ({
    x: Math.random() * W, y: Math.random() * H, s: 6 + Math.random() * 10,
    sp: 0.2 + Math.random() * 0.4, ph: Math.random() * 6.28
  }));

  function heartPath(c, x, y, s) {
    c.beginPath();
    c.moveTo(x, y + s * 0.35);
    c.bezierCurveTo(x, y - s * 0.3, x - s, y - s * 0.3, x - s, y + s * 0.35);
    c.bezierCurveTo(x - s, y + s * 0.75, x - s * 0.4, y + s * 0.95, x, y + s * 1.3);
    c.bezierCurveTo(x + s * 0.4, y + s * 0.95, x + s, y + s * 0.75, x + s, y + s * 0.35);
    c.bezierCurveTo(x + s, y - s * 0.3, x, y - s * 0.3, x, y + s * 0.35);
    c.closePath();
  }

  function buildLevel() {
    bricks = [];
    const rows = Math.min(4 + level, 9), cols = 10;
    const gap = 6, pad = 40, top = 70;
    const bw = (W - pad * 2 - gap * (cols - 1)) / cols;
    const bh = 22;
    for (let r = 0; r < rows; r++) {
      for (let c = 0; c < cols; c++) {
        bricks.push({
          x: pad + c * (bw + gap), y: top + r * (bh + gap), w: bw, h: bh,
          alive: true, color: COLORS[(r + level) % COLORS.length], row: r
        });
      }
    }
  }

  function resetBall() {
    ball.attached = true;
    ball.vx = 0; ball.vy = 0;
    ball.x = paddle.x + paddle.w / 2;
    ball.y = paddle.y - ball.r - 2;
    launchTimer = 0;
    ballTrail = [];
  }

  function launchBall() {
    if (!ball.attached) return;
    ball.attached = false;
    const speed = 5 + (level - 1) * 0.4;
    const angle = (-Math.PI / 2) + (Math.random() - 0.5) * 0.9;
    ball.vx = Math.cos(angle) * speed;
    ball.vy = Math.sin(angle) * speed;
  }

  function burstHearts(x, y, count, power) {
    for (let i = 0; i < count; i++) {
      const a = Math.random() * Math.PI * 2;
      const sp = (Math.random() * 4 + 1.5) * power;
      hearts.push({
        x, y,
        vx: Math.cos(a) * sp,
        vy: Math.sin(a) * sp - 2.5 * power,
        s: 2.5 + Math.random() * 4.5,
        rot: Math.random() * 6.28,
        vr: (Math.random() - 0.5) * 0.3,
        life: 1,
        decay: 0.008 + Math.random() * 0.01,
        color: ['#ff3d7f', '#ff6f9f', '#e0245e', '#ffffff', '#ffc2d8', '#ff1f5a'][Math.floor(Math.random() * 6)]
      });
    }
  }

  function loseLife() {
    lives--;
    shake = 12;
    if (lives <= 0) {
      setMode('over');
    } else {
      resetBall();
    }
    updateHud();
  }

  function setMode(m) {
    mode = m;
    if (m === 'over') {
      started = false;
      msgEl.textContent = 'Game over! Click or press a key to play again';
      subEl.textContent = 'The burger is hungry for more hearts';
      overlay.classList.remove('hidden');
      score = 0; level = 1; lives = 3;
      buildLevel();
      resetBall();
      hearts = [];
      updateHud();
    }
  }

  function startPlay() {
    if (started) return;
    started = true;
    if (mode === 'over') {
      mode = 'demo';
    }
    if (lives <= 0 || lives === 3 && score === 0 && level === 1 && mode !== 'play') {
      // fresh state already ready
    }
    mode = 'play';
    lives = lives <= 0 ? 3 : lives;
    overlay.classList.add('hidden');
    updateHud();
    resetBall();
    setTimeout(() => { if (mode === 'play') launchBall(); }, 300);
  }

  function updateHud() {
    scoreEl.textContent = score;
    levelEl.textContent = level;
    livesEl.textContent = lives > 0 ? '❤'.repeat(lives) : '♡♡♡';
  }

  // Input
  function setPaddleFromClientX(clientX) {
    const rect = canvas.getBoundingClientRect();
    const x = (clientX - rect.left) / rect.width * W;
    mouseX = x;
  }
  canvas.addEventListener('mousemove', e => {
    setPaddleFromClientX(e.clientX);
    if (mode === 'demo') { /* let demo play */ }
  });
  canvas.addEventListener('touchmove', e => {
    e.preventDefault();
    setPaddleFromClientX(e.touches[0].clientX);
  }, { passive: false });
  canvas.addEventListener('touchstart', e => {
    e.preventDefault();
    setPaddleFromClientX(e.touches[0].clientX);
    beginFromUser();
  }, { passive: false });
  stage.addEventListener('click', beginFromUser);
  window.addEventListener('keydown', e => {
    if (['ArrowLeft', 'ArrowRight', ' '].includes(e.key)) e.preventDefault();
    keys[e.key.toLowerCase()] = true;
    beginFromUser();
    if (e.key === ' ' && mode === 'play') launchBall();
  });
  window.addEventListener('keyup', e => { keys[e.key.toLowerCase()] = false; });

  function beginFromUser() {
    if (!started) {
      if (mode === 'over') { mode = 'demo'; }
      startPlay();
    }
  }

  // Update
  function updatePaddle() {
    let target = paddle.x;
    if (mode === 'play') {
      if (mouseX !== null) {
        target = mouseX - paddle.w / 2;
      }
      if (keys['arrowleft'] || keys['a']) target -= 10;
      if (keys['arrowright'] || keys['d']) target += 10;
      paddle.x += (target - paddle.x) * 0.45;
      if (keys['arrowleft'] || keys['a'] || keys['arrowright'] || keys['d']) {
        // direct key movement already applied
      }
    } else {
      // AI demo: follow the ball with slight lag and human-ish error
      const aim = ball.attached ? W / 2 : ball.x;
      const wobble = Math.sin(performance.now() / 500) * 18;
      const want = aim - paddle.w / 2 + wobble * (ball.vy > 0 ? 1 : 0.4);
      const diff = want - paddle.x;
      paddle.x += Math.max(-7.5, Math.min(7.5, diff * 0.18));
    }
    paddle.x = Math.max(0, Math.min(W - paddle.w, paddle.x));
  }

  function updateBall() {
    if (ball.attached) {
      ball.x = paddle.x + paddle.w / 2;
      ball.y = paddle.y - ball.r - 2;
      if (mode === 'demo' || mode === 'play') {
        launchTimer++;
        if (launchTimer > 50) launchBall();
      }
      return;
    }

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

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

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

    // paddle
    if (ball.vy > 0 &&
        ball.y + ball.r >= paddle.y && ball.y + ball.r <= paddle.y + paddle.h + 10 &&
        ball.x >= paddle.x - ball.r && ball.x <= paddle.x + paddle.w + ball.r) {
      const hit = (ball.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2);
      const speed = Math.hypot(ball.vx, ball.vy);
      const maxAngle = 1.15;
      const ang = hit * maxAngle;
      ball.vx = Math.sin(ang) * speed;
      ball.vy = -Math.cos(ang) * speed;
      ball.y = paddle.y - ball.r;
      // little squish of hearts when bouncing off the burger
      if (Math.random() < 0.4) burstHearts(ball.x, paddle.y, 3, 0.5);
    }

    // bricks
    for (const b of bricks) {
      if (!b.alive) continue;
      if (ball.x + ball.r < b.x || ball.x - ball.r > b.x + b.w ||
          ball.y + ball.r < b.y || ball.y - ball.r > b.y + b.h) continue;

      b.alive = false;
      score += 10 * (bricks.length > 0 ? (10 - b.row) : 1);
      const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
      burstHearts(cx, cy, 34, 1.2);
      flash = 0.35;
      shake = Math.max(shake, 4);

      const overlapL = (ball.x + ball.r) - b.x;
      const overlapR = (b.x + b.w) - (ball.x - ball.r);
      const overlapT = (ball.y + ball.r) - b.y;
      const overlapB = (b.y + b.h) - (ball.y - ball.r);
      const minX = Math.min(overlapL, overlapR);
      const minY = Math.min(overlapT, overlapB);
      if (minX < minY) ball.vx = -ball.vx; else ball.vy = -ball.vy;
      updateHud();
      break;
    }

    if (bricks.every(b => !b.alive) && levelTimer === 0) {
      levelTimer = 1;
      setTimeout(() => {
        level++;
        buildLevel();
        resetBall();
        levelTimer = 0;
        updateHud();
      }, 1200);
    }

    // lost
    if (ball.y - ball.r > H) {
      if (mode === 'demo') {
        resetBall();
      } else {
        loseLife();
        if (mode === 'play') resetBall();
      }
    }
  }

  function updateHearts() {
    for (const h of hearts) {
      h.vy += 0.12;
      h.vx *= 0.99;
      h.x += h.vx;
      h.y += h.vy;
      h.rot += h.vr;
      h.life -= h.decay;
      if (h.y > H + 20) h.life = 0;
    }
    hearts = hearts.filter(h => h.life > 0);
    if (hearts.length > 900) hearts.splice(0, hearts.length - 900);
  }

  // Draw
  function drawBackground() {
    const g = ctx.createLinearGradient(0, 0, 0, H);
    g.addColorStop(0, '#fff0f6');
    g.addColorStop(1, '#ffd0e2');
    ctx.fillStyle = g;
    ctx.fillRect(0, 0, W, H);

    // ambient hearts
    ctx.fillStyle = 'rgba(255, 120, 165, 0.14)';
    const t = performance.now() / 1000;
    for (const a of ambient) {
      const y = (a.y - t * a.sp * 20) % H;
      const yy = y < 0 ? y + H : y;
      const x = a.x + Math.sin(t + a.ph) * 14;
      heartPath(ctx, x, yy, a.s / 2);
      ctx.fill();
    }
  }

  function drawBricks() {
    for (const b of bricks) {
      if (!b.alive) continue;
      const g = ctx.createLinearGradient(b.x, b.y, b.x, b.y + b.h);
      g.addColorStop(0, '#ffffff');
      g.addColorStop(0.25, b.color);
      g.addColorStop(1, shade(b.color, -25));
      ctx.fillStyle = g;
      roundRect(b.x, b.y, b.w, b.h, 7);
      ctx.fill();
      ctx.fillStyle = 'rgba(255,255,255,0.5)';
      roundRect(b.x + 4, b.y + 3, b.w - 8, 4, 2);
      ctx.fill();
    }
  }

  function drawHamburger(x, y, w, h) {
    // bottom bun
    ctx.fillStyle = '#d98b3a';
    roundRect(x, y + h - 10, w, 10, 5); ctx.fill();
    ctx.fillStyle = '#f0a955';
    roundRect(x + 4, y + h - 10, w - 8, 3, 2); ctx.fill();
    // patty
    ctx.fillStyle = '#5a2e1a';
    roundRect(x + 2, y + h - 18, w - 4, 9, 4); ctx.fill();
    // cheese (drips)
    ctx.fillStyle = '#ffc93c';
    ctx.beginPath();
    ctx.moveTo(x, y + h - 19);
    for (let i = 0; i <= 10; i++) {
      const px = x + (w / 10) * i;
      const py = y + h - 19 + (i % 2 ? 4 : 0);
      ctx.lineTo(px, py);
    }
    ctx.lineTo(x + w, y + h - 17);
    ctx.lineTo(x, y + h - 17);
    ctx.closePath(); ctx.fill();
    // lettuce (wavy)
    ctx.fillStyle = '#5cd65c';
    ctx.beginPath();
    ctx.moveTo(x - 2, y + h - 22);
    const n = 12;
    for (let i = 0; i <= n; i++) {
      const px = x - 2 + ((w + 4) / n) * i;
      const py = y + h - 24 + (i % 2 ? 4 : 0);
      ctx.lineTo(px, py);
    }
    ctx.lineTo(x + w + 2, y + h - 18);
    ctx.lineTo(x - 2, y + h - 18);
    ctx.closePath(); ctx.fill();
    // tomato
    ctx.fillStyle = '#ff4a4a';
    roundRect(x + 6, y + h - 27, w - 12, 5, 3); ctx.fill();
    // top bun
    ctx.fillStyle = '#e9a048';
    ctx.beginPath();
    ctx.moveTo(x, y + 14);
    ctx.quadraticCurveTo(x, y - 4, x + w / 2, y - 4);
    ctx.quadraticCurveTo(x + w, y - 4, x + w, y + 14);
    ctx.closePath(); ctx.fill();
    ctx.fillStyle = '#f7bd6a';
    ctx.beginPath();
    ctx.ellipse(x + w / 2, y + 2, w / 2 - 6, 5, 0, 0, Math.PI * 2);
    ctx.fill();
    // sesame seeds
    ctx.fillStyle = '#fff6d8';
    for (let i = 0; i < 8; i++) {
      const sx = x + 14 + (w - 28) * (i / 7);
      const sy = y + 2 + Math.sin(i * 1.7) * 3 + (i % 2) * 2;
      ctx.beginPath(); ctx.ellipse(sx, sy, 2.4, 1.3, 0.4, 0, Math.PI * 2); ctx.fill();
    }
  }

  function drawPaddle() {
    drawHamburger(paddle.x, paddle.y, paddle.w, paddle.h);
  }

  function drawBall() {
    // trail hearts
    for (let i = 0; i < ballTrail.length; i++) {
      const p = ballTrail[i];
      const a = i / ballTrail.length;
      ctx.globalAlpha = a * 0.5;
      ctx.fillStyle = '#ff5c92';
      heartPath(ctx, p.x, p.y, 3 + a * 3);
      ctx.fill();
    }
    ctx.globalAlpha = 1;
    // the ball is a little heart
    const s = 9;
    const g = ctx.createRadialGradient(ball.x - 3, ball.y - 4, 1, ball.x, ball.y, 14);
    g.addColorStop(0, '#ffffff');
    g.addColorStop(0.3, '#ff6d9f');
    g.addColorStop(1, '#d6175a');
    ctx.fillStyle = g;
    heartPath(ctx, ball.x, ball.y - 4, s * 0.85);
    ctx.fill();
  }

  function drawHearts() {
    for (const h of hearts) {
      ctx.save();
      ctx.globalAlpha = Math.max(0, Math.min(1, h.life));
      ctx.translate(h.x, h.y);
      ctx.rotate(h.rot);
      ctx.fillStyle = h.color;
      heartPath(ctx, 0, 0, h.s);
      ctx.fill();
      ctx.restore();
    }
    ctx.globalAlpha = 1;
  }

  function drawOverlayHint() {
    if (mode === 'demo' && !started) {
      ctx.fillStyle = 'rgba(176,18,79,0.55)';
      ctx.font = 'bold 14px "Trebuchet MS", sans-serif';
      ctx.textAlign = 'center';
      ctx.fillText('DEMO MODE', W / 2, H - 12);
    }
  }

  function roundRect(x, y, w, h, r) {
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.arcTo(x + w, y, x + w, y + h, r);
    ctx.arcTo(x + w, y + h, x, y + h, r);
    ctx.arcTo(x, y + h, x, y, r);
    ctx.arcTo(x, y, x + w, y, r);
    ctx.closePath();
  }

  function shade(hex, amt) {
    const n = parseInt(hex.slice(1), 16);
    let r = (n >> 16) + amt, g = ((n >> 8) & 255) + amt, b = (n & 255) + amt;
    r = Math.max(0, Math.min(255, r));
    g = Math.max(0, Math.min(255, g));
    b = Math.max(0, Math.min(255, b));
    return `rgb(${r},${g},${b})`;
  }

  function render() {
    ctx.save();
    if (shake > 0) {
      ctx.translate((Math.random() - 0.5) * shake, (Math.random() - 0.5) * shake);
      shake *= 0.85;
      if (shake < 0.3) shake = 0;
    }
    drawBackground();
    drawBricks();
    drawHearts();
    drawPaddle();
    drawBall();
    drawOverlayHint();
    if (flash > 0) {
      ctx.fillStyle = `rgba(255,255,255,${flash * 0.3})`;
      ctx.fillRect(0, 0, W, H);
      flash *= 0.85;
    }
    ctx.restore();
  }

  function frame() {
    updatePaddle();
    updateBall();
    updateHearts();
    render();
    requestAnimationFrame(frame);
  }

  // Init
  buildLevel();
  resetBall();
  updateHud();
  // Demo mode starts immediately; ball auto-launches.
  setTimeout(() => { if (ball.attached) launchBall(); }, 600);
  frame();
})();
</script>
</body>
</html>
Reasoning trace (2,411 tokens)
I'm planning a single-file canvas breakout game with a hamburger paddle, bricks that burst into hearts, and a self-playing demo behind a start prompt. I'm sketching the paddle now: a brown top bun, a wavy green lettuce layer, a brown patty, and a bottom bun, roughly 110 by 40 pixels.

Next I'm spelling out the breakout physics: bricks burst into 20 to 30 hearts that fall, fade, and rotate, each drawn as a path. The demo keeps the AI paddle tracking the ball's x-position behind the start message, and the first real click or key press hands control to the mouse and arrow keys with lives and level reset.

I'm mapping out how the overlay and input will work: clicking or pressing a key starts real play, mouse and arrow keys steer the paddle, and the ball auto-launches shortly after it's reset.

Still writing the collision and drawing code...

Still working through the collision logic: the paddle bounce angle, then a simple overlap test that flips whichever axis penetrates least. I'm also planning the demo AI to chase the ball's x-position with a bit of noise and a capped speed.

Still writing the overlay and input handling: the wrapper takes the click to start play, and a window-level key listener handles the keyboard. I'm setting the demo to begin immediately with an auto-launched ball, and when the ball is lost during the demo I'll reset it without touching the lives count. The canvas scales to fit its container while keeping the 800x600 logical size.

Still writing the heart drawing function...

Still writing the heart drawing function...

Still writing the heart drawing function...

Still writing the game code.