Models 44

GPT-6.1 Sol

by OpenAIreasoning defaultClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
2 min 19 s
Cost
$0.097
Output tokens
9,628
Spent thinking
1,372 tokens
Speed
80.2 tok/s
Console errors
0
First token after
18.9 s
First visible text after
50.3 s
Prompt tokens
508
Code size
23.4 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-6.1-sol
Reasoning sent
model default
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 · 379 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Breakout — Offscript Arcade</title>
<style>
:root{color-scheme:dark;--bg:#0c1016;--panel:#111720;--line:#25303c;--text:#ecf1f5;--muted:#82909e;--mint:#b5f4ce}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;min-height:100vh}
body:before{content:"";position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at 50% 25%,#1b2a302b,transparent 65%)}
button{font:inherit;cursor:pointer}
.shell{width:min(1104px,calc(100% - 64px));margin:auto;position:relative}
nav{height:77px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #ffffff0d}
.brand{display:flex;align-items:center;gap:11px;font-size:17px;font-weight:700;letter-spacing:-.6px}
.brand svg{color:var(--mint)}
.brand span{color:#768493;font-weight:400}
.edition{font:10px ui-monospace,monospace;color:#8693a0;letter-spacing:2px;display:flex;align-items:center;gap:12px}
.edition i{width:6px;height:6px;border-radius:100%;background:var(--mint);box-shadow:0 0 12px #b5f4ce66}
.titlebar{display:flex;align-items:center;justify-content:space-between;padding:24px 0 23px;gap:15px}
h1{font-size:36px;letter-spacing:-1.6px;line-height:1;margin:0 0 9px;font-weight:650}
h1 span{color:var(--mint)}
.subtitle{font-size:13px;color:var(--muted);margin:0;letter-spacing:.1px}
.level{display:flex;align-items:center;gap:13px}
.level-number{font:24px ui-monospace,monospace;color:#b0bcc7}
.level-info{font:10px/1.8 ui-monospace,monospace;letter-spacing:1.5px;color:var(--muted)}
.level-info strong{display:block;color:#d7e0e8;font-weight:400;letter-spacing:1px}
.game{overflow:hidden;border:1px solid #2b3541;border-radius:14px;background:#10161f;box-shadow:0 18px 65px #0003}
.scorebar{height:72px;display:flex;align-items:center;justify-content:space-between;padding:0 25px;border-bottom:1px solid #27313d;background:#141b24}
.stats{display:flex;align-items:center;gap:35px}
.stat{min-width:73px}
.label{display:block;font:9px ui-monospace,monospace;letter-spacing:1.7px;color:#8594a5;margin-bottom:6px}
.value{font:21px ui-monospace,monospace;letter-spacing:1px;line-height:1}
.life-icons{display:flex;gap:6px;height:21px;align-items:center}
.life-icons svg{width:16px;height:17px;fill:var(--mint);transition:opacity .2s}
.life-icons svg.lost{fill:#34404c}
.status-group{display:flex;align-items:center;gap:15px}
.mode{font:9px ui-monospace,monospace;letter-spacing:1.3px;color:var(--mint);display:flex;align-items:center;gap:7px;border:1px solid #b5f4ce26;background:#b5f4ce08;padding:7px 9px;border-radius:4px}
.mode:before{content:"";width:5px;height:5px;background:var(--mint);border-radius:50%;box-shadow:0 0 8px #b5f4ce50}
.icon-button{border:1px solid #303b47;background:transparent;color:#9eacba;border-radius:6px;width:32px;height:32px;display:grid;place-items:center;transition:.15s}
.icon-button:hover{color:#fff;background:#ffffff0a;border-color:#748191}
.icon-button svg{width:15px;height:15px}
.tools{display:flex;gap:7px}
.field{position:relative;height:clamp(360px,55vh,450px);background:radial-gradient(ellipse at 50% 35%,#17222b 0%,#10161f 65%);overflow:hidden}
canvas{display:block;width:100%;height:100%;touch-action:none;outline:none}
.overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none}
.overlay.hidden{display:none}
.dialog{pointer-events:auto;text-align:center;max-width:calc(100% - 32px);padding:23px 28px 22px;border:1px solid #3b4b57;background:#111b24ed;border-radius:11px;box-shadow:0 10px 45px #0005;backdrop-filter:blur(9px);min-width:330px;transform:translateY(48px)}
.eyebrow{font:9px ui-monospace,monospace;letter-spacing:2px;color:var(--mint);margin-bottom:10px;display:flex;gap:9px;align-items:center;justify-content:center}
.eyebrow:before,.eyebrow:after{content:"";height:1px;width:17px;background:#b5f4ce50}
.dialog h2{font-size:25px;font-weight:550;letter-spacing:-.8px;margin:0 0 9px}
.dialog p{font-size:12px;color:#a4b1bd;margin:0 0 17px;line-height:1.5}
.start{background:var(--mint);color:#192c24;font-weight:650;font-size:12px;padding:12px 18px;border:0;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;gap:15px;box-shadow:0 3px 15px #a5efc415;transition:.15s}
.start:hover{background:#d0ffdf;transform:translateY(-1px)}
.start svg{width:14px;height:14px}
.gamefooter{min-height:56px;border-top:1px solid #27313d;display:flex;align-items:center;justify-content:space-between;padding:12px 23px;gap:12px;background:#131a23}
.controls{display:flex;align-items:center;gap:7px;font-size:11px;color:#8f9caa}
kbd{font:11px ui-monospace,monospace;border:1px solid #3c4652;border-bottom:2px solid #3c4652;border-radius:4px;padding:2px 5px;color:#c2ccd6;background:#1b232e;min-width:20px;text-align:center}
.control-divider{color:#45515d;margin:0 4px}
.legend{display:flex;gap:19px;font:10px ui-monospace,monospace;color:#95a2af}
.legend-item{display:flex;align-items:center;gap:6px}
.badge-icon{height:19px;min-width:22px;display:grid;place-items:center;font-size:12px;border-radius:4px;background:#b5f4ce13;border:1px solid #b5f4ce40;color:var(--mint)}
.badge-icon.multi{color:#c1b4fa;border-color:#c1b4fa40;background:#c1b4fa13;font-size:15px}
.bottom{padding:18px 2px;display:flex;align-items:center;justify-content:space-between;font-size:11px;color:#667684}
.bottom span:last-child{font:9px ui-monospace,monospace;letter-spacing:1.5px}
#power{font:10px ui-monospace,monospace;color:var(--mint);margin-left:7px;letter-spacing:0;min-width:0}
@media(min-height:850px){.titlebar{padding-top:32px;padding-bottom:29px}.field{height:480px}}
@media(max-width:650px){.shell{width:calc(100% - 28px)}nav{height:61px}.edition{font-size:8px;letter-spacing:1px}.brand{font-size:15px}.titlebar{padding:23px 1px}h1{font-size:31px}.subtitle{font-size:11px}.level-info{font-size:8px}.level-number{font-size:21px}.level{gap:8px}.scorebar{height:68px;padding:0 14px}.stats{gap:19px}.stat{min-width:58px}.value{font-size:18px}.status-group{gap:8px}.mode{font-size:8px;padding:6px;letter-spacing:.5px}.tools{gap:5px}.icon-button{width:27px;height:28px}.field{height:450px}.gamefooter{padding:12px;align-items:flex-start;flex-direction:column;gap:13px}.legend{width:100%;justify-content:space-between;font-size:9px}.controls{font-size:10px}.dialog{min-width:285px;padding:20px 16px;transform:translateY(35px)}.dialog h2{font-size:23px}.bottom{font-size:10px}.bottom span:last-child{font-size:8px;letter-spacing:.6px}#power{display:none}}
@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto}}
</style>
</head>
<body>
<div class="shell">
<nav>
 <div class="brand"><svg width="23" height="25" viewBox="0 0 23 25" fill="none"><path d="M2 7 11.5 2 21 7v11l-9.5 5L2 18V7Z" stroke="currentColor" stroke-width="1.5"/><path d="m2 7 9.5 5L21 7M11.5 12v11M6.5 4.5 16 9.6" stroke="currentColor" stroke-width="1.5"/></svg>offscript <span>/ arcade</span></div>
 <div class="edition"><i></i> SMALL GAMES. GOOD TIMES.</div>
</nav>
<section class="titlebar">
 <div><h1>Breakout<span>.</span></h1><p class="subtitle">Find your angle. Break the pattern.</p></div>
 <div class="level"><div class="level-number">01</div><div class="level-info">THE PRISM FIELD<strong>6 ROWS. ONE CLEAN SLATE.</strong></div></div>
</section>
<main class="game">
 <div class="scorebar">
  <div class="stats">
   <div class="stat"><span class="label">SCORE</span><div class="value" id="score">00000</div></div>
   <div class="stat"><span class="label">LIVES</span><div class="life-icons" id="lives"></div></div>
   <div class="stat" id="remaining-stat"><span class="label">BRICKS</span><div class="value" id="remaining">72<span style="font-size:11px;color:#617180"> / 72</span></div></div>
  </div>
  <div class="status-group"><span id="power"></span><span class="mode" id="mode">LIVE DEMO</span><div class="tools">
   <button class="icon-button" id="pause" title="Pause / resume (Space)" aria-label="Pause or resume"><svg viewBox="0 0 20 20" fill="currentColor"><rect x="5" y="4" width="3" height="12" rx="1"/><rect x="12" y="4" width="3" height="12" rx="1"/></svg></button>
   <button class="icon-button" id="restart" title="Restart (R)" aria-label="Restart game"><svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M4 8a6.5 6.5 0 1 1-.3 4M4 3v5h5" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
  </div></div>
 </div>
 <div class="field">
  <canvas id="canvas" tabindex="0" aria-label="Breakout game. Move with the mouse or arrow keys. Space pauses."></canvas>
  <div class="overlay" id="overlay"><div class="dialog">
   <div class="eyebrow" id="overlay-label">THE FLOOR IS YOURS</div>
   <h2 id="overlay-title">Make a clean break.</h2>
   <p id="overlay-copy">A classic, with a few little upgrades.</p>
   <button class="start" id="start"><span id="start-label">Click or press a key to start</span><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 8h11M8 3l5 5-5 5"/></svg></button>
  </div></div>
 </div>
 <div class="gamefooter">
  <div class="controls"><svg width="13" height="17" viewBox="0 0 13 17" fill="none" stroke="#9eacba"><rect x="1.5" y="1" width="10" height="15" rx="5"/><path d="M6.5 4v3"/></svg> Move mouse <span class="control-divider">/</span> <kbd>←</kbd><kbd>→</kbd> to move <span class="control-divider">·</span><kbd>space</kbd> pause</div>
  <div class="legend"><span class="legend-item"><span class="badge-icon">↔</span> Wide paddle · 10s</span><span class="legend-item"><span class="badge-icon multi">∴</span> Multiball</span><span class="legend-item"><span style="color:#b3bdc7">▤</span> Armored · 2 hits</span></div>
 </div>
</main>
<footer class="bottom"><span>A little focus. A lot of impact.</span><span>BUILT FOR THE BREAK BETWEEN THINGS.</span></footer>
</div>
<script>
(() => {
'use strict';
const canvas=document.getElementById('canvas'), ctx=canvas.getContext('2d');
const $=id=>document.getElementById(id);
const palette=['#b5a1ec','#939ee6','#7bb7da','#7fcbbd','#dccb87','#e5a38a'];
let W=1000,H=440,bricks=[],balls=[],drops=[],particles=[],score=0,lives=3,total=72;
let demo=true,paused=false,ended=false,wide=0,shake=0,clock=0,serve=0,endTimer=0;
let paddle={x:500,y:400,w:100,h:12},target=500,keys={left:false,right:false},pointer=false;
let last=0,accumulator=0,uiScore=-1,uiLives=-1,uiRemain=-1;
const STEP=1/180;
function rounded(x,y,w,h,r){ctx.beginPath();ctx.roundRect(x,y,w,h,r)}
function resize(){
 const oldW=W,oldH=H,rect=canvas.getBoundingClientRect();
 W=rect.width;H=rect.height;
 const dpr=Math.min(window.devicePixelRatio||1,2);
 canvas.width=Math.round(W*dpr);canvas.height=Math.round(H*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);
 paddle.x=paddle.x/oldW*W;paddle.y=H-35;target=paddle.x;
 paddle.w=baseWidth()*(wide>0?1.65:1);
 balls.forEach(b=>{b.x=b.x/oldW*W;b.y=b.y/oldH*H;b.trail=[]});
 layoutBricks();
}
function baseWidth(){return W<500?75:106}
function layoutBricks(){
 const cols=W<500?8:12,margin=W<500?17:32,gap=W<500?6:8;
 if(bricks.length && cols!==bricks[0].cols){buildBricks();return}
 const bw=(W-margin*2-gap*(cols-1))/cols,bh=W<500?22:25;
 bricks.forEach(b=>{b.x=margin+b.col*(bw+gap);b.y=29+b.row*(bh+8);b.w=bw;b.h=bh});
}
function buildBricks(){
 const cols=W<500?8:12; bricks=[];total=cols*6;
 for(let r=0;r<6;r++)for(let c=0;c<cols;c++){
  const tough=(r===0 || (r+c)%4===0);
  const power=(r*cols+c)%11===4?'wide':((r*cols+c)%13===8?'multi':null);
  bricks.push({row:r,col:c,cols,hp:tough?2:1,max:tough?2:1,power,alive:true,flash:0,x:0,y:0,w:0,h:0});
 }
 layoutBricks();uiRemain=-1;
}
function speed(){return W<500?315:410}
function newBall(x,y,angle=0.35){
 const s=speed();return {x,y,vx:Math.sin(angle)*s,vy:-Math.cos(angle)*s,r:5,trail:[]};
}
function reset(asDemo=false){
 demo=asDemo;paused=false;ended=false;score=0;lives=3;wide=0;serve=0;endTimer=0;clock=0;
 particles=[];drops=[];paddle.w=baseWidth();paddle.x=W/2;target=W/2;
 buildBricks();balls=[newBall(W*.48,H*.68,.36)];uiScore=uiLives=uiRemain=-1;
 $('mode').textContent=demo?'LIVE DEMO':'IN PLAY';
 if(demo)showOverlay('THE FLOOR IS YOURS','Make a clean break.','A classic, with a few little upgrades.','Click or press a key to start');
 else $('overlay').classList.add('hidden');
 updateUI();
}
function showOverlay(label,title,copy,button){
 $('overlay-label').textContent=label;$('overlay-title').textContent=title;$('overlay-copy').textContent=copy;$('start-label').textContent=button;$('overlay').classList.remove('hidden');
}
function updateUI(){
 if(score!==uiScore){$('score').textContent=String(score).padStart(5,'0');uiScore=score}
 if(lives!==uiLives){$('lives').innerHTML=Array.from({length:3},(_,i)=>`<svg viewBox="0 0 20 20" class="${i>=lives?'lost':''}"><path d="M10 17.1 3.1 10.4C-1 6.4 4.7.2 10 5.2c5.3-5 11 1.2 6.9 5.2Z"/></svg>`).join('');uiLives=lives}
 const left=bricks.filter(b=>b.alive).length;
 if(left!==uiRemain){$('remaining').innerHTML=left+`<span style="font-size:10px;color:#617180"> / ${total}</span>`;uiRemain=left}
 $('power').textContent=wide>0?'↔ '+Math.ceil(wide)+'s':'';
}
function burst(x,y,color,count=15){
 for(let i=0;i<count;i++){const a=Math.random()*Math.PI*2,s=35+Math.random()*150;particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:.4+Math.random()*.4,max:.8,color,size:1.5+Math.random()*3})}
}
function hitBrick(b){
 b.hp--;b.flash=.12;
 if(b.hp<=0){
  b.alive=false;score+=b.max===2?150:100;burst(b.x+b.w/2,b.y+b.h/2,palette[b.row]);shake=2;
  if(b.power)drops.push({x:b.x+b.w/2,y:b.y+b.h/2,type:b.power,phase:Math.random()*6});
  if(!bricks.some(q=>q.alive))finish(true);
 }else{score+=25;burst(b.x+b.w/2,b.y+b.h/2,palette[b.row],5)}
}
function finish(won){
 ended=true;endTimer=0;
 if(demo)return;
 $('mode').textContent=won?'COMPLETE':'GAME OVER';
 showOverlay(won?'A CLEAN SLATE':'ONE MORE ROUND?',won?'Beautifully broken.':'End of the line.',`Final score: ${score.toLocaleString()}${won?' · Every brick, accounted for.':' · A fresh angle changes everything.'}`,'Play again');
}
function sweep(b,rect,t){
 const minX=rect.x-b.r,maxX=rect.x+rect.w+b.r,minY=rect.y-b.r,maxY=rect.y+rect.h+b.r;
 let tx1=-Infinity,tx2=Infinity,ty1=-Infinity,ty2=Infinity;
 if(Math.abs(b.vx)<1e-8){if(b.x<minX||b.x>maxX)return null}
 else{tx1=(minX-b.x)/b.vx;tx2=(maxX-b.x)/b.vx;if(tx1>tx2)[tx1,tx2]=[tx2,tx1]}
 if(Math.abs(b.vy)<1e-8){if(b.y<minY||b.y>maxY)return null}
 else{ty1=(minY-b.y)/b.vy;ty2=(maxY-b.y)/b.vy;if(ty1>ty2)[ty1,ty2]=[ty2,ty1]}
 const entry=Math.max(tx1,ty1),exit=Math.min(tx2,ty2);
 if(entry>exit||exit<0||entry<-.000001||entry>t)return null;
 return {t:Math.max(0,entry),nx:tx1>ty1?(b.vx>0?-1:1):0,ny:ty1>=tx1?(b.vy>0?-1:1):0};
}
function moveBall(b,dt){
 let left=dt;
 for(let iter=0;iter<8&&left>0.0000001;iter++){
  let event=null,time=left+.00001;
  function consider(t,nx,ny,type,obj){if(t>=-.000001&&t<=left&&t<time){time=Math.max(0,t);event={nx,ny,type,obj}}}
  if(b.vx<0)consider((b.r+10-b.x)/b.vx,1,0,'wall');
  if(b.vx>0)consider((W-10-b.r-b.x)/b.vx,-1,0,'wall');
  if(b.vy<0)consider((b.r+8-b.y)/b.vy,0,1,'wall');
  if(b.vy>0){
   const hit=sweep(b,{x:paddle.x-paddle.w/2,y:paddle.y,w:paddle.w,h:paddle.h},left);
   if(hit&&b.y<=paddle.y)consider(hit.t,hit.nx,hit.ny,'paddle');
  }
  for(const brick of bricks){
   if(!brick.alive)continue;
   const hit=sweep(b,brick,left);
   if(hit)consider(hit.t,hit.nx,hit.ny,'brick',brick);
  }
  if(!event){b.x+=b.vx*left;b.y+=b.vy*left;break}
  b.x+=b.vx*time;b.y+=b.vy*time;left-=time;
  if(event.type==='paddle'){
   const offset=Math.max(-1,Math.min(1,(b.x-paddle.x)/(paddle.w/2)));
   const a=offset*Math.PI*.365,s=Math.hypot(b.vx,b.vy);
   b.vx=Math.sin(a)*s;b.vy=-Math.cos(a)*s;b.y=paddle.y-b.r-.1;
   burst(b.x,paddle.y,'#b5f4ce',4);
  }else{
   if(event.nx)b.vx=-b.vx;
   if(event.ny)b.vy=-b.vy;
   b.x+=event.nx*.05;b.y+=event.ny*.05;
   if(event.type==='brick')hitBrick(event.obj);
  }
 }
}
function getDrop(d){
 burst(d.x,d.y,d.type==='wide'?'#b5f4ce':'#b5a1ec',20);
 if(d.type==='wide'){wide=10;paddle.w=baseWidth()*1.65}
 else{
  const source=balls[0];
  if(source){
   const current=balls.length;
   for(let i=current;i<3;i++){
    const a=(i===1?-.57:.57);
    balls.push(newBall(source.x+(i===1?-3:3),source.y,a));
   }
  }
 }
 score+=50;
}
function update(dt){
 clock+=dt;
 if(ended){endTimer+=dt;if(demo&&endTimer>2)reset(true);updateParticles(dt);return}
 if(wide>0){wide-=dt;if(wide<=0)paddle.w=baseWidth()}
 if(demo){
  let b=balls.filter(b=>b.vy>0).sort((a,b)=>(paddle.y-a.y)/a.vy-(paddle.y-b.y)/b.vy)[0]||balls[0];
  if(b){
   let px=b.x;
   if(b.vy>0){px=b.x+b.vx*Math.max(0,(paddle.y-b.y)/b.vy);const span=W-30;px=((px-15)%(span*2)+span*2)%(span*2);px=15+(px>span?2*span-px:px)}
   target=px+Math.sin(clock*1.7)*paddle.w*.24;
  }
  const drop=drops.find(d=>d.y>H*.58&&Math.abs(d.x-paddle.x)<150);
  if(drop&&(!b||b.vy<0||b.y<H*.65))target=drop.x;
 }else if(keys.left||keys.right){target=paddle.x+(Number(keys.right)-Number(keys.left))*700*dt;pointer=false}
 const limit=paddle.w/2+12;
 target=Math.max(limit,Math.min(W-limit,target));
 const delta=target-paddle.x;
 paddle.x+=Math.sign(delta)*Math.min(Math.abs(delta),dt*(demo?1050:1800));
 paddle.x=Math.max(limit,Math.min(W-limit,paddle.x));
 if(serve>0){serve-=dt;if(serve<=0)balls=[newBall(paddle.x,paddle.y-18,(Math.random()-.5)*.8)]}
 for(let i=balls.length-1;i>=0;i--){
  const b=balls[i];moveBall(b,dt);
  if(b.y>H+20)balls.splice(i,1);
 }
 if(!balls.length&&serve<=0&&!ended){
  lives--;wide=0;paddle.w=baseWidth();drops=[];
  if(lives<=0)finish(false);else serve=.7;
 }
 for(let i=drops.length-1;i>=0;i--){
  const d=drops[i];d.y+=105*dt;d.phase+=dt*3;
  if(d.y+11>=paddle.y&&d.y-11<=paddle.y+paddle.h&&Math.abs(d.x-paddle.x)<paddle.w/2+10){getDrop(d);drops.splice(i,1)}
  else if(d.y>H+20)drops.splice(i,1);
 }
 bricks.forEach(b=>b.flash=Math.max(0,b.flash-dt));
 shake=Math.max(0,shake-dt*12);updateParticles(dt);
}
function updateParticles(dt){
 for(let i=particles.length-1;i>=0;i--){const p=particles[i];p.x+=p.vx*dt;p.y+=p.vy*dt;p.vy+=190*dt;p.life-=dt;if(p.life<=0)particles.splice(i,1)}
}
function powerIcon(x,y,type,color,size=1){
 ctx.save();ctx.translate(x,y);ctx.scale(size,size);ctx.strokeStyle=color;ctx.fillStyle=color;ctx.lineWidth=1.3;ctx.lineCap='round';ctx.lineJoin='round';
 if(type==='wide'){ctx.beginPath();ctx.moveTo(-8,0);ctx.lineTo(8,0);ctx.moveTo(-5,-3);ctx.lineTo(-8,0);ctx.lineTo(-5,3);ctx.moveTo(5,-3);ctx.lineTo(8,0);ctx.lineTo(5,3);ctx.stroke()}
 else{[[-4,3],[4,3],[0,-4]].forEach(([a,b])=>{ctx.beginPath();ctx.arc(a,b,2,0,Math.PI*2);ctx.fill()})}
 ctx.restore();
}
function draw(){
 ctx.clearRect(0,0,W,H);ctx.save();
 if(shake>0)ctx.translate((Math.random()-.5)*shake,(Math.random()-.5)*shake);
 ctx.fillStyle='#a8c7dd0e';
 for(let x=18;x<W;x+=25)for(let y=18;y<H;y+=25){ctx.beginPath();ctx.arc(x,y,.65,0,Math.PI*2);ctx.fill()}
 ctx.strokeStyle='#687b8b15';ctx.lineWidth=1;
 ctx.beginPath();ctx.moveTo(10,16);ctx.lineTo(10,H-18);ctx.moveTo(W-10,16);ctx.lineTo(W-10,H-18);ctx.stroke();
 for(const b of bricks){
  if(!b.alive)continue;
  const color=palette[b.row];
  ctx.fillStyle='#00000030';rounded(b.x,b.y+3,b.w,b.h,4);ctx.fill();
  const gradient=ctx.createLinearGradient(0,b.y,0,b.y+b.h);
  gradient.addColorStop(0,color);gradient.addColorStop(1,color+'cc');
  ctx.fillStyle=gradient;rounded(b.x,b.y,b.w,b.h,4);ctx.fill();
  ctx.strokeStyle='#ffffff26';ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(b.x+5,b.y+1.5);ctx.lineTo(b.x+b.w-5,b.y+1.5);ctx.stroke();
  if(b.max===2&&b.hp===2){
   ctx.strokeStyle='#23334455';ctx.lineWidth=1;rounded(b.x+3,b.y+3,b.w-6,b.h-6,2);ctx.stroke();
   ctx.fillStyle='#24303c66';
   ctx.fillRect(b.x+6,b.y+b.h-7,2,2);ctx.fillRect(b.x+b.w-8,b.y+b.h-7,2,2);
  }
  if(b.hp===1&&b.max===2){
   const cx=b.x+b.w*.56;ctx.strokeStyle='#253144bb';ctx.lineWidth=1.5;
   ctx.beginPath();ctx.moveTo(cx-3,b.y);ctx.lineTo(cx+2,b.y+7);ctx.lineTo(cx-3,b.y+13);ctx.lineTo(cx+4,b.y+b.h);ctx.moveTo(cx-3,b.y+13);ctx.lineTo(cx-10,b.y+15);ctx.stroke();
  }
  if(b.power)powerIcon(b.x+b.w/2,b.y+b.h/2,b.power,'#23323c',W<500?.8:1);
  if(b.flash>0){ctx.fillStyle=`rgba(255,255,255,${b.flash*4})`;rounded(b.x,b.y,b.w,b.h,4);ctx.fill()}
 }
 for(const d of drops){
  const col=d.type==='wide'?'#b5f4ce':'#c1b4fa';
  ctx.save();ctx.shadowColor=col;ctx.shadowBlur=15;ctx.fillStyle='#1c2b32';ctx.strokeStyle=col;ctx.lineWidth=1;rounded(d.x-15,d.y-10,30,20,5);ctx.fill();ctx.stroke();ctx.restore();
  powerIcon(d.x,d.y,d.type,col);
 }
 for(const b of balls){
  b.trail.push({x:b.x,y:b.y});if(b.trail.length>13)b.trail.shift();
  for(let i=0;i<b.trail.length;i++){const t=b.trail[i];ctx.globalAlpha=(i/b.trail.length)*.22;ctx.fillStyle='#d6ffe6';ctx.beginPath();ctx.arc(t.x,t.y,b.r*i/b.trail.length,0,Math.PI*2);ctx.fill()}
  ctx.globalAlpha=1;ctx.shadowColor='#b5f4ce';ctx.shadowBlur=16;ctx.fillStyle='#f1fff5';ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;
 }
 ctx.shadowColor='#b5f4ce';ctx.shadowBlur=wide>0?24:13;
 ctx.fillStyle=wide>0?'#d6ffe5':'#b5f4ce';rounded(paddle.x-paddle.w/2,paddle.y,paddle.w,paddle.h,6);ctx.fill();ctx.shadowBlur=0;
 ctx.strokeStyle='#35574755';ctx.lineWidth=1;
 [-7,0,7].forEach(o=>{ctx.beginPath();ctx.moveTo(paddle.x+o,paddle.y+4);ctx.lineTo(paddle.x+o,paddle.y+8);ctx.stroke()});
 ctx.fillStyle='#b5f4ce0c';rounded(paddle.x-paddle.w/2+8,paddle.y+20,paddle.w-16,3,2);ctx.fill();
 if(serve>0){ctx.fillStyle='#b5f4ce';ctx.font='11px ui-monospace,monospace';ctx.textAlign='center';ctx.fillText('FIND YOUR NEXT ANGLE',W/2,H*.64)}
 for(const p of particles){ctx.globalAlpha=Math.min(1,p.life*2);ctx.fillStyle=p.color;ctx.fillRect(p.x,p.y,p.size,p.size)}
 ctx.globalAlpha=1;ctx.restore();
}
function start(){reset(false);canvas.focus({preventScroll:true})}
function togglePause(){
 if(demo){start();return}
 if(ended)return;
 paused=!paused;
 if(paused){$('mode').textContent='PAUSED';showOverlay('TAKE A BREATHER','Hold that thought.','Your next angle can wait.','Resume game')}
 else{$('mode').textContent='IN PLAY';$('overlay').classList.add('hidden')}
}
$('start').addEventListener('click',()=>{if(paused)togglePause();else start()});
$('restart').addEventListener('click',start);
$('pause').addEventListener('click',togglePause);
canvas.addEventListener('pointerdown',e=>{
 if(demo||ended)start();else if(paused)togglePause();
 const rect=canvas.getBoundingClientRect();target=e.clientX-rect.left;pointer=true;
 canvas.focus({preventScroll:true});canvas.setPointerCapture(e.pointerId);
});
canvas.addEventListener('pointermove',e=>{if(!demo&&!paused&&!ended){target=e.clientX-canvas.getBoundingClientRect().left;pointer=true}});
window.addEventListener('keydown',e=>{
 if(['ArrowLeft','ArrowRight',' ','ArrowUp','ArrowDown'].includes(e.key))e.preventDefault();
 if(e.repeat&&e.key!=='ArrowLeft'&&e.key!=='ArrowRight')return;
 if(demo||ended){if(!['Tab','Shift','Control','Alt','Meta','Escape'].includes(e.key)){start();if(e.key==='ArrowLeft')keys.left=true;if(e.key==='ArrowRight')keys.right=true}return}
 if(e.key==='ArrowLeft')keys.left=true;
 if(e.key==='ArrowRight')keys.right=true;
 if(e.code==='Space'||e.key==='Escape')togglePause();
 if(e.key.toLowerCase()==='r')start();
});
window.addEventListener('keyup',e=>{if(e.key==='ArrowLeft')keys.left=false;if(e.key==='ArrowRight')keys.right=false});
window.addEventListener('blur',()=>{keys.left=keys.right=false});
document.addEventListener('visibilitychange',()=>{last=0;accumulator=0});
new ResizeObserver(resize).observe(canvas.parentElement);
resize();reset(true);
function frame(t){
 if(!last)last=t;
 const dt=Math.min((t-last)/1000,.045);last=t;
 if(!paused){accumulator+=dt;while(accumulator>=STEP){update(STEP);accumulator-=STEP}}
 else accumulator=0;
 draw();updateUI();requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
Reasoning trace (1,372 tokens)
**Figuring out design dimensions**

I'm looking at how to set up a design canvas with logical coordinates that relate to pixel sizes in CSS. I want to create a responsive layout with 10 or 12 bricks across and 6 rows at the top, with each row having 22 elements. For mobile design, I think the board height should be 470, while the canvas width is 100%. I’ll also consider desktop dimensions, like keeping the height at 470 with a main width of 1080.**Exploring Canvas Design**

I need to create a glowing radial background for my screen canvas and render it transparently using CSS. The canvas renderer should account for device pixel ratio and dimensions to ensure crisp, colored brick drawings. I'm considering a palette of six rows with colors like lavender, violet, and mint, with dominant mint for visual appeal. The bricks need to feature a gradient, and I want to include shading for depth. Accessibility is essential, so I'll keep content concise.