Hamburger breakoutOne-line
GPT-5 Nano
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 54.3 s
- Cost
- $0.0034
- Output tokens
- 8,496
- Spent thinking
- 4,160 tokens
- Speed
- 166.3 tok/s
- Console errors
- 0
- First token after
- 3.2 s
- First visible text after
- 30.2 s
- Prompt tokens
- 357
- Code size
- 14.0 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-5-nano- Reasoning sent
{"effort":"medium"}- 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 · 479 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hamburger Breakout: Hearts Explosion</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root {
--bg: #0a0a0a;
--card: rgba(0,0,0,0.6);
--text: #fff;
--accent: #ff5a7a;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
background: #111;
color: #eee;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial;
overflow: hidden; /* avoid horizontal scrollbar on all devices */
}
.stage {
display: grid;
place-items: center;
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 20px;
}
/* The game canvas scales nicely to different viewports */
#game {
width: min(94vw, 900px);
height: auto;
display: block;
background: #0b0b1a;
border-radius: 12px;
box-shadow: 0 8px 40px rgba(0,0,0,.5);
image-rendering: crisp-edges;
}
/* Overlay message */
.overlay {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
display: grid;
place-items: center;
pointer-events: none;
}
.panel {
pointer-events: auto;
background: linear-gradient(#1a1a1aaa, #000000aa);
border: 1px solid #555;
border-radius: 14px;
padding: 22px 28px;
text-align: center;
max-width: 90vw;
color: #fff;
backdrop-filter: blur(2px);
}
.panel h1 {
margin: 0 0 6px 0;
font-size: 28px;
font-weight: 700;
}
.panel p {
margin: 6px 0 0 0;
font-size: 15px;
opacity: 0.95;
}
.stats {
position: absolute;
left: 14px;
top: 10px;
font-family: system-ui, sans-serif;
font-size: 13px;
color: #fff;
text-shadow: 0 1px 0 rgba(0,0,0,.3);
user-select: none;
}
.badge {
padding: 4px 8px;
border-radius: 999px;
background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.25);
margin-right: 8px;
}
@media (max-width: 700px) {
.panel { padding: 18px; }
.panel h1 { font-size: 22px; }
}
</style>
</head>
<body>
<div class="stage" id="stage" aria-label="Hamburger Breakout stage">
<canvas id="game" width="900" height="600" tabindex="0"></canvas>
<div class="overlay" id="overlay" aria-label="Start overlay">
<div class="panel">
<h1>Hamburger Breakout</h1>
<p>Click or press any key to start</p>
<p style="font-size:12px; opacity:.8; margin-top:6px;">Paddle is a hamburger. Bricks explode into hearts!</p>
</div>
</div>
<div class="stats" id="stats" aria-label="Game stats">
<span class="badge" id="scoreBadge">Score: 0</span>
<span class="badge" id="livesBadge">Lives: 3</span>
<span class="badge" id="levelBadge" style="display:none;">Bricks: 0</span>
</div>
</div>
<script>
// Core constants
const BASE_W = 900;
const BASE_H = 600;
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
// Adjust DPR for crisp rendering
function setupCanvasDPR() {
const dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));
canvas.width = Math.floor(BASE_W * dpr);
canvas.height = Math.floor(BASE_H * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0); // operate in CSS pixels
}
setupCanvasDPR();
window.addEventListener('resize', () => {
// Keep the visual scale pleasant on resize, without redrawing the world scale
// We simply re-apply DPR transform to keep crispness.
setupCanvasDPR();
});
// Game state
let showOverlay = true; // show initial "Click to start" message
const overlayEl = document.getElementById('overlay');
const scoreEl = document.getElementById('scoreBadge');
const livesEl = document.getElementById('livesBadge');
const levelEl = document.getElementById('levelBadge');
// Demo / AI mode runs regardless; overlay only hides on first interaction
let score = 0;
let lives = 3;
let level = 1;
// World objects
const paddle = {
w: 110,
h: 20,
x: (BASE_W - 110) / 2,
y: BASE_H - 40,
color: '#d2691e' // hamburger bun color
};
const ball = {
x: BASE_W / 2,
y: BASE_H - 60,
r: 6.5,
vx: 4.0,
vy: -5.0,
};
// Bricks setup
let bricks = [];
const BRICK_COLS = 10;
const BRICK_ROWS = 6;
const BRICK_GAP = 8;
const BRICK_TOP = 60;
const brickW = (BASE_W - (BRICK_COLS + 1) * BRICK_GAP) / BRICK_COLS;
const brickH = 22;
function initBricks() {
bricks = [];
const colors = ['#e74c3c', '#e67e22', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6'];
for (let row = 0; row < BRICK_ROWS; row++) {
for (let col = 0; col < BRICK_COLS; col++) {
bricks.push({
x: BRICK_GAP + col * (brickW + BRICK_GAP),
y: BRICK_TOP + row * (brickH + BRICK_GAP),
w: brickW,
h: brickH,
alive: true,
color: colors[(row + col) % colors.length]
});
}
}
}
initBricks();
// Particles for hearts
const particles = [];
function spawnHeartBurst(x, y, count = 24) {
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 1.8 + Math.random() * 2.8;
particles.push({
x, y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
life: 1.0,
size: 2 + Math.random() * 2,
color: '#ff5a7a',
rot: Math.random() * Math.PI
});
}
}
// Helpers
function clamp(n, a, b) { return Math.max(a, Math.min(b, n)); }
function rectsOverlap(ax, ay, aw, ah, bx, by, bw, bh) {
return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by;
}
// Collision detection between circle and rectangle
function circleRectCollision(circle, rect) {
// closest point on rect to circle center
const cx = clamp(circle.x, rect.x, rect.x + rect.w);
const cy = clamp(circle.y, rect.y, rect.y + rect.h);
const dx = circle.x - cx;
const dy = circle.y - cy;
return dx * dx + dy * dy <= circle.r * circle.r;
}
// Input (overlay interaction)
function startIfNeeded() {
if (showOverlay) {
showOverlay = false;
overlayEl.style.display = 'none';
// The AI-driven demo starts immediately; no user input required for motion
// The game loop continues automatically.
}
}
// Click/touch on the stage or key press to start
window.addEventListener('mousedown', startIfNeeded);
window.addEventListener('keydown', (e) => {
// any key to start
startIfNeeded();
});
// Main loop
let lastTime = null;
function loop(ts) {
if (lastTime == null) lastTime = ts;
const dt = Math.min(0.033, (ts - lastTime) / 1000); // cap dt
lastTime = ts;
update(dt);
render();
requestAnimationFrame(loop);
}
// Update game state
function update(dt) {
// AI paddle control: simple smooth chase toward ball x
const targetX = ball.x - paddle.w / 2;
const dx = targetX - paddle.x;
const paddleSpeed = 520 * dt; // tuned for smoothness with 900x600 scale
if (dx > 0) paddle.x += Math.min(dx, paddleSpeed);
else paddle.x += Math.max(dx, -paddleSpeed);
// Boundary clamp
paddle.x = clamp(paddle.x, 0, BASE_W - paddle.w);
// Ball movement
ball.x += ball.vx;
ball.y += ball.vy;
// Wall collisions
if (ball.x - ball.r < 0) { ball.x = ball.r; ball.vx = -ball.vx; }
if (ball.x + ball.r > BASE_W) { ball.x = BASE_W - ball.r; ball.vx = -ball.vx; }
if (ball.y - ball.r < 0) { ball.y = ball.r; ball.vy = -ball.vy; }
// Paddle collision
if (circleRectCollision(ball, { x: paddle.x, y: paddle.y, w: paddle.w, h: paddle.h })) {
ball.y = paddle.y - ball.r;
// reflect with angle depending on hit position
const hitPos = (ball.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2); // -1..1
const maxAngle = 1.1; // ~50 degrees
const angle = hitPos * maxAngle;
const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
ball.vx = speed * Math.sin(angle);
ball.vy = -Math.abs(speed * Math.cos(angle));
}
// Brick collisions
for (let i = 0; i < bricks.length; i++) {
const b = bricks[i];
if (!b.alive) continue;
if (circleRectCollision(ball, b)) {
// Determine collision side to bounce accordingly
// Compute overlap
const nearestX = clamp(ball.x, b.x, b.x + b.w);
const nearestY = clamp(ball.y, b.y, b.y + b.h);
const dx = ball.x - nearestX;
const dy = ball.y - nearestY;
if (Math.abs(dx) > Math.abs(dy)) {
ball.vx = -ball.vx;
} else {
ball.vy = -ball.vy;
}
b.alive = false;
score += 100;
spawnHeartBurst(b.x + b.w / 2, b.y + b.h / 2, 22 + Math.floor(Math.random() * 10));
// Update level bricks count display (optional)
break; // only one brick per frame
}
}
// Missed ball
if (ball.y - ball.r > BASE_H) {
lives--;
if (lives <= 0) {
// Game over: reset everything after short pause
lives = 3;
score = 0;
initBricks();
ball.x = BASE_W / 2;
ball.y = BASE_H - 60;
ball.vx = 4;
ball.vy = -5;
paddle.x = (BASE_W - paddle.w) / 2;
} else {
// Reset ball and paddle positions
ball.x = BASE_W / 2;
ball.y = BASE_H - 60;
ball.vx = 4 * (Math.random() > 0.5 ? 1 : -1);
ball.vy = -5;
paddle.x = (BASE_W - paddle.w) / 2;
}
}
// Remove dead bricks is handled by alive flag
// If all bricks destroyed, repopulate for a continued demo
const anyAlive = bricks.some(br => br.alive);
if (!anyAlive) {
// Rebuild bricks for a new wave with increased difficulty slightly
level++;
initBricks();
// optional: add speed boost
ball.vx *= 1.04;
ball.vy = -Math.abs(ball.vy) * 1.02;
}
// Update score/lives display
scoreEl.textContent = 'Score: ' + score;
livesEl.textContent = 'Lives: ' + lives;
levelEl.textContent = 'Level: ' + level;
}
// Render scene
function render() {
// Clear
ctx.clearRect(0, 0, BASE_W, BASE_H);
// Background gradient for depth
const g = ctx.createLinearGradient(0, 0, 0, BASE_H);
g.addColorStop(0, '#0b0b1a');
g.addColorStop(1, '#050512');
ctx.fillStyle = g;
ctx.fillRect(0, 0, BASE_W, BASE_H);
// Bricks
bricks.forEach(br => {
if (!br.alive) return;
// simple rounded rect
const r = 6;
ctx.fillStyle = br.color;
roundRect(ctx, br.x, br.y, br.w, br.h, r, true, false);
// subtle inner glow
ctx.fillStyle = 'rgba(255,255,255,.08)';
roundRect(ctx, br.x+2, br.y+2, br.w-4, br.h-4, 4, true, false);
});
// Paddle (hamburger)
drawHamburgerPaddle(ctx, paddle.x, paddle.y, paddle.w, paddle.h);
// Ball
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
ctx.fill();
// Hearts particles
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.life -= 0.012;
if (p.life <= 0) {
particles.splice(i, 1);
continue;
}
p.x += p.vx;
p.y += p.vy;
// gentle gravity
p.vy += 0.01;
ctx.save();
ctx.globalAlpha = p.life;
ctx.fillStyle = p.color;
drawHeart(ctx, p.x, p.y, p.size);
ctx.restore();
}
}
// Helpers for drawing
function roundRect(ctx, x, y, w, h, r, fill, stroke) {
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();
if (fill) ctx.fill();
if (stroke) ctx.stroke();
}
// Hamburger paddle drawing
function drawHamburgerPaddle(ctx, x, y, w, h) {
// Top bun
const bunRadius = h * 0.5;
ctx.fillStyle = '#f5c36a';
roundRect(ctx, x, y, w, h, h * 0.5, true, false);
// Add bun center shine
ctx.fillStyle = 'rgba(255,255,255,.25)';
ctx.fillRect(x + w * 0.15, y + h * 0.2, w * 0.7, h * 0.6);
// Patty
ctx.fillStyle = '#7a4a2c';
ctx.fillRect(x + w * 0.15, y + h * 0.42, w * 0.7, h * 0.22);
// Bottom bun shade
ctx.fillStyle = '#f3a24b';
ctx.fillRect(x + w * 0.05, y + h * 0.6, w * 0.9, h * 0.6);
// Outer edge to resemble a hamburger silhouette
ctx.strokeStyle = 'rgba(0,0,0,.25)';
ctx.lineWidth = 2;
roundRect(ctx, x, y, w, h, h * 0.5, false, true);
}
// Heart drawing (simple path)
function drawHeart(ctx, x, y, size) {
const s = size;
ctx.save();
ctx.translate(x, y);
ctx.scale(1, 1);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.arc(-s * 0.5, -s * 0.5, s * 0.5, Math.PI, 0, false);
ctx.arc(s * 0.5, -s * 0.5, s * 0.5, Math.PI, 0, false);
ctx.lineTo(0, s);
ctx.closePath();
ctx.fill();
ctx.restore();
}
// initialize
// The initial loop runs regardless of overlay to produce a self-playing demo
function init() {
// Keep overlay hidden only after first interaction; the demo runs now
overlayEl.style.display = 'block';
showOverlay = true;
// Start the animation loop
requestAnimationFrame(loop);
// Kickoff minimal state to look "alive"
scoreEl.textContent = 'Score: 0';
livesEl.textContent = 'Lives: ' + lives;
levelEl.textContent = 'Level: ' + level;
}
init();
</script>
</body>
</html>Reasoning trace (4,160 tokens)
**Implementing game start interactions** I’m thinking it should start with a message overlay that says, "Click or press a key to start". After the user interacts, the AI begins controlling the paddle. The iframe won’t receive keyboard events until it’s clicked. I’ll add a flag `started` set to false initially. Once clicked or any key is pressed, I’ll set `started` to true and start the paddle following the ball's position. I’ll avoid using localStorage, instead use CSS for resizing and focus on drawing shapes directly in the canvas.**Implementing game mechanics for lives** I need to set up a fallback if the ball falls below the bottom, which means losing a life. I’m thinking of a simple system with three lives. When the ball falls out, I’ll reset both the ball and paddle to the center. If lives reach zero, I’ll display a "Game Over" overlay and stop the game. For canvas size, I'll set it to 960x600 using CSS, ensuring it adapts responsively to smaller screens while maintaining fixed internal dimensions for game physics. A resize handler will help adjust the canvas scale properly.**Adjusting canvas for responsiveness** To set the canvas dimensions properly, I'll use CSS to make its width 100% and height auto. To maintain the pixel ratio, I’ll scale the canvas coordinates based on the actual display size. Using devicePixelRatio scaling is a common approach. I plan to implement a resize function that calculates the scale based on the window's dimensions. For the best fit, I'll initially set the canvas dimensions to 960x600, ensuring it adapts for both desktop and mobile views. A full-bleed CSS option is worth considering too, but I’ll prioritize fitting the 1280x800 viewport.**Choosing canvas dimensions and scaling** I've decided on a canvas size of 900x600 because it looks good. However, to accommodate narrower screens down to 375px wide, I need to implement resizing logic. I’ll set base dimensions at 900 for width and 600 for height and compute a scale factor based on the window size. Then, I’ll set the canvas's displayed width and height accordingly. I’ll ensure the internal resolution remains crisp by adjusting for devicePixelRatio. Finally, I'm considering sticking with a height of 520 for brick placements.**Implementing the game mechanics** I'm setting up the game with a width of 900 and height of 600. The paddle will be 120x20, and the ball will have a radius of 6.5. I’ll create a brick grid of 10 columns and 6 rows, where each brick is around 70 wide. When a brick is broken, it will spawn heart particles. The paddle will take on a "hamburger" shape, using rounded rectangles for the buns and a patty in the center. I'll use artificial intelligence to control the paddle automatically after starting the game, ensuring it chases the ball smoothly. There will also be a game-over overlay with a restart option. The canvas will be styled and managed accordingly for a clean user experience.**Implementing canvas and game objects** I'm establishing constants for the game with a width of 900 and height of 600. The canvas will be set up with its internal dimensions based on the device pixel ratio to ensure crisp graphics. I’ll use a function to resize the canvas while maintaining the aspect ratio, with CSS handling the visual display. For the game objects, I'll define the paddle, ball, bricks, and heart particles, specifying their properties. The AI will have logic to control the paddle's movement, ensuring it tracks the ball accurately. I’ll also implement collision detection for the ball with walls, the paddle, and bricks, ensuring the game's mechanics work smoothly.**Implementing game mechanics and UI** I'm setting up the collision detection for bricks using a simple mathematical formula based on circle-rectangle intersection. If a collision occurs, I’ll reflect the ball's velocity and mark the brick as destroyed while spawning heart particles to celebrate! Additionally, I'll create a "You Win!" overlay when all bricks are gone. It's important to ensure that the page adapts properly to various screen sizes. I’ll work on the starting overlay and event listeners for starting the game. The AI will control the paddle throughout, allowing for a seamless demo experience while the overlay is displayed. I'll ensure to avoid console errors and keep the code clear of external resources. Now, let’s write the code!