GPT-6.1 Terra
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 1 min 5 s
- Cost
- $0.029
- Output tokens
- 5,753
- Spent thinking
- 30 tokens
- Speed
- 92.3 tok/s
- Console errors
- 0
- First token after
- 2.4 s
- First visible text after
- 4.7 s
- Prompt tokens
- 508
- Code size
- 16.4 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6.1-terra- Reasoning sent
{"effort":"low"}- Max output
- 128,000 tokens
- Generated
- Nov 12, 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 · 201 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Neon Breaker</title>
<style>
:root{color-scheme:dark;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;background:#080c18;color:#f4f6ff;display:flex;align-items:center;justify-content:center;padding:24px}
body:before{content:"";position:fixed;inset:0;background:radial-gradient(ellipse at 50% 0%,#202648 0%,transparent 48%),radial-gradient(ellipse at 10% 85%,#102940 0%,transparent 35%);pointer-events:none}
.app{position:relative;width:min(100%,1040px)}
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;gap:16px}
.brand{display:flex;align-items:center;gap:12px}
.logo{width:38px;height:38px;border:1px solid #6e8cff;border-radius:11px;display:grid;place-items:center;background:linear-gradient(140deg,#516dff,#932cf2);box-shadow:0 0 22px #7251ff55}
.logo span{display:block;width:18px;height:15px;background:repeating-linear-gradient(to bottom,#fff 0 3px,transparent 3px 6px);transform:skewX(-15deg)}
h1{font-size:17px;letter-spacing:.14em;margin:0;font-weight:850}
.subtitle{font-size:10px;letter-spacing:.16em;color:#8c96b8;margin-top:3px;font-weight:700}
.topright{font-size:11px;color:#8b96b7;letter-spacing:.13em;font-weight:700;text-transform:uppercase}
.topright b{color:#c7d3ff}
.shell{border:1px solid #303a5b;border-radius:18px;background:#11182b;overflow:hidden;box-shadow:0 24px 80px #0008,0 0 0 1px #ffffff05}
.status{height:74px;display:flex;align-items:center;justify-content:space-between;padding:0 28px;border-bottom:1px solid #303a59;background:linear-gradient(90deg,#19223a,#141b30)}
.stat{display:flex;align-items:center;gap:13px}
.stat-icon{width:34px;height:34px;display:grid;place-items:center;border-radius:10px;background:#ffffff0a;color:#a0adff;font-size:16px}
.label{color:#8995b4;font-size:9px;letter-spacing:.15em;font-weight:800;margin-bottom:3px}
.value{font-size:18px;font-weight:800;letter-spacing:.04em;line-height:1}
.hearts{color:#ff6d96;letter-spacing:3px;font-size:17px;white-space:nowrap}
.level{color:#fff;font-size:13px}
.level em{font-style:normal;color:#8995b4;font-weight:500}
.game-wrap{position:relative;background:#090f20;line-height:0}
canvas{display:block;width:100%;height:auto;touch-action:none}
.start{position:absolute;left:50%;bottom:19%;transform:translateX(-50%);line-height:1.4;text-align:center;pointer-events:none;white-space:nowrap;transition:opacity .3s}
.start .pill{display:inline-flex;align-items:center;gap:8px;border:1px solid #7589d16b;background:#111a32df;border-radius:30px;padding:10px 17px;box-shadow:0 8px 30px #0008;color:#e9edff;font-size:12px;font-weight:700;letter-spacing:.02em}
.start .dot{width:7px;height:7px;background:#7ff6dc;border-radius:50%;box-shadow:0 0 10px #7ff6dc}
.start small{display:block;color:#8492b5;font-size:10px;letter-spacing:.1em;margin-top:8px;text-transform:uppercase}
.hidden{opacity:0}
.end{position:absolute;inset:0;display:none;align-items:center;justify-content:center;background:#080d1ce8;line-height:normal;text-align:center}
.end.show{display:flex}
.end-card{padding:30px}
.end-kicker{font-size:11px;color:#8d9cff;letter-spacing:.2em;font-weight:800}
.end h2{font-size:40px;letter-spacing:-.04em;margin:10px 0}
.end p{font-size:14px;color:#9ca9c9;margin:0 0 22px}
button{border:0;border-radius:9px;color:white;background:linear-gradient(100deg,#6a76ff,#a652e9);padding:12px 23px;font:700 13px inherit;cursor:pointer;box-shadow:0 5px 20px #745aff55}
button:hover{filter:brightness(1.15)}
footer{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:15px 4px;color:#7f8baa;font-size:11px}
.controls{display:flex;gap:18px;align-items:center}
kbd{font:inherit;color:#c1cae2;background:#1d263c;border:1px solid #39445f;border-radius:4px;padding:3px 6px;margin-right:5px}
.legend{display:flex;gap:14px;align-items:center}
.legend span{white-space:nowrap}.legend b{color:#88f3dc;margin-right:5px}.legend .multi{color:#f4b5ff}
@media(max-width:600px){body{padding:12px;align-items:flex-start}.app{margin-top:12px}header{margin-bottom:12px}.topright{font-size:9px}.status{height:62px;padding:0 14px}.stat{gap:7px}.stat-icon{display:none}.value{font-size:15px}.hearts{font-size:13px;letter-spacing:1px}.level{font-size:11px}.start .pill{font-size:10px;padding:7px 11px}.start small{font-size:8px;margin-top:4px}footer{flex-wrap:wrap;font-size:10px}.controls{gap:8px}.legend{gap:9px}}
</style>
</head>
<body>
<div class="app">
<header>
<div class="brand"><div class="logo"><span></span></div><div><h1>NEON BREAKER</h1><div class="subtitle">THE ARCADE CLASSIC, REMIXED</div></div></div>
<div class="topright">LEVEL <b>01</b> / 01</div>
</header>
<main class="shell">
<div class="status">
<div class="stat"><div class="stat-icon">✦</div><div><div class="label">SCORE</div><div class="value" id="score">00000</div></div></div>
<div class="stat"><div class="stat-icon">◈</div><div><div class="label">STAGE</div><div class="value level">01 <em> / THE GRID</em></div></div></div>
<div class="stat"><div><div class="label">LIVES</div><div class="value hearts" id="lives">♥ ♥ ♥</div></div></div>
</div>
<div class="game-wrap" id="gameWrap">
<canvas id="game" width="1000" height="570" aria-label="Breakout game"></canvas>
<div class="start" id="start"><div class="pill"><span class="dot"></span> Click or press a key to start</div><small>Demo playing · Take control anytime</small></div>
<div class="end" id="end"><div class="end-card"><div class="end-kicker" id="endKicker"></div><h2 id="endTitle"></h2><p id="endText"></p><button id="restart">PLAY AGAIN ↗</button></div></div>
</div>
</main>
<footer>
<div class="controls"><span><kbd>←</kbd><kbd>→</kbd> Move</span><span><kbd>◉</kbd> Mouse / touch</span></div>
<div class="legend"><span><b>↔</b> Wide paddle</span><span><b class="multi">✣</b> Multi-ball</span><span>◇ 2-hit brick</span></div>
</footer>
</div>
<script>
(() => {
const canvas = document.getElementById('game'), ctx = canvas.getContext('2d');
const W = 1000, H = 570;
const scoreEl = document.getElementById('score'), livesEl = document.getElementById('lives');
const startEl = document.getElementById('start'), endEl = document.getElementById('end');
const wrap = document.getElementById('gameWrap');
let bricks, balls, drops, particles, paddle, score, lives, mode, wideTime, keys, last, demoTime;
const colors = ['#fa6b9b','#f486a5','#b67aff','#8f8aff','#65b8f5','#5ed6da'];
const rows = 6, cols = 10, bw = 82, bh = 24, gap = 8, left = 54, top = 60;
function reset(demo) {
bricks=[]; balls=[]; drops=[]; particles=[]; score=0; lives=3; wideTime=0; keys={}; demoTime=0;
paddle={x:500,y:523,w:110,h:13,target:500};
for(let r=0;r<rows;r++) for(let c=0;c<cols;c++){
const tough = (r===1 && c%3===1)||(r===3 && c%3===0)||(r===0 && c%4===0);
let power = null;
if ((r===4 && c===2)||(r===2 && c===7)||(r===0 && c===5)) power='wide';
if ((r===4 && c===7)||(r===2 && c===2)||(r===0 && c===8)) power='multi';
bricks.push({x:left+c*(bw+gap),y:top+r*(bh+gap),w:bw,h:bh,hp:tough?2:1,max:tough?2:1,color:colors[r],power});
}
balls.push({x:500,y:425,vx:155,vy:-270,r:7,trail:[]});
mode=demo?'demo':'play'; startEl.classList.toggle('hidden',!demo); endEl.classList.remove('show'); updateUI();
}
function updateUI(){scoreEl.textContent=String(score).padStart(5,'0');livesEl.textContent='♥ '.repeat(lives).trim() + (lives<3?' ♡'.repeat(3-lives):'')}
function activate(){if(mode==='demo'){reset(false)}}
function endGame(win){
mode='end'; document.getElementById('endKicker').textContent=win?'STAGE COMPLETE':'GAME OVER';
document.getElementById('endTitle').textContent=win?'You broke through.':'Nice run.';
document.getElementById('endText').textContent=(win?'All bricks cleared. ':'The grid wins this time. ')+'Final score: '+score.toLocaleString();
endEl.classList.add('show');
}
document.getElementById('restart').addEventListener('click',e=>{e.stopPropagation();reset(false)});
wrap.addEventListener('pointerdown',e=>{if(mode==='end')return;activate(); movePointer(e)});
wrap.addEventListener('pointermove',e=>{if(mode==='play' && (e.pointerType==='mouse'||e.buttons))movePointer(e)});
function movePointer(e){const rect=canvas.getBoundingClientRect(); paddle.target=(e.clientX-rect.left)/rect.width*W}
window.addEventListener('keydown',e=>{
if(['ArrowLeft','ArrowRight',' ','Enter'].includes(e.key))e.preventDefault();
if(mode==='end'){if(e.key==='Enter'||e.key===' ')reset(false);return}
activate(); keys[e.key]=true;
});
window.addEventListener('keyup',e=>{keys[e.key]=false});
window.addEventListener('blur',()=>{keys={}});
function burst(x,y,color){
for(let i=0;i<13;i++){let a=Math.random()*Math.PI*2,s=40+Math.random()*160;particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:.45+Math.random()*.5,max:1,color,size:2+Math.random()*3})}
}
function spawnMulti(){
if(!balls.length)return;
const b=balls[0], speed=Math.hypot(b.vx,b.vy);
while(balls.length<3){let angle=balls.length===1?-0.65:0.65; let vx=b.vx*Math.cos(angle)-b.vy*Math.sin(angle),vy=b.vx*Math.sin(angle)+b.vy*Math.cos(angle); if(Math.abs(vy)<90)vy=vy<0?-120:120; let f=speed/Math.hypot(vx,vy);balls.push({x:b.x,y:b.y,vx:vx*f,vy:vy*f,r:7,trail:[]})}
}
function hitBrick(b,ball,oldX,oldY){
b.hp--; if(b.hp===0){score+=b.max===2?150:100;burst(b.x+b.w/2,b.y+b.h/2,b.color);if(b.power)drops.push({x:b.x+b.w/2,y:b.y+b.h/2,type:b.power});} else {score+=25;burst(ball.x,ball.y,b.color)}
updateUI();
const wasAbove=oldY+ball.r<=b.y, wasBelow=oldY-ball.r>=b.y+b.h, wasLeft=oldX+ball.r<=b.x, wasRight=oldX-ball.r>=b.x+b.w;
if(wasAbove){ball.y=b.y-ball.r;ball.vy=-Math.abs(ball.vy)}
else if(wasBelow){ball.y=b.y+b.h+ball.r;ball.vy=Math.abs(ball.vy)}
else if(wasLeft){ball.x=b.x-ball.r;ball.vx=-Math.abs(ball.vx)}
else if(wasRight){ball.x=b.x+b.w+ball.r;ball.vx=Math.abs(ball.vx)}
else {ball.vy*=-1;ball.y+=Math.sign(ball.vy)*3}
}
function update(dt){
if(mode==='end')return;
demoTime+=dt;
if(wideTime>0)wideTime=Math.max(0,wideTime-dt);
const desired=wideTime>0?165:110;paddle.w+=(desired-paddle.w)*Math.min(1,dt*8);
if(mode==='demo'){
let target=balls.filter(b=>b.vy>0).sort((a,b)=>b.y-a.y)[0]||balls[0];
if(target){let t=Math.max(0,(paddle.y-target.y)/Math.max(100,target.vy));let x=target.x+target.vx*t;let span=W-20;x=((x-10)%(2*span)+2*span)%(2*span);if(x>span)x=2*span-x;paddle.target=x+10+Math.sin(demoTime*1.3)*19}
} else if(keys.ArrowLeft||keys.ArrowRight){paddle.target=paddle.x+((keys.ArrowRight?1:0)-(keys.ArrowLeft?1:0))*650*dt}
paddle.target=Math.max(paddle.w/2+12,Math.min(W-paddle.w/2-12,paddle.target));
paddle.x+=(paddle.target-paddle.x)*Math.min(1,dt*(mode==='demo'?7:18));
for(let i=balls.length-1;i>=0;i--){
let ball=balls[i];ball.trail.unshift({x:ball.x,y:ball.y});if(ball.trail.length>8)ball.trail.pop();
const steps=Math.max(1,Math.ceil(Math.hypot(ball.vx,ball.vy)*dt/3));const step=dt/steps;
for(let s=0;s<steps;s++){
let 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<ball.r+10){ball.y=ball.r+10;ball.vy=Math.abs(ball.vy)}
if(ball.vy>0 && oy+ball.r<=paddle.y && ball.y+ball.r>=paddle.y && ball.x+ball.r>=paddle.x-paddle.w/2 && ball.x-ball.r<=paddle.x+paddle.w/2){
ball.y=paddle.y-ball.r;let offset=Math.max(-1,Math.min(1,(ball.x-paddle.x)/(paddle.w/2)));let angle=offset*1.12;let speed=Math.min(430,Math.max(315,Math.hypot(ball.vx,ball.vy)+2));ball.vx=Math.sin(angle)*speed;ball.vy=-Math.cos(angle)*speed;
}
for(const b of bricks){if(b.hp>0 && 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){hitBrick(b,ball,ox,oy);break}}
}
if(ball.y>H+20)balls.splice(i,1);
}
if(!balls.length){
if(mode==='demo'){reset(true);return}
lives--;updateUI();if(lives<=0){endGame(false);return}
balls.push({x:paddle.x,y:paddle.y-25,vx:135,vy:-290,r:7,trail:[]});
}
if(bricks.every(b=>b.hp===0)){if(mode==='demo')reset(true);else endGame(true);return}
for(let i=drops.length-1;i>=0;i--){let d=drops[i];d.y+=115*dt;
if(d.y+12>=paddle.y && d.y-12<=paddle.y+paddle.h && Math.abs(d.x-paddle.x)<paddle.w/2+10){if(d.type==='wide')wideTime=10;else spawnMulti();burst(d.x,d.y,d.type==='wide'?'#81f7df':'#e6adff');drops.splice(i,1)}
else if(d.y>H+15)drops.splice(i,1);
}
for(let i=particles.length-1;i>=0;i--){let p=particles[i];p.x+=p.vx*dt;p.y+=p.vy*dt;p.vy+=180*dt;p.life-=dt;if(p.life<=0)particles.splice(i,1)}
}
function rounded(x,y,w,h,r){ctx.beginPath();ctx.roundRect(x,y,w,h,r)}
function draw(){
ctx.clearRect(0,0,W,H);
let bg=ctx.createLinearGradient(0,0,0,H);bg.addColorStop(0,'#101932');bg.addColorStop(1,'#090f20');ctx.fillStyle=bg;ctx.fillRect(0,0,W,H);
ctx.strokeStyle='#263453';ctx.globalAlpha=.24;ctx.lineWidth=1;
for(let x=0;x<W;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke()}
for(let y=0;y<H;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke()}ctx.globalAlpha=1;
ctx.fillStyle='#7582a5';ctx.font='700 10px system-ui';ctx.letterSpacing='2px';ctx.fillText('CLEAR THE GRID',54,34);ctx.textAlign='right';ctx.fillText(bricks.filter(b=>b.hp>0).length+' BRICKS REMAINING',946,34);ctx.textAlign='left';ctx.letterSpacing='0px';
for(const b of bricks){if(!b.hp)continue;
ctx.save();ctx.shadowColor=b.color;ctx.shadowBlur=9;ctx.fillStyle=b.color;rounded(b.x,b.y,b.w,b.h,5);ctx.fill();ctx.restore();
let grad=ctx.createLinearGradient(0,b.y,0,b.y+b.h);grad.addColorStop(0,'#ffffff55');grad.addColorStop(.48,'#ffffff08');grad.addColorStop(1,'#00000030');ctx.fillStyle=grad;rounded(b.x,b.y,b.w,b.h,5);ctx.fill();
ctx.strokeStyle='#ffffff4a';ctx.lineWidth=1;rounded(b.x+.5,b.y+.5,b.w-1,b.h-1,5);ctx.stroke();
if(b.max===2){ctx.strokeStyle='#ffffffaa';ctx.lineWidth=1.3;if(b.hp===2){ctx.beginPath();ctx.moveTo(b.x+9,b.y+8);ctx.lineTo(b.x+13,b.y+4);ctx.lineTo(b.x+17,b.y+8);ctx.lineTo(b.x+13,b.y+12);ctx.closePath();ctx.stroke()}else{ctx.beginPath();ctx.moveTo(b.x+38,b.y+2);ctx.lineTo(b.x+34,b.y+9);ctx.lineTo(b.x+40,b.y+12);ctx.lineTo(b.x+35,b.y+22);ctx.moveTo(b.x+40,b.y+12);ctx.lineTo(b.x+48,b.y+15);ctx.lineTo(b.x+51,b.y+21);ctx.stroke()}}
if(b.power){ctx.fillStyle='#fff';ctx.font='bold 17px system-ui';ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText(b.power==='wide'?'↔':'✣',b.x+b.w/2,b.y+b.h/2+1);ctx.textAlign='left';ctx.textBaseline='alphabetic'}
}
for(const d of drops){ctx.save();ctx.shadowColor=d.type==='wide'?'#72ffe0':'#e6a4ff';ctx.shadowBlur=16;ctx.fillStyle=d.type==='wide'?'#69e8d1':'#d38df5';rounded(d.x-15,d.y-12,30,24,7);ctx.fill();ctx.shadowBlur=0;ctx.fillStyle='#142039';ctx.font='bold 17px system-ui';ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText(d.type==='wide'?'↔':'✣',d.x,d.y+1);ctx.restore()}
for(const p of particles){ctx.globalAlpha=Math.max(0,p.life);ctx.fillStyle=p.color;ctx.fillRect(p.x,p.y,p.size,p.size)}ctx.globalAlpha=1;
for(const b of balls){for(let i=b.trail.length-1;i>=0;i--){ctx.fillStyle=`rgba(155,206,255,${(1-i/b.trail.length)*.13})`;ctx.beginPath();ctx.arc(b.trail[i].x,b.trail[i].y,b.r*(1-i/b.trail.length*.65),0,Math.PI*2);ctx.fill()}ctx.save();ctx.shadowColor='#a8ddff';ctx.shadowBlur=20;ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,Math.PI*2);ctx.fill();ctx.restore()}
ctx.save();ctx.shadowColor=wideTime>0?'#78fce2':'#8b9bff';ctx.shadowBlur=20;let pg=ctx.createLinearGradient(paddle.x-paddle.w/2,0,paddle.x+paddle.w/2,0);pg.addColorStop(0,wideTime>0?'#63e7ce':'#768cff');pg.addColorStop(.5,'#e2eaff');pg.addColorStop(1,wideTime>0?'#63e7ce':'#a976ff');ctx.fillStyle=pg;rounded(paddle.x-paddle.w/2,paddle.y,paddle.w,paddle.h,7);ctx.fill();ctx.restore();
if(wideTime>0){ctx.fillStyle='#91ebd9';ctx.font='700 10px system-ui';ctx.textAlign='center';ctx.fillText('WIDE '+Math.ceil(wideTime)+'s',paddle.x,paddle.y-12);ctx.textAlign='left'}
ctx.fillStyle='#ffffff14';ctx.fillRect(50,H-10,W-100,1);
}
function frame(t){if(!last)last=t;let dt=Math.min((t-last)/1000,.035);last=t;update(dt);draw();requestAnimationFrame(frame)}
reset(true);draw();requestAnimationFrame(frame);
})();
</script>
</body>
</html>Reasoning trace (30 tokens)
**Planning game demo implementation** I'm focusing on creating a polished canvas game demo with autoplay features that don't obstruct the gameplay. I need it to be responsive and handle collision substeps effectively. Power-ups and user interface design are definitely on my list too. It's all about producing a seamless experience for players while ensuring everything works smoothly together. I wonder if I'll hit any snags, but I'm excited to get started!