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
- 33.1 s
- Cost
- $0.0020
- Output tokens
- 4,903
- Spent thinking
- 0 tokens
- Speed
- 152.5 tok/s
- Console errors
- 1
- First token after
- 973 ms
- First visible text after
- 973 ms
- Prompt tokens
- 508
- Code size
- 16.3 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
Console errors
Caught in the first 3 seconds after load, in a sandboxed iframe like the one above.
Cannot set properties of undefined (setting 'w')
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 · 564 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Breakout Mini - Self-Play Demo</title>
<style>
:root{
--bg: #0b1020;
--panel: rgba(20,20,40,.9);
--brick: #2d9cdb;
--brick2: #e67e22;
--paddle: #66ff92;
--ball: #ffffff;
--charge: #ffd700;
--shadow: rgba(0,0,0,.4);
}
html,body{height:100%;margin:0}
body{
background: radial-gradient(circle at 30% -10%, #1a1f3a 0%, #0b1020 40%, #0b1020 100%), #0b1020;
color:#e8e8e8;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial;
overflow:hidden;
}
.wrap{
width:100%;
height:100%;
display:flex;
align-items:flex-start;
justify-content:center;
padding:20px;
box-sizing:border-box;
}
.card{
width: min(1100px, 92vw);
aspect-ratio: 16 / 9;
background: linear-gradient(#11152b, #0b1020);
border-radius: 14px;
box-shadow: 0 20px 40px rgba(0,0,0,.5);
position:relative;
overflow:hidden;
display:flex;
flex-direction:column;
}
.hud{
display:flex; gap:14px; align-items:center;
padding:10px 14px; font-family:monospace;
font-size:14px; color:#d5e0ff;
background: linear-gradient(to bottom, rgba(0,0,0,.25), rgba(0,0,0,.45));
position:relative; z-index:2;
}
.hud .badge{ padding:6px 10px; border-radius:8px; background:#1e2540; border:1px solid #3a4b8a; }
.hud .grow{ margin-left:auto; }
.stage{ flex:1; position:relative; }
canvas{ width:100%; height:100%; display:block; background: linear-gradient(#0a0f1a, #0a0f1a); }
.overlay{
position:absolute; inset:0;
display:flex; align-items:center; justify-content:center;
background: rgba(8,12,25,.55);
color:#eaf0ff; font-family: system-ui, sans-serif;
font-size:20px; text-align:center;
letter-spacing:.5px;
}
.overlay .btn{
margin-top:14px;
padding:10px 16px; border-radius:8px;
border:1px solid #5a7bd8; background:#2a4bd3; color:white;
cursor:pointer;
}
.overlay .btn:focus{ outline:2px solid #89a4ff; outline-offset:2px; }
@media (max-width: 700px){
.card{ width:100%; height:auto; aspect-ratio: auto; }
.hud{ font-size:12px; }
}
/* simple flourish for bricks */
.crack{ opacity:.0; transition:opacity .2s; }
.crack.active{ opacity:1; }
</style>
</head>
<body>
<div class="wrap">
<div class="card" id="gameCard" aria-label="Breakout game demo">
<div class="hud" aria-hidden="true">
<span class="badge" id="scoreLabel">Score: 0</span>
<span class="badge" id="livesLabel">Lives: 3</span>
<span class="badge" id="levelLabel" style="margin-left:8px;">Level: 1</span>
<span class="grow"></span>
<span class="badge" id="powerLabel" title="Active power-ups">Power: None</span>
</div>
<div class="stage" id="stage" aria-label="Game stage">
<canvas id="gameCanvas"></canvas>
<div class="overlay" id="startOverlay" style="display:flex;flex-direction:column;gap:12px;">
<div>Click or press any key to start</div>
<button class="btn" id="startBtn" aria-label="Start game">Start</button>
</div>
<div class="overlay" id="overOverlay" style="display:none; flex-direction:column;">
<div id="overText" style="font-size:28px; font-weight:700; text-shadow:0 2px 12px rgba(0,0,0,.5);"></div>
<button class="btn" id="restartBtn" aria-label="Restart game">Restart</button>
</div>
</div>
</div>
</div>
<script>
(function(){
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startOverlay = document.getElementById('startOverlay');
const overOverlay = document.getElementById('overOverlay');
const overText = document.getElementById('overText');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const scoreLabel = document.getElementById('scoreLabel');
const livesLabel = document.getElementById('livesLabel');
const levelLabel = document.getElementById('levelLabel');
const powerLabel = document.getElementById('powerLabel');
const stage = document.getElementById('stage');
// sizing to fit responsiveness
function fitCanvas(){
const rect = stage.getBoundingClientRect();
const dpr = Math.max(1, Math.min(2, window.devicePixelRatio || 1));
canvas.width = Math.floor(rect.width * dpr);
canvas.height = Math.floor(rect.height * dpr);
ctx.setTransform(dpr,0,0,dpr,0,0);
}
window.addEventListener('resize', fitCanvas);
fitCanvas();
// Game objects
const STATE = { READY:0, PLAY:1, PAUSED:2, GAMEOVER:3, WON:4 };
let gameState = STATE.READY;
const config = {
paddleW: 120,
paddleH: 14,
paddleSpeed: 7,
ballR: 6,
gravity: 0, // not used
brickRows: 6,
brickCols: 10,
brickW: 0, // computed
brickH: 22,
brickPadding: 6,
brickOffsetTop: 60,
brickOffsetLeft: 20,
};
// Entities
let paddle, balls, bricks, particles, powerUps;
let score = 0;
let lives = 3;
let level = 1;
let activePower = null;
let powerTimer = 0;
// Input
let mouseX = window.innerWidth / 2;
let keys = {};
// Helpers
function rand(min, max){ return Math.random()*(max-min)+min; }
function clamp(v, a, b){ return Math.max(a, Math.min(b, v)); }
// Brick definitions
// brick: {x,y,w,h, hp, color, cracked:false, isPower:false, crackedOnce:false}
function createBricks(){
const bricksArr = [];
const rows = config.brickRows;
const cols = config.brickCols;
const w = (canvas.clientWidth - config.brickOffsetLeft*2 - (cols-1)*config.brickPadding) / cols;
config.brickW = w;
for(let r=0;r<rows;r++){
for(let c=0;c<cols;c++){
const wBr = w;
const x = config.brickOffsetLeft + c * (wBr + config.brickPadding);
const y = config.brickOffsetTop + r * (config.brickH + 10);
// mix bricks: some tougher that needs 2 hits
const isTough = (r+ c) % 4 === 0; // variety
const brick = {
x, y, w: wBr, h: config.brickH,
hp: isTough ? 2 : 1,
color: isTough ? '#e74c3c' : '#2ecc71',
cracked: false,
isPower: Math.random() < 0.18, // some bricks carry power
destroyed: false
};
bricksArr.push(brick);
}
}
return bricksArr;
}
function resetGame(){
level = 1;
score = 0;
lives = 3;
powerClear();
bricks = createBricks();
balls = [ { x: canvas.clientWidth/2, y: canvas.clientHeight - 60, vx: 4, vy: -4 } ];
}
function powerClear(){
activePower = null;
powerTimer = 0;
paddle.w = config.paddleW;
powerLabel.textContent = 'Power: None';
}
// Particles
function spawnParticles(x, y, color){
for(let i=0;i<12;i++){
particles.push({
x, y,
vx: rand(-2,2), vy: rand(-2,2),
life: rand(20, 40),
color
});
}
}
function spawnPower(x, y){
// simple icon as an object with type
const types = ['wide','multiball'];
const type = types[Math.floor(Math.random()*types.length)];
powerUps.push({ x, y, vy: 1.6, type, collected:false });
}
// Collision helpers
function circleRectCollide(cx, cy, r, rx, ry, rw, rh){
// clamp circle center to rect, then see distance
const closestX = clamp(cx, rx, rx+rw);
const closestY = clamp(cy, ry, ry+rh);
const dx = cx - closestX;
const dy = cy - closestY;
return (dx*dx + dy*dy) <= (r*r);
}
function reflectBall(ball, hitX){
// normalize hit position along paddle to angle
const paddleCenter = paddle.x + paddle.w/2;
const diff = (hitX - paddleCenter) / (paddle.w/2); // -1..1
const angle = diff * (Math.PI/3); // max 60 degrees
const speed = Math.hypot(ball.vx, ball.vy);
ball.vx = Math.sin(angle) * speed;
ball.vy = -Math.abs(Math.cos(angle) * speed);
}
// Init game
function init(){
fitCanvas();
resetGame();
// initial overlay
startOverlay.style.display = 'flex';
}
// Movements
function updatePaddle(){
// mouse
paddle.x = mouseX - paddle.w/2;
// constrain
paddle.x = clamp(paddle.x, 0, canvas.clientWidth - paddle.w);
// keyboard
if (keys['ArrowLeft'] || keys['Left']) paddle.x -= config.paddleSpeed;
if (keys['ArrowRight'] || keys['Right']) paddle.x += config.paddleSpeed;
paddle.x = clamp(paddle.x, 0, canvas.clientWidth - paddle.w);
}
// Main loop
let lastTs = 0;
function loop(ts){
if(!lastTs) lastTs = ts;
const dt = Math.min(32, ts - lastTs);
lastTs = ts;
// auto-start
if (gameState === STATE.READY){
// just render
}
// update only when playing
if (gameState === STATE.PLAY){
update(dt);
}
render();
requestAnimationFrame(loop);
}
function update(dt){
// dt in ms to seconds
// dt not used heavily
// move paddle by input handled in updatePaddle
updatePaddle();
// update balls
for(let i=0;i<balls.length;i++){
const b = balls[i];
b.x += b.vx;
b.y += b.vy;
// wall collisions
if (b.x - config.ballR < 0){
b.x = config.ballR;
b.vx *= -1;
} else if (b.x + config.ballR > canvas.clientWidth){
b.x = canvas.clientWidth - config.ballR;
b.vx *= -1;
}
if (b.y - config.ballR < 0){
b.y = config.ballR;
b.vy *= -1;
}
// paddle collision
if (circleRectCollide(b.x, b.y, config.ballR, paddle.x, paddle.y, paddle.w, paddle.h)){
// compute hit position
reflectBall(b, b.x);
// move ball just above paddle to avoid sticking
b.y = paddle.y - config.ballR - 1;
}
// bricks collision
outer:
for(let br of bricks){
if (br.destroyed) continue;
if (circleRectCollide(b.x, b.y, config.ballR, br.x, br.y, br.w, br.h)){
// determine side hit to reflect
// approximate by finding penetration direction
const prevX = b.x - b.vx;
const prevY = b.y - b.vy;
let collidedSide = null;
// check from previous position which side is closer
if (prevX < br.x && b.x >= br.x) collidedSide = 'left';
else if (prevX > br.x + br.w && b.x <= br.x + br.w) collidedSide = 'right';
else if (prevY < br.y && b.y >= br.y) collidedSide = 'top';
else if (prevY > br.y + br.h && b.y <= br.y + br.h) collidedSide = 'bottom';
// fallback
if (!collidedSide){
// reflect based on overlap vector
collidedSide = (Math.abs(b.x - br.x) < Math.abs(b.x - (br.x+br.w))) ? 'left' : 'right';
}
if (collidedSide === 'left' || collidedSide === 'right'){
b.vx *= -1;
} else {
b.vy *= -1;
}
// brick damage
br.hp -= 1;
br.cracked = br.hp === 1;
br.destroyed = br.hp <= 0;
// crack visual cue
if (br.cracked){
br.color = '#f1c40f';
}
// power up drop
if (br.isPower && !br.destroyed){
// ensure drop when destroyed only
}
if (br.destroyed){
score += 50;
// spawn particles
spawnParticles(br.x + br.w/2, br.y + br.h/2, br.color);
// maybe spawn power
if (br.isPower && Math.random() < 0.9){
spawnPower(br.x + br.w/2, br.y + br.h/2);
}
}
break outer;
}
}
// bottom miss
if (b.y - config.ballR > canvas.clientHeight){
balls.splice(i,1);
i--;
if (balls.length === 0){
lives -= 1;
if (lives <= 0){
gameOver(false);
} else {
// reset ball
balls.push({ x: canvas.clientWidth/2, y: canvas.clientHeight - 60, vx: 4, vy: -4 });
}
}
}
}
// power-ups
for(let pu of powerUps){
pu.y += pu.vy;
// catch paddle
if (circleRectCollide(pu.x, pu.y, 10, paddle.x, paddle.y, paddle.w, paddle.h)){
applyPower(pu.type);
pu.collected = true;
}
}
// remove collected or offscreen
powerUps = powerUps.filter(p => !p.collected && p.y < canvas.clientHeight + 20);
// cleanup bricks fully destroyed may reduce level
const aliveBricks = bricks.filter(b => !b.destroyed);
if (aliveBricks.length === 0){
// win
gameOver(true);
}
// update power timer
if (activePower){
powerTimer -= dt/1000;
if (powerTimer <= 0){
powerClear();
}
}
// small: if no balls, nothing
}
function applyPower(type){
if (type === 'wide'){
activePower = 'wide';
powerTimer = 10;
paddle.w = config.paddleW * 1.6;
powerLabel.textContent = 'Power: Wide';
} else if (type === 'multiball'){
powerTimer = 0;
// spawn 2 extra balls if not already many
const current = balls.length;
for(let i=0;i<Math.min(2, 3 - current);i++){
const base = balls[0] || { x: canvas.clientWidth/2, y: canvas.clientHeight - 60, vx: 4, vy: -4 };
balls.push({ x: base.x, y: base.y, vx: (Math.random()<0.5?1:-1)*4, vy: -4 });
}
powerLabel.textContent = 'Power: Multiball';
// multiball lasts some seconds
powerTimer = 10;
}
}
function render(){
// clear
ctx.clearRect(0,0, canvas.clientWidth, canvas.clientHeight);
// bricks
for(let br of bricks){
if (br.destroyed) continue;
ctx.fillStyle = br.color;
ctx.fillRect(br.x, br.y, br.w, br.h);
// crack overlay if cracked
if (br.cracked){
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 1;
ctx.strokeRect(br.x+2, br.y+2, br.w-4, br.h-4);
}
// power icon indicator
if (br.isPower){
ctx.fillStyle = '#ffd700';
ctx.font = '12px sans-serif';
ctx.fillText('P', br.x + br.w/2 - 5, br.y + br.h/2 + 4);
}
}
// particles
for(let p of particles){
ctx.fillStyle = p.color;
ctx.globalAlpha = Math.max(0, p.life/40);
ctx.fillRect(p.x, p.y, 2, 2);
p.x += p.vx;
p.y += p.vy;
p.life -= 1;
}
ctx.globalAlpha = 1;
// balls
for(let b of balls){
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(b.x, b.y, config.ballR, 0, Math.PI*2);
ctx.fill();
}
// paddle
ctx.fillStyle = '#66ff92';
ctx.fillRect(paddle.x, paddle.y, paddle.w, paddle.h);
}
function gameOver(won){
gameState = STATE.GAMEOVER;
overOverlay.style.display = 'flex';
overText.textContent = won ? 'You cleared the bricks! Victory!' : 'Game Over';
}
// init stage data
function setupNewRun(){
bricks = createBricks();
balls = [{ x: canvas.clientWidth/2, y: canvas.clientHeight - 60, vx: 4, vy: -4 }];
paddle = { x: (canvas.clientWidth - config.paddleW)/2, y: canvas.clientHeight - 20, w: config.paddleW, h: 14 };
particles = [];
powerUps = [];
}
// User input
canvas.addEventListener('mousemove', (e)=>{
const rect = canvas.getBoundingClientRect();
mouseX = e.clientX - rect.left;
});
window.addEventListener('keydown', (e)=>{
if (gameState === STATE.READY){
// start on key press
startGame();
} else {
keys[e.key] = true;
// prevent scrolling with arrows
if (['ArrowLeft','ArrowRight'].includes(e.key)) e.preventDefault();
}
}, {passive:false});
window.addEventListener('keyup', (e)=>{ keys[e.key] = false; });
function startGame(){
// init state
setupNewRun();
gameState = STATE.PLAY;
startOverlay.style.display = 'none';
overOverlay.style.display = 'none';
levelLabel.textContent = 'Level: 1';
scoreLabel.textContent = 'Score: 0';
livesLabel.textContent = 'Lives: 3';
powerLabel.textContent = 'Power: None';
}
// Restart
restartBtn.addEventListener('click', ()=>{
// reset same
resetGame();
gameState = STATE.PLAY;
overOverlay.style.display = 'none';
});
startBtn.addEventListener('click', ()=> startGame());
// kick off
init();
// start with ready overlay
// also handle click on stage to start
stage.addEventListener('click', ()=>{
if (gameState === STATE.READY){
startGame();
}
});
// frame loop
requestAnimationFrame(loop);
// initial sizing setups
// ensure paddle exists before first render
paddle = { x: (canvas.clientWidth - config.paddleW)/2, y: canvas.clientHeight - 20, w: config.paddleW, h: 14 };
})();
</script>
</body>
</html>