Models 44

GPT-5 Nano

by OpenAIReasoningClean
Open in new window

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

How it went

Total time
30.3 s
Cost
$0.0018
Output tokens
4,473
Spent thinking
0 tokens
Speed
152.1 tok/s
Console errors
0
First token after
903 ms
First visible text after
903 ms
Prompt tokens
357
Code size
13.5 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-5-nano
Reasoning sent
{"effort":"minimal"}
Max output
128,000 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 · 491 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hamburger Breakout — Hearts Explosion</title>
<style>
  :root{
    --bg:#101018;
    --panel:#1e1a24;
    --hamburger:#d35400;
    --bun:#f2a043;
    --lettuce:#2ecc71;
    --tomato:#e74c3c;
    --cheese:#f1c40f;
    --heart:#ff5a7a;
    --brick:#e74c3c;
    --brick2:#c0392b;
    --shadow: rgba(0,0,0,.35);
  }
  html,body{height:100%;margin:0;padding:0;background: radial-gradient(circle at 20% 0%, #1a1020 0%, #0b0b14 40%, #0b0b14 100%), #0b0b14; font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial; color:#fff; overflow:hidden;}
  .wrap{display:flex;align-items:center;justify-content:center;height:100vh;}\n
  canvas{display:block;background: linear-gradient(#0b0b14, #0a0a12); box-shadow:0 10px 40px var(--shadow); border-radius:14px; width: min(1100px, 92vw); height:auto;}
  /* Overlay message */
  .overlay{
    position: absolute; top:0; left:0; right:0; bottom:0;
    display:flex; align-items:center; justify-content:center;
    pointer-events:none;
  }
  .panel{
    background: rgba(0,0,0,.55);
    border:1px solid rgba(255,255,255,.15);
    padding: 22px 28px;
    border-radius:12px;
    text-align:center;
    max-width: 90vw;
    backdrop-filter: blur(3px);
  }
  h1{margin:0 0 8px;font-size:22px;letter-spacing:.4px}
  p{margin:0 0 8px;opacity:.9}
  .startBtn{
    display:inline-block;margin-top:6px;padding:10px 14px;
    background:#fff0; color:#fff; border:1px solid #fff; border-radius:999px;
    padding-left:18px;padding-right:18px; cursor:pointer;
    transition: all .2s ease;
  }
  .startBtn:hover{background:#fff; color:#111}
  /* HUD */
  .hud{ position:absolute; left:12px; top:12px; display:flex; gap:12px; align-items:center; font-family:monospace; font-size:14px; }
  .hud .chip{ background:rgba(0,0,0,.4); border:1px solid rgba(255,255,255,.15); padding:6px 10px; border-radius:999px; }
  .heartIcon{ color: #ff6b9a; filter: drop-shadow(0 0 2px rgba(255,90,122,.8)); }
  @media (max-width: 720px){
    .panel{font-size:14px; padding:18px}
  }
</style>
</head>
<body>
<div class="wrap">
  <canvas id="gameCanvas" width="1120" height="640" tabindex="0"></canvas>
  <div class="overlay" id="overlay">
    <div class="panel" aria-label="Start screen">
      <h1>Hamburger Breakout</h1>
      <p>Controls: Move with mouse or touch. The hamburger is the paddle. Break bricks to reveal exploding hearts!</p>
      <p>Click or press any key to start</p>
      <button class="startBtn" id="startBtn" aria-label="Start game">Click or press a key to start</button>
    </div>
  </div>
  <div class="hud" aria-label="Score and lives">
    <div class="chip" id="scoreBox">Score: 0</div>
    <div class="chip" id="levelBox">Level: 1</div>
    <div class="chip" id="livesBox">Lives: 3</div>
  </div>
</div>

<script>
(() => {
  const canvas = document.getElementById('gameCanvas');
  const ctx = canvas.getContext('2d');
  const overlay = document.getElementById('overlay');
  const startBtn = document.getElementById('startBtn');
  const scoreBox = document.getElementById('scoreBox');
  const levelBox = document.getElementById('levelBox');
  const livesBox = document.getElementById('livesBox');

  // Game state
  let w = canvas.width, h = canvas.height;
  let running = false; // auto-play mode true after start
  let started = false;
  let keys = {};
  let score = 0;
  let level = 1;
  let lives = 3;

  // Paddle shaped as a hamburger
  const paddle = {
    width: 110,
    height: 28,
    x: (w - 110) / 2,
    y: h - 60,
    speed: 8,
    // components for drawing the hamburger
    draw(p){
      // bun top
      p.fillRect(p.x, p.y, p.width, 10, '#f2a043');
    }
  };

  // We'll implement drawing with helper
  function drawHamburger(x,y,wid,h) {
    // bun top
    ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--bun') || '#f2a043';
    ctx.strokeStyle = 'rgba(0,0,0,.2)';
    // Bun top
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.quadraticCurveTo(x + wid*0.5, y - h*0.5, x + wid, y);
    ctx.lineTo(x + wid, y + 10);
    ctx.quadraticCurveTo(x + wid*0.5, y + h*0.6, x, y + 10);
    ctx.closePath();
    ctx.fill();

    // Filling
    ctx.fillStyle = '#e74c3c';
    ctx.fillRect(x, y + 10, wid, h * 0.55);

    // Lettuce
    ctx.fillStyle = '#2ecc71';
    ctx.fillRect(x, y + 20, wid, h * 0.25);

    // Cheese
    ctx.fillStyle = '#f1c40f';
    ctx.fillRect(x, y + h * 0.5, wid, 6);

    // Bun bottom
    ctx.fillStyle = '#d35400';
    ctx.beginPath();
    ctx.moveTo(x, y + h);
    ctx.quadraticCurveTo(x + wid*0.5, y + h + h*0.25, x + wid, y + h);
    ctx.lineTo(x + wid, y + h + 6);
    ctx.quadraticCurveTo(x + wid*0.5, y + h - h*0.15, x, y + h + 6);
    ctx.closePath();
    ctx.fill();
  }

  // We'll implement bricks
  const bricks = [];
  const brickRows = 6;
  const brickCols = 10;
  const brickW =  (w - 80) / brickCols;
  const brickH = 22;

  const heartPieces = []; // particles after brick break

  function initBricks() {
    bricks.length = 0;
    const offsetX = 40;
    const offsetY = 60;
    for(let r=0;r<brickRows;r++){
      for(let c=0;c<brickCols;c++){
        const hp = {
          x: offsetX + c*brickW,
          y: offsetY + r*(brickH+6),
          w: brickW - 6,
          h: brickH,
          alive: true,
          color: r%2===0 ? '#e74c3c' : '#c0392b'
        };
        bricks.push(hp);
      }
    }
  }

  // Ball
  const ball = {
    x: w/2,
    y: h/2,
    r: 8,
    vx: 4,
    vy: -4,
    speed: 6
  };

  // Hearts particles
  function spawnHearts(x,y,dirX,dirY){
    const n = 12 + Math.floor(Math.random()*10);
    for(let i=0;i<n;i++){
      heartPieces.push({
        x: x,
        y: y,
        vx: dirX*(0.5 + Math.random()*2),
        vy: dirY*(0.5 + Math.random()*2) - Math.random()*2,
        life: 60 + Math.floor(Math.random()*60),
        r: 3 + Math.random()*3,
        color: '#ff5a7a'
      });
    }
  }

  // Helpers
  function rectsOverlap(a,b){
    return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
  }

  // Basic circle-rect collision for ball with bricks
  function circleRectCollision(bx,by,br,rx,ry,rw,rh){
    // Find closest point to circle within the rectangle
    const closestX = clamp(bx, rx, rx+rw);
    const closestY = clamp(by, ry, ry+rh);
    const dx = bx - closestX;
    const dy = by - closestY;
    return (dx*dx + dy*dy) < (br*br);
  }
  function clamp(v, a, b){ return Math.max(a, Math.min(b, v)); }

  // Input
  function resetPaddle(){
    paddle.x = (w - 110) / 2;
  }

  // AI auto play: simple follower of ball with lag
  function aiMove(){
    // keep paddle near ball x
    const targetX = ball.x - 55;
    const dx = targetX - paddleX();
    const step = 6;
    if(Math.abs(dx) > step){
      if(dx>0) paddle.v = Math.min(6, dx);
      else paddle.v = Math.max(-6, dx);
    } else {
      paddle.v = 0;
    }
  }

  function paddleX(){ return clamp(paddle.x,0,w-110); }
  function updatePaddle(){
    // in demo, AI controls movement
    const targetX = ball.x - 55;
    const dx = targetX - paddle.x;
    const maxStep = 6;
    if(Math.abs(dx) > maxStep){
      paddle.x += dx>0 ? maxStep : -maxStep;
    } else {
      paddle.x += dx;
    }
    // bounds
    if(paddle.x < 0) paddle.x = 0;
    if(paddle.x + 110 > w) paddle.x = w - 110;
  }

  function drawPaddle(){
    // draw hamburger using simple shapes layered
    const x = paddle.x;
    const y = paddle.y;
    // bun top
    ctx.fillStyle = '#f2a043';
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.quadraticCurveTo(x + 55, y - 14, x + 110, y);
    ctx.lineTo(x + 110, y + 12);
    ctx.quadraticCurveTo(x + 55, y + 20, x, y + 12);
    ctx.closePath();
    ctx.fill();

    // Lettuce layer
    ctx.fillStyle = '#2ecc71';
    ctx.fillRect(x, y + 12, 110, 6);

    // Patty
    ctx.fillStyle = '#7f4a2b';
    ctx.fillRect(x + 0, y + 18, 110, 6);

    // Bun bottom
    ctx.fillStyle = '#d35400';
    ctx.beginPath();
    ctx.moveTo(x, y + 24);
    ctx.quadraticCurveTo(x + 55, y + 38, x + 110, y + 24);
    ctx.lineTo(x + 110, y + 38);
    ctx.quadraticCurveTo(x + 55, y + 50, x, y + 38);
    ctx.closePath();
    ctx.fill();
  }

  // Init
  function init(){
    w = canvas.width; h = canvas.height;
    resetPaddle();
    initBricks();
    spawnAllHeartsReady();
    ball.x = w/2; ball.y = h/2; ball.vx = 4; ball.vy = -4;
  }

  function spawnAllHeartsReady(){
    // nothing pre-spawn; hearts spawn on brick break
  }

  // Start / restart
  function startGame(){
    overlay.style.display = 'none';
    started = true;
    running = true;
  }

  // Rendering loop
  let lastT = 0;
  function loop(ts){
    if(!lastT) lastT = ts;
    const dt = Math.min(50, ts - lastT);
    lastT = ts;
    update(dt);
    render();
    requestAnimationFrame(loop);
  }

  function update(dt){
    // AI moves paddle
    updatePaddle();

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

    // Wall collisions
    if(ball.x - ball.r < 0){ ball.x = ball.r; ball.vx *= -1; }
    if(ball.x + ball.r > w){ ball.x = w - ball.r; ball.vx *= -1; }
    if(ball.y - ball.r < 0){ ball.y = ball.r; ball.vy *= -1; }
    if(ball.y - ball.r > h){
      // life lost
      lives--;
      if(lives <= 0){
        // restart
        score = 0; level = 1; lives = 3;
        init();
      } else {
        // reset ball
        ball.x = w/2; ball.y = h/2; ball.vx = 4 * (Math.random() < 0.5 ? 1 : -1);
        ball.vy = -4;
      }
    }

    // Paddle collision (simple AABB)
    const paddleRect = {x: paddle.x, y: paddle.y, w: 110, h: 20};
    if(ball.y + ball.r >= paddleRect.y && ball.y - ball.r <= paddleRect.y + paddleRect.h &&
       ball.x >= paddleRect.x && ball.x <= paddleRect.x + paddleRect.w){
      ball.y = paddleRect.y - ball.r;
      // reflect with some angle based on where hit
      const relative = (ball.x - (paddleRect.x + paddleRect.w/2)) / (paddleRect.w/2);
      const maxBounce = 1.1;
      ball.vy = -Math.abs(ball.vy);
      ball.vx = relative * maxBounce * 5;
    }

    // Bricks collisions
    for(let i=0;i<bricks.length;i++){
      const b = bricks[i];
      if(!b.alive) continue;
      if(circleRectCollision(ball.x, ball.y, ball.r, b.x, b.y, b.w, b.h)){
        // Basic response: reflect y
        ball.vy = -ball.vy;
        b.alive = false;

        // spawn hearts
        spawnHearts(b.x + b.w/2, b.y + b.h/2, (Math.random()<0.5?-1:1),  -1 - Math.random()*1.5);
        score += 10;
        // level complete check
        if(bricks.every(br => !br.alive)){
          level++;
          levelBox.textContent = 'Level: ' + level;
          // reset bricks and ball
          initBricks();
          ball.x = w/2; ball.y = h/2;
          ball.vx = 4 * (Math.random()<0.5?1:-1);
          ball.vy = -4;
        }
        break;
      }
    }

    // Hearts particles update
    for(let p of heartPieces){
      p.x += p.vx;
      p.y += p.vy;
      p.vy += 0.15;
      p.life--;
    }
    // remove dead
    for(let i = heartPieces.length -1; i>=0; i--){
      if(heartPieces[i].life <= 0) heartPieces.splice(i,1);
    }

    // Update HUD
    scoreBox.textContent = 'Score: ' + score;
    livesBox.textContent = 'Lives: ' + lives;
  }

  function render(){
    // Clear
    ctx.clearRect(0,0,w,h);

    // Bricks
    for(let b of bricks){
      if(!b.alive) continue;
      ctx.fillStyle = b.color;
      ctx.fillRect(b.x, b.y, b.w, b.h);
      // subtle border
      ctx.strokeStyle = 'rgba(0,0,0,.25)';
      ctx.lineWidth = 1;
      ctx.strokeRect(b.x, b.y, b.w, b.h);
    }

    // Hearts pieces
    for(let p of heartPieces){
      // heart shape drawn as simple rotated square hearts
      ctx.save();
      ctx.translate(p.x, p.y);
      ctx.rotate(Math.atan2(p.vy, p.vx));
      ctx.fillStyle = p.color;
      // heart path simplified
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.bezierCurveTo(-p.r, -p.r, -p.r*2, -p.r, -p.r, 0);
      ctx.bezierCurveTo(-p.r, p.r, 0, p.r*1.5, 0, p.r*0.8);
      ctx.bezierCurveTo(0, p.r*1.5, p.r, p.r, p.r, 0);
      ctx.bezierCurveTo(p.r, -p.r, 0, -p.r, 0, 0);
      ctx.closePath();
      ctx.fill();
      ctx.restore();
      // fallbacks if heart path not visible, draw simple circle
    }

    // Paddle
    drawPaddle();

    // Ball
    ctx.fillStyle = '#fff';
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI*2);
    ctx.fill();

    // Overlay show if not started
    if(!started){
      // Keep overlay visible
    }
  }

  // Input handling
  function onPointerMove(e){
    // Move paddle with pointer for non-AI initial
    if(!started) return;
    const rect = canvas.getBoundingClientRect();
    const px = (e.clientX ?? 0) - rect.left;
    paddle.x = px - 55;
    if(paddle.x < 0) paddle.x = 0;
    if(paddle.x + 110 > w) paddle.x = w - 110;
  }

  function onKey(e){
    if(!started){
      startGame();
    }
  }

  // Start and click to hide overlay
  startBtn.addEventListener('click', startGame);
  canvas.addEventListener('mousemove', onPointerMove);
  window.addEventListener('keydown', onKey);

  // Resize support
  function fit(){
    // maintain aspect
    const ratio = 1120/640;
    const pw = window.innerWidth;
    const ph = window.innerHeight;
    // We rely on canvas intrinsic size; nothing heavy
  }
  window.addEventListener('resize', fit);

  // Initialize and start
  init();
  // self-playing demo: we auto-run; show initial overlay until user interaction
  requestAnimationFrame(loop);

  // Expose minimal event to start on keydown if focused
  // Ensure iframe click will capture keyboard after user click by focusing canvas
  canvas.addEventListener('click', ()=>{ if(!started){ canvas.focus(); } });
  // Focusable for keyboard events
  canvas.focus();

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