hypeorcode
Network offOpen alone (new tab)

Run details

Model
opencode/ling-3.1-flash-free
Route
opencode CLI
Reasoning
provider default
Limits
60 min · 32,768 output tokens
Date
2 Oct 2026
Time
5 min 25 s
Size
25 kB
Tokens
8,449 in, 32,932 out
List price
$0.008 ($0.075 in / $0.225 out per M)
Paid
Not billed per run (CLI plan)

Raw files: output · meta.json

Exact prompt

Make a Breakout game I can play right now with the keyboard or the mouse. Give it several levels, a score and some juice.

It must be one complete, self-contained HTML file: inline all CSS and JavaScript, and make no network requests of any kind (no CDNs, web fonts or remote images). Save it as index.html in the current directory, then stop. Do not ask questions.

Source

25 kB 637 lines Raw

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>NEON BREAKER</title>
<style>
  * { margin:0; padding:0; box-sizing:border-box; }
  html,body { height:100%; background:#05060f; overflow:hidden; }
  body { display:flex; flex-direction:column; align-items:center; justify-content:center;
         font-family:ui-monospace,Menlo,Consolas,monospace; }
  canvas {
    display:block;
    width:min(96vw, calc(92vh * 4 / 3));
    aspect-ratio:4/3;
    background:#070a18;
    border-radius:10px;
    box-shadow:0 0 40px rgba(80,120,255,.25), 0 0 120px rgba(80,120,255,.12);
    touch-action:none;
  }
  #foot { color:#4a5578; font-size:12px; letter-spacing:1px; margin-top:10px; user-select:none; }
</style>
</head>
<body>
<canvas id="game"></canvas>
<div id="foot">MOVE: MOUSE or &#8592; &#8594; / A D &nbsp;&middot;&nbsp; LAUNCH: SPACE / CLICK &nbsp;&middot;&nbsp; P PAUSE &nbsp;&middot;&nbsp; M MUTE</div>
<script>
'use strict';
(() => {
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const W = 800, H = 600, TAU = Math.PI * 2;
const clamp = (v,a,b) => v<a?a:v>b?b:v;
const rand  = (a,b) => a + Math.random()*(b-a);

function fit(){ const d=Math.min(window.devicePixelRatio||1,2);
  canvas.width=W*d; canvas.height=H*d; ctx.setTransform(d,0,0,d,0,0); }
window.addEventListener('resize', fit); fit();

function rr(x,y,w,h,r){
  ctx.beginPath();
  if(ctx.roundRect){ ctx.roundRect(x,y,w,h,r); return; }
  ctx.moveTo(x+r,y);
  ctx.arcTo(x+w,y,x+w,y+h,r); ctx.arcTo(x+w,y+h,x,y+h,r);
  ctx.arcTo(x,y+h,x,y,r);     ctx.arcTo(x,y,x+w,y,r);
  ctx.closePath();
}

/* ---------------- audio (all synthesized, no files) ---------------- */
let actx=null, muted=false;
function ac(){ if(!actx){ try{ actx=new (window.AudioContext||window.webkitAudioContext)(); }catch(e){} }
  if(actx && actx.state==='suspended') actx.resume(); return actx; }
function tone(f,d,type,v,slide){
  if(muted) return; const a=ac(); if(!a) return;
  const t=a.currentTime, o=a.createOscillator(), g=a.createGain();
  o.type=type||'square'; o.frequency.setValueAtTime(f,t);
  if(slide) o.frequency.exponentialRampToValueAtTime(Math.max(30,slide), t+d);
  g.gain.setValueAtTime(v||.12,t); g.gain.exponentialRampToValueAtTime(.0001, t+d);
  o.connect(g); g.connect(a.destination); o.start(t); o.stop(t+d+.03);
}
function sfx(n){ switch(n){
  case 'paddle': tone(200,.08,'square',.10,320); break;
  case 'brick':  tone(480+Math.random()*120,.06,'square',.09,700); break;
  case 'hard':   tone(170,.07,'sawtooth',.11,110); break;
  case 'wall':   tone(150,.04,'triangle',.06); break;
  case 'bomb':   tone(140,.4,'sawtooth',.20,35); tone(70,.3,'square',.14,30); break;
  case 'power':  tone(520,.1,'square',.11,760); setTimeout(()=>tone(760,.12,'square',.11,1080),70); break;
  case 'life':   tone(660,.1,'sine',.12,990); setTimeout(()=>tone(990,.15,'sine',.12,1320),90); break;
  case 'lose':   tone(280,.45,'sawtooth',.15,60); break;
  case 'launch': tone(300,.09,'square',.10,520); break;
  case 'click':  tone(600,.05,'square',.07); break;
  case 'level':  [523,659,784,1047].forEach((f,i)=>setTimeout(()=>tone(f,.14,'square',.11), i*90)); break;
  case 'over':   [400,300,220,140].forEach((f,i)=>setTimeout(()=>tone(f,.2,'sawtooth',.12), i*140)); break;
  case 'win':    [523,659,784,1047,1319].forEach((f,i)=>setTimeout(()=>tone(f,.18,'square',.11), i*110)); break;
}}

/* ---------------- levels ---------------- */
const COLORS={a:'#ff5d73',b:'#ffa94d',c:'#ffe066',d:'#69db7c',e:'#4dabf7',f:'#9775fa',g:'#f783ac',X:'#c3cad6',B:'#ff8787'};
const LEVELS=[
 ["aaaaaaaaaaaa","bbbbbbbbbbbb","cccccccccccc","dddddddddddd","eeeeeeeeeeee"],
 ["XXXXXXXXXXXX","a.a.a.a.a.a.",".b.b.b.b.b.b","a.a.a.a.a.a.",".b.b.b.b.b.b","XXXXXXXXXXXX"],
 [".....BB.....","....aXXa....","...abccba...","..abccdccb..","...abccba...","....aXXa....",".....BB....."],
 ["..a......a..","...a....a...","..aaaaaaaa..",".aa.aaaa.aa.","aaaaaaaaaaaa","a.aaaaaaaa.a","a.a......a.a","....aa.aa..."],
 ["b.B.b.B.b.B.","XXXXXXXXXXXX","c.x.c.x.c.x.",".x.c.x.c.x.c","XXXXXXXXXXXX","d.B.d.B.d.B.","eeeeeeeeeeee"]
];
const BCOLS=12, GAP=6, BW=(W-80-(BCOLS-1)*GAP)/BCOLS, BH=26, BTOP=92;

/* ---------------- state ---------------- */
const G={ mode:'menu', score:0, best:0, lives:3, level:0, combo:0, broken:0,
          shake:0, freeze:0, levelT:0, clearT:0, flash:0, time:0, paused:false, lastBonus:0 };
try{ G.best=+localStorage.getItem('neonbreaker_best')||0; }catch(e){}
function saveBest(){ try{ localStorage.setItem('neonbreaker_best', G.best); }catch(e){} }

const paddle={ x:W/2, w:110, base:110, h:14, y:H-48, squash:0, expandT:0 };
let balls=[], bricks=[], parts=[], texts=[], powers=[], booms=[], stars=[];
let slowT=0;
for(let i=0;i<90;i++) stars.push({x:rand(0,W), y:rand(0,H), z:rand(.2,1), s:rand(.5,1.8)});

function targetSpeed(){ return Math.min(330 + G.level*28 + G.broken*2.2, 600) * (slowT>0 ? .62 : 1); }
function shake(a){ G.shake=Math.max(G.shake,a); }

function buildLevel(idx){
  bricks=[]; const map=LEVELS[idx];
  map.forEach((row,r)=>{
    for(let c=0;c<row.length;c++){
      const ch=row[c]; if(ch==='.') continue;
      const hard=ch==='X', bomb=ch==='B';
      bricks.push({ x:40+c*(BW+GAP), y:BTOP+r*(BH+GAP), w:BW, h:BH,
        hp:hard?2:1, maxHp:hard?2:1, type:bomb?'B':(hard?'X':'N'),
        color:COLORS[ch]||'#fff', flash:0, born:r*.06+c*.02, alive:true });
    }
  });
}
function startGame(){ G.score=0; G.lives=3; G.level=0; startLevel(); G.mode='serve'; }
function startLevel(){
  G.combo=0; G.broken=0; G.levelT=0;
  buildLevel(G.level);
  balls=[]; powers=[]; booms=[]; parts=[]; texts=[];
  slowT=0; paddle.expandT=0; paddle.w=paddle.base;
  resetServe();
}
function resetServe(){
  paddle.x=W/2;
  balls=[{ x:paddle.x, y:paddle.y-10, vx:0, vy:0, r:7, trail:[] }];
}
function launch(){
  if(G.mode!=='serve') return;
  G.mode='play';
  const sp=targetSpeed(), a=rand(-.35,.35);
  balls[0].vx=Math.sin(a)*sp; balls[0].vy=-Math.cos(a)*sp;
  sfx('launch');
}
function enterClear(){
  if(G.mode!=='play') return;
  G.mode='clear'; G.clearT=2.4; balls=[];
  G.lastBonus=250*(G.level+1); G.score+=G.lastBonus;
  if(G.score>G.best){ G.best=G.score; saveBest(); }
  sfx('level');
  for(let i=0;i<6;i++) burst(rand(120,W-120), rand(120,320), Object.values(COLORS)[i%7], 18, 300);
}
function nextLevel(){
  if(G.level>=LEVELS.length-1){ G.mode='win'; sfx('win'); saveBest(); return; }
  G.level++; startLevel(); G.mode='serve';
}
function gameOver(){ G.mode='over'; sfx('over'); saveBest(); }

/* ---------------- gameplay ---------------- */
function burst(x,y,color,n,spd){
  for(let i=0;i<n;i++){
    const a=rand(0,TAU), s=rand(spd*.3,spd);
    parts.push({ x,y, vx:Math.cos(a)*s, vy:Math.sin(a)*s, life:rand(.35,.8), max:.8, color, size:rand(2,4.5) });
  }
}
function addText(x,y,str,color){ texts.push({ x,y, str, color:color||'#fff', life:1 }); }

function hitBrick(br){
  br.flash=1;
  if(br.type==='X' && br.hp>1){
    br.hp--; G.score+=30;
    burst(br.x+br.w/2, br.y+br.h/2, '#c3cad6', 5, 120); sfx('hard');
    return;
  }
  destroyBrick(br);
}
function destroyBrick(br){
  if(!br.alive) return;
  br.alive=false; G.broken++; G.combo++;
  const mult=1+Math.floor(G.combo/5);
  const base=br.type==='B'?75:(br.maxHp>1?120:50);
  const pts=base*mult;
  G.score+=pts;
  if(G.score>G.best){ G.best=G.score; saveBest(); }
  addText(br.x+br.w/2, br.y+br.h/2, '+'+pts, mult>1?'#ffd43b':'#ffffff');
  burst(br.x+br.w/2, br.y+br.h/2, br.color, br.type==='B'?26:12, br.type==='B'?260:180);
  shake(br.type==='B'?9:2.2);
  if(br.type==='B'){
    booms.push({ x:br.x+br.w/2, y:br.y+br.h/2, r:100, t:0, delay:.14, done:false });
    sfx('bomb'); G.freeze=.05;
  } else sfx('brick');
  if(Math.random()<.14) spawnPower(br.x+br.w/2, br.y+br.h/2);
  if(bricks.every(b=>!b.alive)) enterClear();
}

const PTYPES=[{k:'E',c:'#69db7c',w:3},{k:'M',c:'#4dabf7',w:3},{k:'S',c:'#ffd43b',w:2.4},{k:'L',c:'#ff5d73',w:.8}];
function spawnPower(x,y){
  const tot=PTYPES.reduce((a,p)=>a+p.w,0); let r=Math.random()*tot;
  for(const p of PTYPES){ if((r-=p.w)<=0){ powers.push({ x,y, vy:150, k:p.k, c:p.c, t:0 }); return; } }
}
function applyPower(p){
  sfx(p.k==='L'?'life':'power');
  addText(p.x, paddle.y-22, {E:'EXPAND!',M:'MULTIBALL!',S:'SLOW-MO!',L:'+1 LIFE!'}[p.k], p.c);
  burst(p.x, p.y, p.c, 14, 200);
  if(p.k==='E') paddle.expandT=12;
  else if(p.k==='M'){
    const src=balls.slice();
    for(const b of src){
      if(balls.length>=8) break;
      const sp=Math.hypot(b.vx,b.vy)||targetSpeed(), a=Math.atan2(b.vy,b.vx)||(-Math.PI/2);
      for(const da of [-.45,.45]){
        if(balls.length>=8) break;
        balls.push({ x:b.x, y:b.y, vx:Math.cos(a+da)*sp, vy:Math.sin(a+da)*sp, r:7, trail:[] });
      }
    }
  }
  else if(p.k==='S') slowT=9;
  else if(p.k==='L') G.lives=Math.min(G.lives+1, 6);
}

function updatePaddle(dt){
  let dir=0;
  if(keys['arrowleft']||keys['a']) dir-=1;
  if(keys['arrowright']||keys['d']) dir+=1;
  if(dir!==0) paddle.x+=dir*560*dt;
  paddle.x=clamp(paddle.x, paddle.w/2+6, W-paddle.w/2-6);
}
function updateBalls(dt){
  const sp=targetSpeed();
  for(let i=balls.length-1;i>=0;i--){
    const b=balls[i];
    const cur=Math.hypot(b.vx,b.vy)||1; b.vx=b.vx/cur*sp; b.vy=b.vy/cur*sp;
    const steps=Math.max(1, Math.ceil(sp*dt/5)), sdt=dt/steps;
    for(let s=0;s<steps;s++){
      b.x+=b.vx*sdt; b.y+=b.vy*sdt;
      if(b.x<b.r+4){ b.x=b.r+4; b.vx=Math.abs(b.vx); sfx('wall'); }
      if(b.x>W-b.r-4){ b.x=W-b.r-4; b.vx=-Math.abs(b.vx); sfx('wall'); }
      if(b.y<b.r+44){ b.y=b.r+44; b.vy=Math.abs(b.vy); sfx('wall'); }
      if(b.vy>0 && b.y+b.r>=paddle.y && b.y-b.r<=paddle.y+paddle.h &&
         b.x>=paddle.x-paddle.w/2-b.r && b.x<=paddle.x+paddle.w/2+b.r){
        const rel=clamp((b.x-paddle.x)/(paddle.w/2), -1, 1);
        const ang=rel*1.15, nsp=Math.hypot(b.vx,b.vy);
        b.vx=Math.sin(ang)*nsp; b.vy=-Math.abs(Math.cos(ang)*nsp);
        b.y=paddle.y-b.r-.5;
        if(G.combo>=5) addText(paddle.x, paddle.y-26, 'COMBO x'+(1+Math.floor(G.combo/5))+'!', '#ffd43b');
        G.combo=0; paddle.squash=1;
        burst(b.x, paddle.y, '#8ec5ff', 6, 140); sfx('paddle');
      }
      for(const br of bricks){
        if(!br.alive) continue;
        const nx=clamp(b.x,br.x,br.x+br.w), ny=clamp(b.y,br.y,br.y+br.h);
        const dx=b.x-nx, dy=b.y-ny;
        if(dx*dx+dy*dy<=b.r*b.r){
          hitBrick(br);
          if(Math.abs(dx)>Math.abs(dy)){ b.vx=dx>0?Math.abs(b.vx):-Math.abs(b.vx); b.x=nx+Math.sign(dx)*(b.r+.1); }
          else{
            if(dx===0&&dy===0) b.vy=-b.vy;
            else { b.vy=dy>0?Math.abs(b.vy):-Math.abs(b.vy); b.y=ny+Math.sign(dy)*(b.r+.1); }
          }
          break;
        }
      }
    }
    b.trail.push({x:b.x,y:b.y}); if(b.trail.length>10) b.trail.shift();
    if(b.y>H+30) balls.splice(i,1);
  }
  if(balls.length===0 && G.mode==='play'){
    G.lives--; G.combo=0; G.flash=1; shake(10); sfx('lose');
    if(G.lives<=0) gameOver(); else { G.mode='serve'; resetServe(); }
  }
}
function updatePowers(dt){
  for(let i=powers.length-1;i>=0;i--){
    const p=powers[i]; p.t+=dt; p.y+=p.vy*dt; p.x+=Math.sin(p.t*4)*18*dt;
    if(p.y>H+20){ powers.splice(i,1); continue; }
    if(p.y+10>=paddle.y && p.y-10<=paddle.y+paddle.h && Math.abs(p.x-paddle.x)<=paddle.w/2+10){
      applyPower(p); powers.splice(i,1);
    }
  }
}
function updateBooms(dt){
  for(const ex of booms){
    ex.t+=dt;
    if(!ex.done && ex.t>=ex.delay){
      ex.done=true;
      for(const br of bricks){
        if(!br.alive) continue;
        const cx=br.x+br.w/2, cy=br.y+br.h/2;
        if((cx-ex.x)*(cx-ex.x)+(cy-ex.y)*(cy-ex.y)<=ex.r*ex.r){
          if(br.type==='B'){
            br.alive=false; G.broken++; G.combo++; G.score+=75;
            booms.push({ x:cx, y:cy, r:100, t:0, delay:.16, done:false });
            burst(cx,cy,'#ff8787',20,240); addText(cx,cy,'+75','#fff');
          }
          else if(br.type==='X' && br.hp>1){ br.hp--; br.flash=1; burst(cx,cy,'#c3cad6',6,140); }
          else destroyBrick(br);
        }
      }
      burst(ex.x, ex.y, '#ffd43b', 30, 340); burst(ex.x, ex.y, '#ff8787', 20, 260);
      shake(13); G.freeze=Math.max(G.freeze,.04);
      if(bricks.every(b=>!b.alive)) enterClear();
    }
  }
  booms=booms.filter(e=>e.t<e.delay+.45);
}
function updateParts(dt){
  for(let i=parts.length-1;i>=0;i--){
    const p=parts[i]; p.life-=dt;
    if(p.life<=0){ parts.splice(i,1); continue; }
    p.x+=p.vx*dt; p.y+=p.vy*dt; p.vy+=500*dt; p.vx*=(1-2*dt);
  }
}
function updateTexts(dt){
  for(let i=texts.length-1;i>=0;i--){
    const t=texts[i]; t.life-=dt*.8; t.y-=40*dt;
    if(t.life<=0) texts.splice(i,1);
  }
}
function update(dt){
  G.time+=dt;
  for(const s of stars){ s.y+=s.z*14*dt; if(s.y>H){ s.y-=H; s.x=rand(0,W); } }
  updateParts(dt); updateTexts(dt);
  if(G.freeze>0){ G.freeze-=dt; return; }
  if(G.mode==='play' || G.mode==='serve'){
    G.levelT+=dt;
    updatePaddle(dt);
    if(G.mode==='serve'){
      const b=balls[0]; b.x=paddle.x; b.y=paddle.y-b.r-3; b.trail.length=0;
    } else {
      slowT=Math.max(0, slowT-dt);
      if(paddle.expandT>0) paddle.expandT-=dt;
      const tw=paddle.expandT>0 ? paddle.base*1.55 : paddle.base;
      paddle.w+=(tw-paddle.w)*Math.min(1,dt*10);
      if(paddle.expandT<=0) paddle.w=paddle.base;
      updateBalls(dt); updatePowers(dt); updateBooms(dt);
    }
    for(const br of bricks) if(br.flash>0) br.flash-=dt*4;
  } else if(G.mode==='clear'){
    G.clearT-=dt; updateBooms(dt); updatePowers(dt);
    if(G.clearT<=0) nextLevel();
  }
  G.shake=Math.max(0, G.shake-dt*36);
  G.flash=Math.max(0, G.flash-dt*2.4);
  paddle.squash=Math.max(0, paddle.squash-dt*5);
}

/* ---------------- render ---------------- */
function drawBricks(){
  for(const br of bricks){
    if(!br.alive) continue;
    const ap=clamp((G.levelT-br.born)/.3, 0, 1);
    if(ap<=0) continue;
    ctx.save();
    ctx.globalAlpha=ap;
    const cx=br.x+br.w/2, cy=br.y+br.h/2;
    ctx.translate(cx,cy); ctx.scale(1+(1-ap)*.6, 1+(1-ap)*.6); ctx.translate(-cx,-cy);
    ctx.shadowColor=br.color; ctx.shadowBlur=10+br.flash*18;
    ctx.fillStyle=br.color; rr(br.x,br.y,br.w,br.h,5); ctx.fill();
    ctx.shadowBlur=0;
    ctx.fillStyle='rgba(255,255,255,.28)'; rr(br.x+2,br.y+2,br.w-4,br.h*.38,4); ctx.fill();
    if(br.type==='B'){
      ctx.fillStyle='#2b0a12';
      ctx.beginPath(); ctx.arc(cx,cy+2,7,0,TAU); ctx.fill();
      ctx.strokeStyle='#2b0a12'; ctx.lineWidth=2;
      ctx.beginPath(); ctx.moveTo(cx+4,cy-4); ctx.quadraticCurveTo(cx+9,cy-12,cx+13,cy-9); ctx.stroke();
      ctx.fillStyle='#ffe066';
      ctx.beginPath(); ctx.arc(cx+13,cy-9,2.5+Math.sin(G.time*20)*1,0,TAU); ctx.fill();
    }
    if(br.type==='X' && br.hp<br.maxHp){
      ctx.strokeStyle='rgba(40,40,60,.7)'; ctx.lineWidth=1.5;
      ctx.beginPath();
      ctx.moveTo(br.x+br.w*.2,br.y+2); ctx.lineTo(br.x+br.w*.45,br.y+br.h*.6); ctx.lineTo(br.x+br.w*.3,br.y+br.h-2);
      ctx.moveTo(br.x+br.w*.75,br.y+3); ctx.lineTo(br.x+br.w*.6,br.y+br.h*.55);
      ctx.stroke();
    }
    if(br.flash>0){ ctx.fillStyle='rgba(255,255,255,'+(br.flash*.7)+')'; rr(br.x,br.y,br.w,br.h,5); ctx.fill(); }
    ctx.restore();
  }
}
function drawPaddle(){
  const p=paddle, sq=p.squash;
  const w=p.w*(1+sq*.35), h=p.h*(1-sq*.35);
  const x=p.x-w/2, y=p.y+(p.h-h)/2;
  ctx.save();
  ctx.shadowColor='#4dabf7'; ctx.shadowBlur=16;
  const g=ctx.createLinearGradient(x,y,x,y+h);
  g.addColorStop(0,'#9fd0ff'); g.addColorStop(.5,'#4dabf7'); g.addColorStop(1,'#1c6dd9');
  ctx.fillStyle=g; rr(x,y,w,h,h/2); ctx.fill();
  ctx.shadowBlur=0;
  ctx.fillStyle='rgba(255,255,255,.5)'; rr(x+6,y+2,w-12,h*.3,h*.15); ctx.fill();
  ctx.restore();
}
function drawBalls(){
  for(const b of balls){
    for(let i=0;i<b.trail.length;i++){
      const t=b.trail[i], a=(i+1)/b.trail.length;
      ctx.globalAlpha=a*.25; ctx.fillStyle='#8ec5ff';
      ctx.beginPath(); ctx.arc(t.x,t.y,b.r*a,0,TAU); ctx.fill();
    }
    ctx.globalAlpha=1;
    ctx.save();
    ctx.shadowColor='#aee2ff'; ctx.shadowBlur=18;
    const g=ctx.createRadialGradient(b.x-2,b.y-2,1,b.x,b.y,b.r);
    g.addColorStop(0,'#ffffff'); g.addColorStop(1,'#74c0fc');
    ctx.fillStyle=g;
    ctx.beginPath(); ctx.arc(b.x,b.y,b.r,0,TAU); ctx.fill();
    ctx.restore();
  }
}
function drawPowers(){
  for(const p of powers){
    ctx.save();
    ctx.shadowColor=p.c; ctx.shadowBlur=14;
    ctx.fillStyle=p.c; rr(p.x-16,p.y-10,32,20,10); ctx.fill();
    ctx.shadowBlur=0;
    ctx.fillStyle='rgba(0,0,0,.55)';
    ctx.font='bold 13px ui-monospace,monospace';
    ctx.textAlign='center'; ctx.textBaseline='middle';
    ctx.fillText(p.k, p.x, p.y+1);
    ctx.restore();
  }
}
function drawBooms(){
  for(const ex of booms){
    const k=clamp(ex.t/(ex.delay+.4), 0, 1);
    ctx.save();
    ctx.globalAlpha=(1-k)*.9;
    ctx.strokeStyle='#ffd43b'; ctx.lineWidth=4*(1-k)+1;
    ctx.beginPath(); ctx.arc(ex.x,ex.y,ex.r*k,0,TAU); ctx.stroke();
    ctx.strokeStyle='#ff8787'; ctx.lineWidth=2;
    ctx.beginPath(); ctx.arc(ex.x,ex.y,ex.r*k*.7,0,TAU); ctx.stroke();
    ctx.restore();
  }
}
function drawParts(){
  ctx.save();
  ctx.globalCompositeOperation='lighter';
  for(const p of parts){
    ctx.globalAlpha=clamp(p.life/p.max,0,1);
    ctx.fillStyle=p.color;
    ctx.fillRect(p.x-p.size/2, p.y-p.size/2, p.size, p.size);
  }
  ctx.restore();
}
function drawTexts(){
  ctx.save();
  ctx.textAlign='center'; ctx.textBaseline='middle';
  for(const t of texts){
    ctx.globalAlpha=clamp(t.life,0,1);
    ctx.font='bold 16px ui-monospace,monospace';
    ctx.fillStyle=t.color; ctx.shadowColor=t.color; ctx.shadowBlur=8;
    ctx.fillText(t.str, t.x, t.y);
  }
  ctx.restore();
}
function badge(x,y,label,k,c){
  ctx.fillStyle='rgba(255,255,255,.08)'; rr(x,y-8,58,16,8); ctx.fill();
  ctx.fillStyle=c; rr(x,y-8,58*clamp(k,0,1),16,8); ctx.fill();
  ctx.fillStyle='#0b1030'; ctx.font='bold 10px ui-monospace,monospace';
  ctx.textAlign='center'; ctx.textBaseline='middle';
  ctx.fillText(label, x+29, y);
}
function drawHUD(){
  ctx.save();
  ctx.fillStyle='rgba(255,255,255,.06)'; ctx.fillRect(0,0,W,40);
  ctx.strokeStyle='rgba(140,170,255,.25)';
  ctx.beginPath(); ctx.moveTo(0,40.5); ctx.lineTo(W,40.5); ctx.stroke();
  ctx.textBaseline='middle'; ctx.font='bold 18px ui-monospace,monospace';
  ctx.textAlign='left';  ctx.fillStyle='#8ec5ff';
  ctx.fillText('SCORE '+String(G.score).padStart(6,'0'), 16, 21);
  ctx.textAlign='center'; ctx.fillStyle='#9d6bff';
  ctx.fillText('LEVEL '+(G.level+1)+' / '+LEVELS.length, W/2, 21);
  ctx.textAlign='right'; ctx.fillStyle='#ff8787';
  ctx.fillText('BEST '+G.best, W-16, 21);
  for(let i=0;i<G.lives;i++){
    ctx.fillStyle='#ff5d73'; ctx.shadowColor='#ff5d73'; ctx.shadowBlur=8;
    ctx.beginPath(); ctx.arc(W-24-i*20, 50, 5, 0, TAU); ctx.fill();
  }
  ctx.shadowBlur=0;
  let bx=16;
  if(paddle.expandT>0){ badge(bx,60,'WIDE',paddle.expandT/12,'#69db7c'); bx+=70; }
  if(slowT>0){ badge(bx,60,'SLOW',slowT/9,'#ffd43b'); bx+=70; }
  if(G.combo>=5){
    ctx.fillStyle='#ffd43b'; ctx.font='bold 14px ui-monospace,monospace'; ctx.textAlign='left';
    ctx.fillText('COMBO x'+(1+Math.floor(G.combo/5)), bx, 60);
  }
  if(muted){ ctx.fillStyle='#666'; ctx.font='12px ui-monospace,monospace'; ctx.textAlign='right'; ctx.fillText('MUTED', W-16, 60); }
  ctx.restore();
}
function drawCenter(big, small, color){
  ctx.save();
  ctx.fillStyle='rgba(5,6,15,.72)'; ctx.fillRect(0,0,W,H);
  ctx.textAlign='center'; ctx.textBaseline='middle';
  ctx.font='bold 44px ui-monospace,monospace';
  ctx.fillStyle=color; ctx.shadowColor=color; ctx.shadowBlur=24;
  ctx.fillText(big, W/2, H*.42);
  ctx.shadowBlur=0;
  ctx.font='18px ui-monospace,monospace'; ctx.fillStyle='#cdd7ff';
  ctx.fillText(small, W/2, H*.42+48);
  ctx.restore();
}
function drawServe(){
  ctx.save();
  ctx.textAlign='center'; ctx.textBaseline='middle';
  ctx.globalAlpha=.6+.4*Math.sin(G.time*5);
  ctx.font='bold 20px ui-monospace,monospace'; ctx.fillStyle='#fff';
  ctx.fillText(G.levelT<2 ? 'LEVEL '+(G.level+1) : 'SPACE / CLICK TO LAUNCH', W/2, H*.62);
  ctx.restore();
}
function drawMenu(){
  ctx.save();
  ctx.fillStyle='rgba(5,6,15,.68)'; ctx.fillRect(0,0,W,H);
  ctx.textAlign='center'; ctx.textBaseline='middle';
  ctx.font='bold 56px ui-monospace,monospace';
  const g=ctx.createLinearGradient(W/2-220,0,W/2+220,0);
  g.addColorStop(0,'#ff5d73'); g.addColorStop(.5,'#ffd43b'); g.addColorStop(1,'#4dabf7');
  ctx.fillStyle=g; ctx.shadowColor='#4dabf7'; ctx.shadowBlur=30;
  ctx.fillText('NEON BREAKER', W/2, H*.28);
  ctx.shadowBlur=0;
  ctx.font='16px ui-monospace,monospace'; ctx.fillStyle='#cdd7ff';
  ctx.fillText('SMASH BRICKS · CHAIN COMBOS · CATCH POWER-UPS', W/2, H*.28+52);
  ctx.fillStyle='#8ec5ff';
  ctx.fillText('MOVE   ·  MOUSE  or  ← → / A D', W/2, H*.46);
  ctx.fillText('LAUNCH ·  SPACE  or  CLICK', W/2, H*.46+30);
  ctx.fillText('P PAUSE    ·    M MUTE', W/2, H*.46+60);
  ctx.globalAlpha=.6+.4*Math.sin(G.time*5);
  ctx.font='bold 22px ui-monospace,monospace'; ctx.fillStyle='#69db7c';
  ctx.fillText('PRESS SPACE OR CLICK TO START', W/2, H*.68);
  ctx.globalAlpha=1;
  if(G.best>0){ ctx.font='15px ui-monospace,monospace'; ctx.fillStyle='#ffd43b'; ctx.fillText('BEST '+G.best, W/2, H*.68+40); }
  ctx.restore();
}
function drawOver(){
  ctx.save();
  ctx.fillStyle='rgba(20,5,10,.78)'; ctx.fillRect(0,0,W,H);
  ctx.textAlign='center'; ctx.textBaseline='middle';
  ctx.font='bold 52px ui-monospace,monospace';
  ctx.fillStyle='#ff5d73'; ctx.shadowColor='#ff5d73'; ctx.shadowBlur=26;
  ctx.fillText('GAME OVER', W/2, H*.34);
  ctx.shadowBlur=0;
  ctx.font='22px ui-monospace,monospace'; ctx.fillStyle='#fff';
  ctx.fillText('SCORE  '+G.score, W/2, H*.34+62);
  ctx.font='16px ui-monospace,monospace'; ctx.fillStyle='#ffd43b';
  ctx.fillText(G.score>=G.best ? 'NEW BEST!' : 'BEST '+G.best, W/2, H*.34+94);
  ctx.globalAlpha=.6+.4*Math.sin(G.time*5);
  ctx.font='bold 20px ui-monospace,monospace'; ctx.fillStyle='#8ec5ff';
  ctx.fillText('SPACE / CLICK TO PLAY AGAIN', W/2, H*.34+142);
  ctx.restore();
}
function drawWin(){
  ctx.save();
  ctx.fillStyle='rgba(5,15,10,.78)'; ctx.fillRect(0,0,W,H);
  ctx.textAlign='center'; ctx.textBaseline='middle';
  ctx.font='bold 52px ui-monospace,monospace';
  ctx.fillStyle='#69db7c'; ctx.shadowColor='#69db7c'; ctx.shadowBlur=26;
  ctx.fillText('YOU WIN!', W/2, H*.34);
  ctx.shadowBlur=0;
  ctx.font='22px ui-monospace,monospace'; ctx.fillStyle='#fff';
  ctx.fillText('FINAL SCORE  '+G.score, W/2, H*.34+62);
  ctx.font='16px ui-monospace,monospace'; ctx.fillStyle='#ffd43b';
  ctx.fillText(G.score>=G.best ? 'NEW BEST!' : 'BEST '+G.best, W/2, H*.34+94);
  ctx.globalAlpha=.6+.4*Math.sin(G.time*5);
  ctx.font='bold 20px ui-monospace,monospace'; ctx.fillStyle='#8ec5ff';
  ctx.fillText('SPACE / CLICK FOR MENU', W/2, H*.34+142);
  ctx.restore();
}
function render(){
  const g=ctx.createLinearGradient(0,0,0,H);
  g.addColorStop(0,'#0b1030'); g.addColorStop(.6,'#070a18'); g.addColorStop(1,'#05060f');
  ctx.fillStyle=g; ctx.fillRect(0,0,W,H);
  ctx.save();
  for(const s of stars){ ctx.globalAlpha=.25+.5*s.z; ctx.fillStyle='#9db4ff'; ctx.fillRect(s.x,s.y,s.s,s.s); }
  ctx.restore();

  ctx.save();
  if(G.shake>0) ctx.translate(rand(-G.shake,G.shake)*.5, rand(-G.shake,G.shake)*.5);
  drawBricks(); drawPowers(); drawPaddle(); drawBalls(); drawBooms(); drawParts(); drawTexts();
  ctx.restore();

  drawHUD();
  if(G.mode==='menu') drawMenu();
  else if(G.mode==='clear') drawCenter('LEVEL '+(G.level+1)+' CLEAR!', 'BONUS +'+G.lastBonus, '#69db7c');
  else if(G.mode==='over') drawOver();
  else if(G.mode==='win') drawWin();
  else if(G.paused) drawCenter('PAUSED', 'PRESS P TO RESUME', '#8ec5ff');
  else if(G.mode==='serve') drawServe();
  if(G.flash>0){ ctx.fillStyle='rgba(255,60,80,'+(G.flash*.28)+')'; ctx.fillRect(0,0,W,H); }
  const v=ctx.createRadialGradient(W/2,H/2,H*.45,W/2,H/2,H*.95);
  v.addColorStop(0,'rgba(0,0,0,0)'); v.addColorStop(1,'rgba(0,0,0,.45)');
  ctx.fillStyle=v; ctx.fillRect(0,0,W,H);
}

/* ---------------- input ---------------- */
const keys={};
function primaryAction(){
  if(G.mode==='menu'){ sfx('click'); startGame(); }
  else if(G.mode==='serve') launch();
  else if(G.mode==='clear') nextLevel();
  else if(G.mode==='over'){ sfx('click'); startGame(); }
  else if(G.mode==='win'){ G.mode='menu'; }
}
window.addEventListener('keydown', e=>{
  const k=e.key.toLowerCase();
  if(k===' '||k==='arrowleft'||k==='arrowright'||k==='arrowup'||k==='arrowdown') e.preventDefault();
  keys[k]=true;
  ac();
  if(k===' ') primaryAction();
  else if(k==='p'||k==='escape'){ if(G.mode==='play'||G.mode==='serve') G.paused=!G.paused; }
  else if(k==='m') muted=!muted;
  else if((k==='arrowleft'||k==='arrowright'||k==='a'||k==='d') && G.mode==='serve') launch();
});
window.addEventListener('keyup', e=>{ keys[e.key.toLowerCase()]=false; });
window.addEventListener('blur', ()=>{ if(G.mode==='play'||G.mode==='serve') G.paused=true; });

function canvasX(e){
  const r=canvas.getBoundingClientRect();
  const cx=(e.touches && e.touches[0]) ? e.touches[0].clientX : e.clientX;
  return (cx-r.left)/r.width*W;
}
canvas.addEventListener('mousemove', e=>{
  if(G.paused) return;
  paddle.x=clamp(canvasX(e), paddle.w/2+6, W-paddle.w/2-6);
});
canvas.addEventListener('mousedown', ()=>{ ac(); primaryAction(); });
canvas.addEventListener('touchstart', e=>{
  e.preventDefault(); ac();
  paddle.x=clamp(canvasX(e), paddle.w/2+6, W-paddle.w/2-6);
  primaryAction();
}, {passive:false});
canvas.addEventListener('touchmove', e=>{
  e.preventDefault();
  paddle.x=clamp(canvasX(e), paddle.w/2+6, W-paddle.w/2-6);
}, {passive:false});

/* ---------------- loop ---------------- */
let last=performance.now();
function loop(t){
  requestAnimationFrame(loop);
  let dt=(t-last)/1000; last=t;
  if(dt>1/20) dt=1/20;
  if(!G.paused) update(dt);
  render();
  canvas.style.cursor=(G.mode==='play'||G.mode==='serve')&&!G.paused ? 'none' : 'default';
}
requestAnimationFrame(loop);
})();
</script>
</body>
</html>

Compare with

Ling 3.1 Flash: other attempts

  • Ling 3.1 Flash, Watch: Queued · #6 of 15 paid · not before 23 Oct
  • Ling 3.1 Flash, Landing: No file after 2 runs, used all 32,768 output tokens
  • About · 147 words