GPT-6 Luna
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 53.8 s
- Cost
- $0.0032
- Output tokens
- 6,319
- Spent thinking
- 378 tokens
- Speed
- 155.4 tok/s
- Console errors
- 0
- First token after
- 13.1 s
- First visible text after
- 13.1 s
- Prompt tokens
- 508
- Code size
- 17.6 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6-luna- Reasoning sent
{"effort":"low"}- Max output
- 64,000 tokens
- Generated
- Nov 10, 2026
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 · 405 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Breakout — Neon Arcade</title>
<style>
:root {
color-scheme: dark;
--bg: #090c17;
--panel: #111629;
--muted: #929ab5;
--cyan: #60f5ef;
--pink: #ff5f9e;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
background:
radial-gradient(ellipse at 50% -20%, rgba(66, 74, 151, .27), transparent 55%),
var(--bg);
color: #f4f6ff;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
display: flex;
justify-content: center;
}
.app { width: min(1120px, calc(100% - 40px)); padding: 23px 0 24px; }
header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
margin-bottom: 18px;
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
width: 42px; height: 42px; border-radius: 13px; display: grid; place-items: center;
color: var(--cyan); background: linear-gradient(145deg, #1a2940, #101628);
border: 1px solid rgba(96,245,239,.23); box-shadow: 0 0 24px #60f5ef12;
}
.brand-mark svg { width: 23px; height: 23px; }
h1 { margin: 0; font-size: 18px; letter-spacing: .14em; font-weight: 800; }
.subtitle { color: var(--muted); font-size: 11px; margin-top: 3px; letter-spacing: .08em; }
.live { display:flex; align-items:center; gap:8px; color:#b1bbd2; font-size:11px; letter-spacing:.11em; }
.live-dot { width:7px; height:7px; border-radius:50%; background:#6af5b7; box-shadow:0 0 10px #6af5b7; }
.hud { display:grid; grid-template-columns:repeat(3, 1fr); gap:10px; margin-bottom:12px; }
.stat {
height:64px; padding:11px 16px; border-radius:12px; background:linear-gradient(120deg,#131a2d,#101525);
border:1px solid #ffffff0d; display:flex; align-items:center; justify-content:space-between;
}
.stat-label { font-size:10px; text-transform:uppercase; color:#8993ad; letter-spacing:.13em; }
.stat-value { font-size:21px; font-weight:750; letter-spacing:.03em; font-variant-numeric:tabular-nums; }
#score { color:var(--cyan); }
#lives { color:#ff779c; letter-spacing:.16em; font-size:17px; }
.arena {
position:relative; overflow:hidden; border-radius:16px; border:1px solid #39445f;
background:#0b1020; box-shadow:0 22px 70px #0007, 0 0 40px #6978ff0a;
}
canvas { display:block; width:100%; height:auto; aspect-ratio: 960 / 540; }
.hint {
position:absolute; top:13px; left:50%; transform:translateX(-50%); z-index:2;
white-space:nowrap; font-size:10px; letter-spacing:.09em; color:#c8d3e4;
padding:8px 12px; border-radius:99px; background:#0b1120c9; border:1px solid #ffffff19;
backdrop-filter:blur(8px); transition:opacity .5s;
pointer-events:none;
}
.hint strong { color:var(--cyan); font-weight:700; }
.footer { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:12px 4px 0; }
.controls { display:flex; flex-wrap:wrap; gap:16px; color:#8590aa; font-size:11px; }
.key { display:inline-grid; place-items:center; min-width:23px; height:21px; padding:0 5px; margin-right:5px;
border:1px solid #ffffff25; border-bottom-width:2px; border-radius:5px; color:#d5dbee; font-size:10px; }
.power-legend { display:flex; gap:15px; font-size:11px; color:#a4aec3; }
.power-legend span { display:flex; align-items:center; gap:6px; }
.badge { display:grid; place-items:center; width:18px; height:18px; border-radius:5px; font-size:11px; font-weight:800; }
.wide { color:#6de7ff; background:#6de7ff19; border:1px solid #6de7ff55; }
.multi { color:#ffd36a; background:#ffd36a19; border:1px solid #ffd36a55; }
@media (max-width:600px) {
.app { width:calc(100% - 22px); padding-top:15px; }
header { margin-bottom:12px; }
.hud { gap:6px; margin-bottom:8px; }
.stat { height:56px; padding:8px 9px; }
.stat-value { font-size:17px; }
.stat-label { font-size:9px; }
.hint { font-size:8px; padding:7px 9px; top:8px; }
.footer { align-items:flex-start; flex-direction:column; }
.controls { gap:10px; }
.arena { border-radius:11px; }
}
</style>
</head>
<body>
<div class="app">
<header>
<div class="brand">
<div class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none"><path d="M4 16.5h16M7 12h10M10 7.5h4" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/><circle cx="12" cy="4" r="1.5" fill="currentColor"/></svg>
</div>
<div><h1>BREAKOUT</h1><div class="subtitle">NEON ARCADE · SECTOR 01</div></div>
</div>
<div class="live"><span class="live-dot"></span> ARCADE READY</div>
</header>
<section class="hud" aria-label="Game stats">
<div class="stat"><div class="stat-label">Score</div><div class="stat-value" id="score">00000</div></div>
<div class="stat"><div class="stat-label">Bricks left</div><div class="stat-value" id="remaining">42</div></div>
<div class="stat"><div class="stat-label">Lives</div><div class="stat-value" id="lives">♥ ♥ ♥</div></div>
</section>
<div class="arena" id="arena">
<div class="hint" id="hint"><strong>DEMO IN PLAY</strong> · Click or press a key to start playing</div>
<canvas id="game" width="960" height="540" aria-label="Breakout game canvas"></canvas>
</div>
<div class="footer">
<div class="controls">
<span><b class="key">←</b><b class="key">→</b> Move paddle</span>
<span><b class="key">MOUSE</b> Aim & move</span>
<span><b class="key">R</b> Restart</span>
</div>
<div class="power-legend">
<span><b class="badge wide">↔</b> Wide paddle</span>
<span><b class="badge multi">✦</b> Multi-ball</span>
</div>
</div>
</div>
<script>
(() => {
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const remainingEl = document.getElementById('remaining');
const livesEl = document.getElementById('lives');
const hint = document.getElementById('hint');
const W = 960, H = 540;
const colors = ['#ff648b','#ff9e58','#ffd66b','#67e5b2','#56caff','#a68bff'];
const bricks = [];
const balls = [];
const drops = [];
const particles = [];
let score = 0, lives = 3, remaining = 42;
let paddle = { x: W / 2, y: 496, w: 110, h: 12, target: W / 2, wideUntil: 0 };
let mode = 'playing', manual = false, keys = { left:false, right:false };
let last = performance.now(), elapsed = 0, flash = 0, shake = 0;
let soundless = true;
function reset() {
score = 0; lives = 3; remaining = 42; mode = 'playing'; manual = false;
paddle = { x: W / 2, y: 496, w: 110, h: 12, target: W / 2, wideUntil: 0 };
bricks.length = 0; balls.length = 0; drops.length = 0; particles.length = 0;
const cols = 7, bw = 112, bh = 21, gapX = 9, gapY = 8;
const startX = (W - (cols * bw + (cols - 1) * gapX)) / 2;
for (let r = 0; r < 6; r++) {
for (let c = 0; c < cols; c++) {
const tough = r < 2 && ((r + c) % 2 === 0);
const power = (r === 2 && (c === 1 || c === 5)) || (r === 4 && c === 3);
bricks.push({ x:startX+c*(bw+gapX), y:67+r*(bh+gapY), w:bw, h:bh,
hp:tough?2:1, max:tough?2:1, color:colors[r], power, alive:true, idx:r*cols+c });
}
}
spawnBall(W/2, 444, 3.3, -4.4);
updateHud();
hint.innerHTML = '<strong>DEMO IN PLAY</strong> · Click or press a key to start playing';
hint.style.opacity = '1';
}
function spawnBall(x,y,vx,vy) {
balls.push({x,y,vx,vy,r:7, trail:[]});
}
function updateHud() {
scoreEl.textContent = String(score).padStart(5,'0');
remainingEl.textContent = String(remaining).padStart(2,'0');
livesEl.textContent = '♥ '.repeat(lives).trim() || '—';
}
function activate() {
if (!manual) {
manual = true;
hint.innerHTML = '<strong>PLAYER CONTROL</strong> · Move with ← → or your mouse';
setTimeout(() => { if (manual) hint.style.opacity = '.25'; }, 2400);
}
}
function restart() { reset(); }
function pointerX(e) {
const rect = canvas.getBoundingClientRect();
return (e.clientX - rect.left) * W / rect.width;
}
canvas.addEventListener('pointermove', e => {
activate();
paddle.target = Math.max(paddle.w/2, Math.min(W-paddle.w/2, pointerX(e)));
});
canvas.addEventListener('pointerdown', activate);
window.addEventListener('keydown', e => {
if (['ArrowLeft','ArrowRight',' '].includes(e.key)) e.preventDefault();
activate();
if (e.key === 'ArrowLeft') keys.left = true;
if (e.key === 'ArrowRight') keys.right = true;
if (e.key.toLowerCase() === 'r') restart();
});
window.addEventListener('keyup', e => {
if (e.key === 'ArrowLeft') keys.left = false;
if (e.key === 'ArrowRight') keys.right = false;
});
function burst(x,y,color,count=12) {
for (let i=0;i<count;i++) {
const a = Math.random()*Math.PI*2, speed = 45+Math.random()*180;
particles.push({x,y,vx:Math.cos(a)*speed,vy:Math.sin(a)*speed,life:.35+Math.random()*.42,max:.77,size:2+Math.random()*3,color});
}
}
function hitBrick(b) {
if (!b || !b.alive) return;
b.hp--;
if (b.hp <= 0) {
b.alive = false; remaining--; score += 100; burst(b.x+b.w/2,b.y+b.h/2,b.color,15);
if (b.power) drops.push({x:b.x+b.w/2,y:b.y+b.h/2,kind:Math.random()<.5?'wide':'multi',vy:100});
if (remaining === 0) mode = 'victory';
} else {
score += 35; burst(b.x+b.w/2,b.y+b.h/2,'#fff1b0',5);
}
updateHud();
}
function catchPower(d) {
if (d.kind === 'wide') {
paddle.wideUntil = elapsed + 10;
paddle.w = 168;
burst(d.x,d.y,'#6de7ff',16);
} else {
const source = balls[0];
if (source) {
const count = Math.min(3, balls.length);
for (let i=1;i<count;i++) {
const angle = (i===1?-.42:.42);
const speed = Math.hypot(source.vx,source.vy);
const base = Math.atan2(source.vy,source.vx);
spawnBall(source.x,source.y,Math.cos(base+angle)*speed,Math.sin(base+angle)*speed);
}
}
burst(d.x,d.y,'#ffd36a',16);
}
}
function update(dt) {
elapsed += dt;
if (paddle.wideUntil && elapsed > paddle.wideUntil) { paddle.w = 110; paddle.wideUntil = 0; }
if (manual) {
if (keys.left) paddle.target -= 540*dt;
if (keys.right) paddle.target += 540*dt;
} else {
const alive = balls[0];
if (alive) paddle.target = alive.x + Math.sin(elapsed*1.1)*8;
}
paddle.target = Math.max(paddle.w/2+8, Math.min(W-paddle.w/2-8,paddle.target));
paddle.x += (paddle.target-paddle.x)*Math.min(1,dt*13);
for (let bi=balls.length-1;bi>=0;bi--) {
const ball = balls[bi];
ball.trail.push({x:ball.x,y:ball.y}); if (ball.trail.length>7) ball.trail.shift();
const speed = Math.hypot(ball.vx,ball.vy);
const steps = Math.max(1,Math.ceil(speed*dt/3));
const step = dt/steps;
let lost = false;
for (let s=0;s<steps;s++) {
const ox=ball.x, oy=ball.y;
ball.x += ball.vx*step; ball.y += ball.vy*step;
if (ball.x < ball.r) { ball.x=ball.r; ball.vx=Math.abs(ball.vx); }
if (ball.x > W-ball.r) { ball.x=W-ball.r; ball.vx=-Math.abs(ball.vx); }
if (ball.y < 38+ball.r) { ball.y=38+ball.r; ball.vy=Math.abs(ball.vy); }
if (ball.vy > 0 && oy+ball.r <= paddle.y && ball.y+ball.r >= paddle.y &&
ball.x >= paddle.x-paddle.w/2-ball.r && ball.x <= paddle.x+paddle.w/2+ball.r) {
ball.y = paddle.y-ball.r;
const offset = Math.max(-1,Math.min(1,(ball.x-paddle.x)/(paddle.w/2)));
const angle = offset*1.12;
const mag = Math.max(5.4,Math.hypot(ball.vx,ball.vy));
ball.vx = Math.sin(angle)*mag;
ball.vy = -Math.cos(angle)*mag;
}
let struck = false;
for (const b of bricks) {
if (!b.alive || 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;
const fromX = Math.min(Math.abs(ox-(b.x- ball.r)),Math.abs(ox-(b.x+b.w+ball.r)));
const fromY = Math.min(Math.abs(oy-(b.y- ball.r)),Math.abs(oy-(b.y+b.h+ball.r)));
if (fromX < fromY) ball.vx = ox < b.x+b.w/2 ? -Math.abs(ball.vx) : Math.abs(ball.vx);
else ball.vy = oy < b.y+b.h/2 ? -Math.abs(ball.vy) : Math.abs(ball.vy);
hitBrick(b); struck=true; break;
}
if (struck) {
ball.x = ox + ball.vx*step;
ball.y = oy + ball.vy*step;
}
if (ball.y > H+ball.r) { lost=true; break; }
}
if (lost) balls.splice(bi,1);
}
if (!balls.length && mode === 'playing') {
lives--; updateHud();
if (lives <= 0) mode = 'gameover';
else spawnBall(W/2,444,(Math.random()-.5)*3,-5.3);
}
for (let i=drops.length-1;i>=0;i--) {
const d=drops[i]; d.y += d.vy*dt;
if (d.y+10 >= paddle.y && d.y-10 <= paddle.y+paddle.h &&
d.x >= paddle.x-paddle.w/2 && d.x <= paddle.x+paddle.w/2) {
catchPower(d); drops.splice(i,1);
} else if (d.y > H+15) drops.splice(i,1);
}
for (let i=particles.length-1;i>=0;i--) {
const p=particles[i]; p.life-=dt; p.x+=p.vx*dt; p.y+=p.vy*dt; p.vx*=.985; p.vy=p.vy*.985+30*dt;
if(p.life<=0) particles.splice(i,1);
}
}
function roundRect(x,y,w,h,r) {
ctx.beginPath(); ctx.roundRect(x,y,w,h,r);
}
function draw() {
ctx.clearRect(0,0,W,H);
const bg=ctx.createLinearGradient(0,0,0,H); bg.addColorStop(0,'#10172a'); bg.addColorStop(1,'#090e1c');
ctx.fillStyle=bg; ctx.fillRect(0,0,W,H);
ctx.save();
ctx.strokeStyle='rgba(127,157,213,.055)'; ctx.lineWidth=1;
for(let x=0;x<W;x+=40){ctx.beginPath();ctx.moveTo(x,38);ctx.lineTo(x,H);ctx.stroke();}
for(let y=38;y<H;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();}
ctx.restore();
ctx.fillStyle='#7885a4'; ctx.font='700 10px system-ui'; ctx.textAlign='left'; ctx.letterSpacing='2px';
ctx.fillText('FIELD 01',24,25);
ctx.textAlign='right'; ctx.fillStyle='#55617e'; ctx.fillText('CLEAR THE GRID',W-24,25);
ctx.strokeStyle='#ffffff12'; ctx.beginPath(); ctx.moveTo(0,38); ctx.lineTo(W,38); ctx.stroke();
for(const b of bricks) {
if(!b.alive) continue;
ctx.save();
ctx.shadowColor=b.color+'55'; ctx.shadowBlur=9;
roundRect(b.x,b.y,b.w,b.h,5); ctx.fillStyle=b.color; ctx.fill();
ctx.shadowBlur=0;
const shine=ctx.createLinearGradient(0,b.y,0,b.y+b.h);
shine.addColorStop(0,'#ffffff38'); shine.addColorStop(.3,'#ffffff08'); shine.addColorStop(1,'#00000028');
roundRect(b.x,b.y,b.w,b.h,5); ctx.fillStyle=shine; ctx.fill();
if(b.hp===2) {
ctx.strokeStyle='#392339'; ctx.lineWidth=1.5;
ctx.beginPath(); ctx.moveTo(b.x+b.w*.44,b.y+2); ctx.lineTo(b.x+b.w*.51,b.y+8); ctx.lineTo(b.x+b.w*.46,b.y+12); ctx.lineTo(b.x+b.w*.58,b.y+b.h-2); ctx.stroke();
ctx.beginPath(); ctx.moveTo(b.x+b.w*.51,b.y+8); ctx.lineTo(b.x+b.w*.62,b.y+11); ctx.stroke();
}
if(b.power) {
ctx.beginPath(); ctx.arc(b.x+b.w-13,b.y+10.5,7,0,Math.PI*2);
ctx.fillStyle='#10172a'; ctx.fill();
ctx.fillStyle='#fff'; ctx.textAlign='center'; ctx.textBaseline='middle'; ctx.font='bold 10px system-ui';
ctx.fillText('✦',b.x+b.w-13,b.y+10);
}
ctx.restore();
}
for(const d of drops) {
const col=d.kind==='wide'?'#6de7ff':'#ffd36a';
ctx.save(); ctx.shadowColor=col; ctx.shadowBlur=16;
ctx.beginPath(); ctx.arc(d.x,d.y,11,0,Math.PI*2); ctx.fillStyle='#11192b'; ctx.fill();
ctx.strokeStyle=col; ctx.lineWidth=2; ctx.stroke();
ctx.fillStyle=col; ctx.font='bold 14px system-ui'; ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.fillText(d.kind==='wide'?'↔':'✦',d.x,d.y+1); ctx.restore();
}
for(const p of particles) {
ctx.globalAlpha=Math.max(0,p.life/p.max); ctx.fillStyle=p.color;
ctx.fillRect(p.x,p.y,p.size,p.size);
}
ctx.globalAlpha=1;
for(const ball of balls) {
for(let i=0;i<ball.trail.length;i++) {
const t=ball.trail[i], a=(i+1)/ball.trail.length*.16;
ctx.globalAlpha=a; ctx.beginPath(); ctx.arc(t.x,t.y,ball.r*(.45+i/ball.trail.length*.45),0,Math.PI*2);
ctx.fillStyle='#88fff5'; ctx.fill();
}
ctx.globalAlpha=1; ctx.save(); ctx.shadowColor='#72fff2'; ctx.shadowBlur=20;
ctx.beginPath(); ctx.arc(ball.x,ball.y,ball.r,0,Math.PI*2);
ctx.fillStyle='#eaffff'; ctx.fill(); ctx.restore();
}
const px=paddle.x-paddle.w/2;
ctx.save(); ctx.shadowColor=paddle.wide?'#6de7ff':'#58e9ef'; ctx.shadowBlur=20;
const pg=ctx.createLinearGradient(px,paddle.y,px,paddle.y+paddle.h);
pg.addColorStop(0,'#e6ffff'); pg.addColorStop(.35,paddle.wide?'#6de7ff':'#65faf1'); pg.addColorStop(1,'#25a9cc');
roundRect(px,paddle.y,paddle.w,paddle.h,7); ctx.fillStyle=pg; ctx.fill(); ctx.restore();
ctx.fillStyle='#76f5ed18'; ctx.fillRect(0,H-2,W,2);
if(mode==='gameover' || mode==='victory') {
ctx.fillStyle='#070a13bf'; ctx.fillRect(0,0,W,H);
ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.fillStyle=mode==='victory'?'#6af5c2':'#ff779c'; ctx.font='800 39px system-ui';
ctx.fillText(mode==='victory'?'SECTOR CLEARED':'GAME OVER',W/2,H/2-25);
ctx.fillStyle='#c0c9dc'; ctx.font='15px system-ui';
ctx.fillText(`FINAL SCORE ${String(score).padStart(5,'0')}`,W/2,H/2+14);
ctx.fillStyle='#8b97b1'; ctx.font='12px system-ui'; ctx.fillText('PRESS R OR CLICK TO PLAY AGAIN',W/2,H/2+46);
}
}
canvas.addEventListener('pointerdown', () => { if(mode!=='playing') restart(); });
function frame(now) {
const dt=Math.min(.03,(now-last)/1000); last=now;
if(mode==='playing') update(dt);
draw();
requestAnimationFrame(frame);
}
reset();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>