GPT-5 Nano
by OpenAIReasoning1 console error
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 40.0 s
- Cost
- $0.0023
- Output tokens
- 5,711
- Spent thinking
- 384 tokens
- Speed
- 156.1 tok/s
- Console errors
- 1
- First token after
- 3.4 s
- First visible text after
- 6.2 s
- Prompt tokens
- 508
- Code size
- 17.2 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-5-nano- Reasoning sent
{"effort":"low"}- Max output
- 128,000 tokens
- Generated
- Nov 12, 2026
Console errors
Caught in the first 3 seconds after load, in a sandboxed iframe like the one above.
brickHeight is not a function
What it was asked
The prompt
Build a polished Breakout (brick breaker) game in a single HTML file using vanilla JavaScript and a <canvas>. Requirements: - A paddle controlled with the mouse and with the left/right arrow keys. - The bounce angle off the paddle depends on where the ball hits it (center = straight up, edges = sharp angle). - One level with at least 6 rows of bricks, mixing normal bricks and tougher bricks that need 2 hits and visibly crack after the first one. - Some bricks visibly carry a power-up icon; when broken they drop it, and catching it with the paddle gives either a wider paddle (10 seconds) or multi-ball (3 balls). - The ball must never pass through bricks or the paddle. - Particles when a brick breaks. - 3 lives, score displayed. Game over and victory screens with a restart option.
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 · 637 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Breakout AI Demo</title>
<style>
:root{
--bg: #0b0f1a;
--panel: rgba(255,255,255,.1);
--brick1: #ff6b6b;
--brick2: #ffd166;
--brick3: #8bd8bd;
--text: #e8f0ff;
--glow: 0 0 12px rgba(0,200,255,.6);
}
*{box-sizing:border-box}
html,body{height:100%;margin:0;background:radial-gradient(circle at 50% -20%, #213, #0b0f1a 40%), #0b0f1a;
color:var(--text);font-family:Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
overflow:hidden;}
canvas{display:block; width:100%; height:auto; max-width:1200px; margin:0 auto; outline:none; }
.ui {
position: fixed; top: 12px; left: 50%; transform: translateX(-50%);
display:flex; gap:12px; align-items:center; padding:10px 14px;
background: rgba(0,0,0,.35); border-radius:12px; backdrop-filter: blur(4px);
box-shadow: var(--glow);
user-select:none; z-index: 5;
}
.badge { padding:4px 8px; border-radius:8px; background: rgba(255,255,255,.1); font-weight:600; font-size:12px; }
.overlay {
position: fixed; inset:0; display:flex; align-items:center; justify-content:center;
background: rgba(0,0,0,.5); z-index: 4;
}
.panel {
background: rgba(8,12,22,.95); padding:22px 28px; border-radius:12px;
text-align:center; max-width:520px; box-shadow: 0 8px 28px rgba(0,0,0,.5);
}
.panel h1{ margin:0 0 8px; font-size:28px; letter-spacing:.5px; }
.panel p{ margin:6px 0 14px; color:#cfe0ff; }
.btn {
padding:10px 14px; border-radius:8px; border:0; cursor:pointer;
background: linear-gradient(#4aa3ff, #1a73e8); color:white; font-weight:700;
box-shadow: inset 0 -2px 0 rgba(0,0,0,.25);
}
.btn.secondary{ background: rgba(255,255,255,.15); color:#e8f0ff; }
@media (max-width: 700px){
.ui{flex-direction:column; gap:6px}
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div class="ui" aria-hidden="true" id="uiHint">
<span class="badge">Controls: Move with mouse or left/right arrows</span>
<span class="badge">Click or press any key to start (AI will play)</span>
<span class="badge" id="scoreDisplay">Score: 0</span>
<span class="badge" id="livesDisplay">Lives: 3</span>
</div>
<div class="overlay" id="startOverlay" style="display:flex">
<div class="panel">
<h1>Breakout: AI Demo</h1>
<p>Watch the AI play the game. You can still move the paddle with the mouse or arrow keys, but the demo starts in demo mode.</p>
<p>Click or press any key to start the game.</p>
<button class="btn" id="startBtn">Start Demo</button>
</div>
</div>
<script>
(() => {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const DPR = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));
let W = 1280, H = 800;
function resize() {
const rect = canvas.getBoundingClientRect();
// adapt canvas to CSS size but keep internal resolution for crispness
canvas.width = Math.floor(rect.width * DPR);
canvas.height = Math.floor(rect.height * DPR);
ctx.setTransform(DPR,0,0,DPR,0,0);
W = rect.width;
H = rect.height;
}
function fitCanvas() {
const r = Math.min(window.innerWidth*0.95, 1200);
canvas.style.width = r + 'px';
canvas.style.height = (r * (H/W || 0.6)) + 'px';
}
window.addEventListener('resize', () => { fitCanvas(); resize(); }, {passive:true});
// initial sizing
canvas.style.width = '100%';
resize();
fitCanvas();
// Game objects
const state = {
score:0,
lives:3,
running:false,
won:false,
lost:false,
started:false
};
// Entities
const paddle = {
width: 110,
height: 14,
x: 0, // center
y: 0,
speed: 7,
dx: 0,
color: '#6be1ff',
extendedUntil: 0
};
const BALL_RADIUS = 6;
const Ball = function(x,y,vx,vy){
this.x=x; this.y=y; this.vx=vx; this.vy=vy; this.radius=BALL_RADIUS;
this.alive=true;
};
const balls = [];
const maxBalls = 3;
// Bricks
const bricks = [];
const BRICK_ROWS = 6;
const BRICK_COLS = 10;
const brickWidth = () => Math.max(48, Math.floor((W-40)/BRICK_COLS) - 2);
const brickHeight = 20;
const Brick = function(r,c, type){
this.r=r; this.c=c; this.type=type; // 1 normal, 2 tough
this.hit=0;
this.color = type===1? '#56ccff' : '#f4a261';
this.alive = true;
};
// PowerUps
const Power = function(x,y,type){
this.x=x; this.y=y; this.type=type; // 'widen' or 'multiball'
this.vy = 1.5;
this.col = type==='widen' ? '#4bd6a8' : '#ffd166';
this.size = 12;
this.active = true;
};
const powers = [];
// Particles
const Part = function(x,y, vx,vy, color){
this.x=x; this.y=y; this.vx=vx; this.vy=vy;
this.life=60; this.color=color;
};
const particles = [];
// AI/demo mode
let aiMode = true; // when true, paddle controlled by AI
let launchPrepared = false;
// Init world
function resetLevel(){
bricks.length = 0;
const rows = BRICK_ROWS;
const cols = BRICK_COLS;
const offsetX = 20;
const offsetY = 60;
const w = brickWidth();
for(let r=0;r<rows;r++){
for(let c=0;c<cols;c++){
// mix durability: two tougher rows
const type = (r%3===0 && Math.random()<0.5) ? 2 : 1;
bricks.push(new Brick(r,c,type));
// position
}
}
}
function layBricks(){
const cols = BRICK_COLS;
const rows = BRICK_ROWS;
const w = brickWidth();
const h = brickHeight;
const startX = 20;
const startY = 60;
for(let r=0;r<rows;r++){
for(let c=0;c<cols;c++){
// compute brick position
const x = startX + c*(w+2);
const y = startY + r*(h+4);
// create brick
}
}
}
// Brick array initialization with positions
function initBricks(){
bricks.length = 0;
const cols = BRICK_COLS;
const rows = BRICK_ROWS;
const w = brickWidth();
const h = brickHeight;
const startX = 20;
const startY = 60;
for(let r=0;r<rows;r++){
for(let c=0;c<cols;c++){
const type = (r<2)?1: (r===2||r===3?2: (Math.random()<0.2?2:1));
const b = new Brick(r,c,type);
// position
b.x = startX + c*(w+2);
b.y = startY + r*(h+4);
bricks.push(b);
}
}
}
// helper collisions
function circleRectCollision(cx,cy,cr, rx,ry, rw, rh){
// closest point
const closestX = Math.max(rx, Math.min(cx, rx+rw));
const closestY = Math.max(ry, Math.min(cy, ry+rh));
const dx = cx - closestX;
const dy = cy - closestY;
return (dx*dx + dy*dy) <= cr*cr;
}
function spawnParticle(x,y,color){
for(let i=0;i<8;i++){
const a = (Math.PI*2) * Math.random();
const s = Math.random()*1.5 + 0.4;
particles.push(new Part(x,y, Math.cos(a)*s, Math.sin(a)*s, color));
}
}
// Init
function init(){
paddle.x = W/2 - paddle.width/2;
paddle.y = H - 40;
paddle.dx = 0;
balls.length = 0;
// 1 ball on start
const b = new Ball(W/2, paddle.y - 12, 3, -3);
balls.push(b);
initBricks();
state.score = 0;
state.lives = 3;
state.running = false;
state.won = false;
state.lost = false;
state.started = false;
aiMode = true;
launchPrepared = false;
powers.length = 0;
particles.length = 0;
}
// draw helpers
function drawPaddle(){
ctx.fillStyle = paddle.color;
ctx.beginPath();
ctx.roundRect(paddle.x, paddle.y, paddle.width, paddle.height, 6);
ctx.fill();
// glow
ctx.fillStyle = 'rgba(107,225,255,.25)';
ctx.fillRect(paddle.x, paddle.y+paddle.height, paddle.width, 4);
}
function drawBall(ball){
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);
ctx.fill();
}
function drawBricks(){
for(const b of bricks){
if(!b.alive) continue;
ctx.fillStyle = b.color;
ctx.fillRect(b.x, b.y, brickWidth(), brickHeight);
// crack for durability >1
if(b.type>1){
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(b.x+2, b.y+2);
ctx.lineTo(b.x+brickWidth()-2, b.y+brickHeight()-2);
ctx.stroke();
}
if(b.hit>0){
// subtle crack overlay
ctx.fillStyle = 'rgba(0,0,0,.15)';
ctx.fillRect(b.x, b.y, brickWidth(), brickHeight());
}
// power-up indicator
if(b.type>0 && b.hit===1){
// small icon top-right
ctx.fillStyle = '#fff';
ctx.fillRect(b.x+brickWidth()-8, b.y+2, 6,6);
}
}
}
function drawPowers(){
for(const p of powers){
if(!p.active) continue;
ctx.fillStyle = p.col;
ctx.fillRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size);
// icon
ctx.fillStyle = '#000';
ctx.fillRect(p.x - 2, p.y - 2, 4, 4);
}
}
function drawParticles(){
for(const pt of particles){
ctx.fillStyle = pt.color;
ctx.fillRect(pt.x, pt.y, 2,2);
}
}
function updatePaddle(input){
// keyboard
let targetDx = 0;
if(input.left) targetDx = -paddle.speed;
if(input.right) targetDx = paddle.speed;
// smooth
paddle.dx += (targetDx - paddle.dx) * 0.2;
paddle.x += paddle.dx;
// clamp
const maxX = W - paddle.width;
if(paddle.x < 0) paddle.x = 0;
if(paddle.x > maxX) paddle.x = maxX;
// mouse (handled globally: update paddle.x directly)
}
// Input
const input = { left:false, right:false, mouseX:0, mouseY:0 };
window.addEventListener('mousemove', (e)=>{
const rect = canvas.getBoundingClientRect();
const mx = (e.clientX - rect.left);
input.mouseX = mx;
});
window.addEventListener('keydown', (e)=> {
if(!state.started){
// start via key
startGame();
}
}, {passive:true});
window.addEventListener('mousedown', ()=>{
if(!state.started){
startGame();
}
});
// Power handling
function applyPower(type){
if(type==='widen'){
paddle.width = Math.min(W*0.4, paddle.width + 60);
paddle.extendedUntil = performance.now() + 10000;
} else if(type==='multiball'){
// spawn 2 extra balls
for(let i=0;i<2;i++){
const base = balls[0];
const ang = (i===0? -0.5 : 0.5);
const b = new Ball(base.x, base.y, Math.cos(ang)*3, -3);
balls.push(b);
}
}
}
// Start game
function startGame(){
if(state.started) return;
state.started = true;
state.running = true;
launchPrepared = true;
// set initial ball velocity randomized
for(let i=0;i<balls.length;i++){
balls[i].vx = (Math.random()*2-1)*3;
balls[i].vy = -3;
}
// hide overlay
document.getElementById('startOverlay').style.display='none';
}
// Collision handling
function handleCollisions(){
// Ball vs walls
for(const b of balls){
if(!b.alive) continue;
// left/right walls
if(b.x - b.radius < 0){
b.x = b.radius;
b.vx *= -1;
} else if (b.x + b.radius > W){
b.x = W - b.radius;
b.vx *= -1;
}
// top
if(b.y - b.radius < 0){
b.y = b.radius;
b.vy *= -1;
}
// bottom (lose life)
if(b.y - b.radius > H){
b.alive = false;
}
}
// Ball vs paddle
const p = paddle;
for(const b of balls){
if(!b.alive) continue;
if(circleRectCollision(b.x,b.y,b.radius, p.x, p.y, p.width, p.height)){
// compute hit position
const hitPos = (b.x - (p.x + p.width/2)) / (p.width/2); // -1..1
const angle = hitPos * Math.PI/3; // max +/-60 deg
const speed = Math.hypot(b.vx, b.vy);
b.vx = speed * Math.sin(angle);
b.vy = -Math.abs(speed * Math.cos(angle));
// nudge ball out of paddle
b.y = p.y - b.radius - 1;
}
}
// Ball vs bricks
for(const br of bricks){
if(!br.alive) continue;
const w = brickWidth();
const h = brickHeight;
const rx = br.x, ry = br.y;
const hit = circleRectCollision(0,0,0,0,0,0,0); // placeholder to satisfy lints
// Use AABB collision for brick hit
for(const b of balls){
if(!b.alive) continue;
const cx = b.x, cy = b.y;
const insideX = cx > rx && cx < rx + w;
const insideY = cy > ry && cy < ry + h;
const nearby = insideX && insideY;
// but better: simple circle-rect overlap check
if(circleRectCollision(b.x,b.y,b.radius, rx, ry, w, h)){
// reflect ball
// determine side by comparing previous position
// approximate: invert y velocity
b.vy *= -1;
// damage brick
br.hit += 1;
if(br.type>1 && br.hit>=2){
br.alive = false;
// small chance to drop power
if(Math.random()<0.5){
const t = Math.random()<0.5 ? 'widen' : 'multiball';
powers.push(new Power(br.x + w/2, br.y + h/2, t));
}
} else if(br.hit>=1 && br.type===1){
br.alive = false;
// drop power sometimes
if(Math.random()<0.15){
const t = Math.random()<0.5 ? 'widen' : 'multiball';
powers.push(new Power(br.x + w/2, br.y + h/2, t));
}
}
// particles
spawnParticle(b.x, b.y, br.color);
state.score += br.alive ? 5 : 15;
break;
}
}
}
}
// Update
function update(dt){
// mouse control
// keep paddle within screen
paddle.width = paddle.width; // no-op
// AI movement if enabled
if(aiMode){
// simple pursuit of ball center
const targetX = balls[0]?.x ?? (W/2);
const center = paddle.x + paddle.width/2;
const diff = targetX - center;
paddle.dx = diff * 0.05;
paddle.x += paddle.dx;
// clamp
if(paddle.x < 0) paddle.x = 0;
if(paddle.x > W - paddle.width) paddle.x = W - paddle.width;
} else {
// responsive to mouse
if(input.mouseX){
paddle.x = input.mouseX - paddle.width/2;
if(paddle.x < 0) paddle.x = 0;
if(paddle.x > W - paddle.width) paddle.x = W - paddle.width;
}
// keyboard
updatePaddle(input);
}
// update balls
for(const b of balls){
if(!b.alive) continue;
b.x += b.vx;
b.y += b.vy;
}
// powers movement
for(const p of powers){
if(!p.active) continue;
p.y += p.vy;
// catch paddle
if(circleRectCollision(p.x, p.y, 6, paddle.x, paddle.y, paddle.width, paddle.height)){
p.active = false;
applyPower(p.type);
}
// collect off-screen
if(p.y > H){
p.active = false;
}
}
// bricks check
handleCollisions();
// particles life
for(let i=particles.length-1; i>=0; i--){
const pr = particles[i];
pr.life--;
pr.x += pr.vx;
pr.y += pr.vy;
if(pr.life<=0) particles.splice(i,1);
}
// cleanup dead balls
for(let i=balls.length-1;i>=0;i--){
if(!balls[i].alive){
balls.splice(i,1);
}
}
if(balls.length===0){
// lose a life, reset ball
state.lives--;
if(state.lives<=0){
state.lost = true; state.running = false;
document.getElementById('startOverlay').style.display='flex';
} else {
// new ball on paddle
const nb = new Ball(W/2, paddle.y - 12, 3, -3);
balls.push(nb);
}
}
// win condition
const anyAlive = bricks.some(br => br.alive);
if(!anyAlive){
state.won = true; state.running=false;
document.getElementById('startOverlay').style.display='flex';
document.getElementById('startOverlay').innerHTML = `
<div class="panel">
<h1>You Win!</h1>
<p>Score: ${state.score}</p>
<button class="btn" id="startBtn2">Play Again</button>
</div>
`;
document.getElementById('startBtn2').addEventListener('click', ()=> location.reload());
}
// update UI
document.getElementById('scoreDisplay').textContent = 'Score: ' + state.score;
document.getElementById('livesDisplay').textContent = 'Lives: ' + state.lives;
// end overlay reset on interaction
}
function render(){
// clear
ctx.clearRect(0,0,W,H);
// background gradient
const g = ctx.createLinearGradient(0,0,0,H);
g.addColorStop(0,'#0a1a2b');
g.addColorStop(1,'#091422');
ctx.fillStyle = g;
ctx.fillRect(0,0,W,H);
// bricks
drawBricks();
// powerups
drawPowers();
// balls
for(const b of balls) drawBall(b);
// paddle
drawPaddle();
// particles
drawParticles();
// overlay early
}
// main loop
let last = performance.now();
function loop(t){
const dt = (t - last) / 16.666;
last = t;
if(state.running){
update(dt);
}
render();
requestAnimationFrame(loop);
}
// Start
function initAll(){
init();
// create bricks with positions
bricks.length = 0;
initBricks();
// initial paddle width
paddle.width = 110;
paddle.y = H - 40;
paddle.x = W/2 - paddle.width/2;
// initial ball
balls.length = 0;
balls.push(new Ball(W/2, paddle.y - 12, 3, -3));
// start overlay
document.getElementById('startOverlay').style.display='flex';
requestAnimationFrame(loop);
}
// kick off
initAll();
})();
</script>
</body>
</html>Reasoning trace (384 tokens)
**Planning game mechanics** I’m planning to implement both mouse movement and arrow keys to adjust paddle position. I also want to handle collisions, where the ball bounces off bricks that lose durability, featuring a crack effect. Power-ups will appear as icons for a wider paddle or multiple balls. When these are collected, they grant effects like a wider paddle for ten seconds or three additional balls. Additionally, I'll ensure there's a requestAnimationFrame loop for smooth gameplay, starting with the ball stationary on the paddle. Let's make sure it's all coded within a single HTML file while being responsive!**Designing user interaction** I’m planning to implement a "start" function that activates when the user clicks or presses a key. After that, the AI will take over paddle movement automatically, even though the mouse and keys will still work. However, during this AI mode, I’ll need to ignore user input. The requirement mentions a demo mode behind a message, which seems fine. With time limited, I’ll aim for a simplified yet functional version. So, let's get coding!