• Updated 1 Nov, 11:50 UTC
  • Next run 2 Nov, 05:17 UTCNext 2 Nov 05:17
  • Last 7 days: 4 new models · 99 runs7 d: 4 models · 99 runs
Playable Breakout

Fugu MaxSakana AI

Network offOpen alone (new tab)

Run details

Model
sakana/fugu-max
Route
OpenRouter API, one user message
Served by
Sakana AI
Effort
high, OpenRouter's default for this model
Limits
60 min · 128,000 output tokens (the model's limit)
Date
1 Nov 2026
Time
9 min 15 s
Size
25 kB
Tokens
182 in, 53,740 out (43,822 of them thinking)
List price
$0.32 at today's price ($2.00 in / $6.00 out per M)
Paid
$0.32

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). Reply with the file in a single ```html code block.

Source

25 kB 540 lines Raw

<!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>
  html,body{height:100%}
  body{margin:0;background:#06080c;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;
       font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;-webkit-user-select:none;user-select:none}
  canvas{display:block;width:min(96vw,calc(92vh * 4 / 3));aspect-ratio:4/3;border:1px solid #1b2330;border-radius:10px;
         box-shadow:0 30px 80px rgba(0,0,0,.55);cursor:crosshair;touch-action:none}
  .hint{font-size:11px;letter-spacing:.14em;color:#4a5266;text-align:center;padding:0 12px}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div class="hint">MOUSE / &#8592; &#8594; / A D — MOVE&nbsp;&nbsp;&middot;&nbsp;&nbsp;SPACE / CLICK — LAUNCH&nbsp;&nbsp;&middot;&nbsp;&nbsp;P — PAUSE&nbsp;&nbsp;&middot;&nbsp;&nbsp;M — SOUND</div>
<script>
'use strict';
/* ================= constants & helpers ================= */
const W=960,H=720,HUD=56,TAU=Math.PI*2;
const FIELD={left:16,right:944,top:HUD+12};
const COLS=12,GAP=6,BW=(FIELD.right-FIELD.left-(COLS-1)*GAP)/COLS,BH=26,BRICK_Y0=FIELD.top+16;
const FONT='ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace';
const clamp=(v,a,b)=>v<a?a:v>b?b:v;
const rand=(a,b)=>a+Math.random()*(b-a);
const easeOutBack=t=>{const c=1.70158;t-=1;return 1+t*t*((c+1)*t+c)};
const pad=v=>String(Math.max(0,Math.floor(v))).padStart(6,'0');
const sp=s=>s.split('').join(' ');
function hexA(h,a){const n=parseInt(h.slice(1),16);return`rgba(${n>>16&255},${n>>8&255},${n&255},${a})`}
function rr(x,y,w,h,r){ctx.beginPath();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();}

const canvas=document.getElementById('c');
const ctx=canvas.getContext('2d');
const DPR=Math.min(2,window.devicePixelRatio||1);
canvas.width=W*DPR;canvas.height=H*DPR;

/* ================= audio (WebAudio, no assets) ================= */
let actx=null,muted=false;
function audio(){if(!actx){try{actx=new(window.AudioContext||window.webkitAudioContext)()}catch(e){}}
  if(actx&&actx.state==='suspended')actx.resume();return actx;}
function toneAt(f,dur,when=0,type='square',vol=0.1,slide=0){
  if(muted)return;const a=audio();if(!a)return;
  const t=a.currentTime+when,o=a.createOscillator(),g=a.createGain();
  o.type=type;o.frequency.setValueAtTime(f,t);
  if(slide)o.frequency.exponentialRampToValueAtTime(Math.max(40,f+slide),t+dur);
  g.gain.setValueAtTime(vol,t);g.gain.exponentialRampToValueAtTime(0.0001,t+dur);
  o.connect(g);g.connect(a.destination);o.start(t);o.stop(t+dur+0.05);}
const tone=(f,dur,type,vol,slide)=>toneAt(f,dur,0,type,vol,slide);
const sfx={
  paddle:r=>tone(170+Math.abs(r)*150,0.07,'square',0.12,80),
  wall:()=>tone(140,0.04,'triangle',0.06),
  hit:()=>tone(330,0.05,'square',0.09,-60),
  clank:()=>{tone(95,0.09,'square',0.1,-30);tone(190,0.05,'triangle',0.05);},
  brk:c=>{const f=340*Math.pow(1.0594,Math.min(c,18));tone(f,0.08,'square',0.1,f*0.5);toneAt(f*1.5,0.1,0.02,'triangle',0.06);},
  launch:()=>tone(280,0.09,'square',0.1,260),
  power:()=>{[440,587,880].forEach((f,i)=>toneAt(f,0.09,i*0.07,'square',0.09));},
  lose:()=>{toneAt(320,0.22,0,'sawtooth',0.12,-160);toneAt(160,0.35,0.1,'sawtooth',0.11,-80);},
  clear:()=>{[523,659,784,1047].forEach((f,i)=>toneAt(f,0.15,i*0.08,'square',0.09));},
  win:()=>{[659,784,988,1319,1568].forEach((f,i)=>toneAt(f,0.2,i*0.09,'square',0.09));}
};

/* ================= levels ================= */
/* '1','2','3' = brick hit-points, 'X' = indestructible, '.' = empty */
const LEVELS=[
 {name:'FIRST CONTACT',pal:['#e8590c','#ffa94d','#ffec99'],accent:'#ff922b',rows:[
  '111111111111','111111111111','222222222222','111111111111']},
 {name:'CHECKERBOARD',pal:['#0ca678','#38d9a9','#c3fae8'],accent:'#20c997',rows:[
  '2.2.2.2.2.2.','.1.1.1.1.1.1','2.2.2.2.2.2.','.1.1.1.1.1.1','2.2.2.2.2.2.']},
 {name:'THE PYRAMID',pal:['#6741d9','#9775fa','#e5dbff'],accent:'#845ef7',rows:[
  '.....33.....','....3223....','...321123...','..22111122..','.1111..1111.']},
 {name:'THE VAULT',pal:['#d6336c','#f06595','#ffdeeb'],accent:'#f06595',rows:[
  '.22......22.','.22.X33X.22.','.21.X33X.12.','.21.X33X.12.','.22......22.']},
 {name:'THE FORTRESS',pal:['#cc8500','#ffd43b','#fff3bf'],accent:'#fcc419',rows:[
  '.2222222222.','.2111111112.','.21.3333.12.','.21.3333.12.','.2111111112.','.2222222222.']},
 {name:'THE INVADER',pal:['#2b8a3e','#69db7c','#d3f9d8'],accent:'#51cf66',rows:[
  '..2......2..','...2....2...','..22111122..','.2211331122.','221111111122',
  '2.11111111.2','2.11....11.2','...11..11...']}
];
const CAP={W:{color:'#ffd43b',label:'W'},M:{color:'#3bc9db',label:'M'},
           S:{color:'#b197fc',label:'S'},L:{color:'#69db7c',label:'+'}};

/* ================= state ================= */
let hi=0;try{hi=+localStorage.getItem('nb_hi')||0}catch(e){}
function saveHi(){try{localStorage.setItem('nb_hi',String(G.hi))}catch(e){}}
const G={state:'title',level:0,score:0,dispScore:0,hi,lives:3,combo:0,comboPop:0,
  timeScale:1,shake:0,flash:0,freeze:0,ballSlow:0,stateT:0,levelT:0,paused:false,
  wf:{l:0,r:0,t:0},bonus:0,newBest:false,destr:0};
const P={x:W/2,y:H-54,w:110,baseW:110,h:16,squash:0,wideT:0};
let PAL=LEVELS[0].pal,ACCENT=LEVELS[0].accent;
let bricks=[],capsules=[],balls=[],parts=[],pops=[];
const dust=[];
let T=0,keyL=false,keyR=false,useMouse=false,mouseX=W/2;

const bgCv=document.createElement('canvas');
function makeBg(L){
  bgCv.width=W*DPR;bgCv.height=H*DPR;
  const x=bgCv.getContext('2d');x.scale(DPR,DPR);
  x.fillStyle='#0a0d13';x.fillRect(0,0,W,H);
  x.fillStyle=hexA(L.accent,0.045);x.fillRect(0,0,W,H);
  x.strokeStyle='rgba(255,255,255,0.035)';x.lineWidth=1;x.beginPath();
  for(let gx=48;gx<W;gx+=48){x.moveTo(gx+.5,HUD);x.lineTo(gx+.5,H);}
  for(let gy=72;gy<H;gy+=48){x.moveTo(0,gy+.5);x.lineTo(W,gy+.5);}
  x.stroke();
  const v=x.createRadialGradient(W/2,H*0.42,H*0.3,W/2,H*0.42,W*0.72);
  v.addColorStop(0,'rgba(0,0,0,0)');v.addColorStop(1,'rgba(0,0,0,0.42)');
  x.fillStyle=v;x.fillRect(0,0,W,H);
}

/* ================= game flow ================= */
const baseSpeed=()=>340+G.level*28;
function newBall(){return{x:P.x,y:P.y-P.h/2-9,vx:0,vy:0,r:7,stuck:true,speed:baseSpeed(),trail:[],dead:false};}
function loadLevel(i){
  G.level=i;const L=LEVELS[i];PAL=L.pal;ACCENT=L.accent;
  bricks=[];capsules=[];parts=[];pops=[];
  L.rows.forEach((row,r)=>{
    for(let c=0;c<COLS;c++){
      const ch=row[c];if(!ch||ch==='.')continue;
      const X=ch==='X',hp=X?Infinity:+ch;
      bricks.push({x:FIELD.left+c*(BW+GAP),y:BRICK_Y0+r*(BH+GAP),w:BW,h:BH,hp,
        maxHp:X?Infinity:hp,X,dead:false,flash:0,delay:0.05+r*0.06+c*0.025});
    }});
  G.destr=bricks.filter(k=>!k.X).length;
  G.levelT=0;G.stateT=0;G.state='intro';G.combo=0;G.comboPop=0;G.ballSlow=0;
  P.wideT=0;P.w=P.baseW;P.x=W/2;P.squash=0;
  balls=[newBall()];
  makeBg(L);
}
function startGame(){G.score=0;G.dispScore=0;G.lives=3;G.newBest=false;loadLevel(0);}
function launch(){
  const b=balls.find(b=>b.stuck);if(!b)return;
  b.stuck=false;const a=rand(-0.35,0.35);
  b.vx=Math.sin(a)*b.speed;b.vy=-Math.cos(a)*b.speed;
  G.state='play';G.stateT=0;sfx.launch();
}
function levelCleared(){
  G.state='clear';G.stateT=0;
  G.bonus=500*(G.level+1);G.score+=G.bonus;
  G.timeScale=0.18;G.flash=0.5;addShake(10);sfx.clear();
}
function advanceLevel(){
  G.timeScale=1;const n=G.level+1;
  if(n>=LEVELS.length){G.state='won';G.stateT=0;endGame();sfx.win();}
  else loadLevel(n);
}
function endGame(){
  if(G.score>G.hi){G.hi=G.score;G.newBest=true;saveHi();}else G.newBest=false;
}
function loseLife(lost){
  G.lives--;G.combo=0;G.state='dead';G.stateT=0;
  G.freeze=0.32;addShake(13);G.flash=0.3;
  burst(clamp(lost.x,FIELD.left,FIELD.right),H-24,'#ff6b6b',20,330);
  sfx.lose();
}
function addShake(v){G.shake=Math.min(18,G.shake+v);}
function pop(x,y,text,color){pops.push({x,y,text,color,t:0,life:0.9});}

/* ================= fx ================= */
function burst(x,y,color,n,power){
  for(let i=0;i<n;i++){const a=rand(0,TAU),s=rand(0.25,1)*power;
    parts.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s-120,rot:rand(0,TAU),vr:rand(-8,8),
      s:rand(3,7),color:Math.random()<0.22?'#ffffff':color,t:0,life:rand(0.45,0.9)});}
  if(parts.length>350)parts.splice(0,parts.length-350);
}

/* ================= physics ================= */
function stepBall(b,st){
  b.x+=b.vx*st;b.y+=b.vy*st;
  if(b.x-b.r<FIELD.left){b.x=FIELD.left+b.r;b.vx=Math.abs(b.vx);G.wf.l=1;sfx.wall();}
  else if(b.x+b.r>FIELD.right){b.x=FIELD.right-b.r;b.vx=-Math.abs(b.vx);G.wf.r=1;sfx.wall();}
  if(b.y-b.r<FIELD.top){b.y=FIELD.top+b.r;b.vy=Math.abs(b.vy);G.wf.t=1;sfx.wall();}
  // paddle
  if(b.vy>0&&b.y-b.r<P.y+P.h&&b.y+b.r>P.y-P.h/2){
    const half=P.w/2;
    if(Math.abs(b.x-P.x)<half+b.r*0.8){
      const rel=clamp((b.x-P.x)/half,-1,1),ang=rel*Math.PI/3;
      b.speed=Math.min(b.speed+7,baseSpeed()*1.65);
      b.vx=Math.sin(ang)*b.speed;b.vy=-Math.cos(ang)*b.speed;
      b.y=P.y-P.h/2-b.r-0.1;
      G.combo=0;P.squash=1;addShake(2.5);
      burst(b.x,P.y-P.h/2,'#ffffff',5,170);
      sfx.paddle(rel);
    }
  }
  brickCollide(b);
  if(b.y-b.r>H+24)b.dead=true;
}
function brickCollide(b){
  for(const k of bricks){
    if(k.dead)continue;
    if(b.x+b.r<k.x||b.x-b.r>k.x+k.w||b.y+b.r<k.y||b.y-b.r>k.y+k.h)continue;
    const cx=clamp(b.x,k.x,k.x+k.w),cy=clamp(b.y,k.y,k.y+k.h);
    const dx=b.x-cx,dy=b.y-cy,d2=dx*dx+dy*dy;
    if(d2>b.r*b.r)continue;
    let nx,ny;
    if(d2>1e-4){const d=Math.sqrt(d2);nx=dx/d;ny=dy/d;b.x=cx+nx*(b.r+0.2);b.y=cy+ny*(b.r+0.2);}
    else{ny=b.vy>0?-1:1;nx=0;b.y=ny<0?k.y-b.r-0.2:k.y+k.h+b.r+0.2;}
    const dot=b.vx*nx+b.vy*ny;
    if(dot<0){b.vx-=2*dot*nx;b.vy-=2*dot*ny;}
    const minVy=0.18*b.speed;                 // never let the ball go near-horizontal
    if(Math.abs(b.vy)<minVy){
      const sy=b.vy===0?(Math.random()<0.5?-1:1):Math.sign(b.vy);
      b.vy=sy*minVy;
      const nvx=Math.sqrt(Math.max(0,b.speed*b.speed-b.vy*b.vy));
      b.vx=(b.vx<0?-1:1)*nvx;
    }
    hitBrick(k,b);return;
  }
}
function hitBrick(k){
  if(k.X){k.flash=1;addShake(2);sfx.clank();return;}
  k.hp--;
  if(k.hp<=0){
    k.dead=true;G.destr--;
    G.combo++;const mult=Math.min(G.combo,9);
    const base=k.maxHp===1?50:k.maxHp===2?80:120;
    const pts=Math.round(base*(1+(mult-1)*0.5)/5)*5;
    G.score+=pts;
    pop(k.x+k.w/2,k.y+k.h/2,'+'+pts,'#ffffff');
    burst(k.x+k.w/2,k.y+k.h/2,PAL[0],14,270);
    addShake(4);G.freeze=Math.max(G.freeze,0.028);
    if(G.combo>=3)G.comboPop=1;
    sfx.brk(G.combo);
    maybeDrop(k);
    if(G.destr<=0)levelCleared();
  }else{k.flash=1;addShake(2);sfx.hit();}
}
function updateBalls(dt){
  const eff=dt*(G.ballSlow>0?0.55:1);
  for(const b of balls){
    if(b.stuck)continue;
    const sp=Math.hypot(b.vx,b.vy);
    const steps=Math.max(1,Math.ceil(sp*eff/6)),st=eff/steps;
    for(let i=0;i<steps&&!b.dead;i++)stepBall(b,st);
    if(!b.dead){b.trail.push({x:b.x,y:b.y});if(b.trail.length>11)b.trail.shift();}
  }
  let lost=null;
  balls=balls.filter(b=>{if(b.dead){if(!lost)lost=b;return false;}return true;});
  if(lost&&G.state==='play')loseLife(lost);
}

/* ================= power-ups ================= */
function maybeDrop(k){
  if(capsules.length>=3||Math.random()>0.11)return;
  const r=Math.random();
  const type=r<0.38?'W':r<0.63?'M':r<0.85?'S':'L';
  capsules.push({x:k.x+k.w/2,y:k.y+k.h/2,type,t:rand(0,6),vy:170,dead:false});
}
function apply(c){
  const C=CAP[c.type];sfx.power();burst(c.x,c.y,C.color,10,200);
  if(c.type==='W'){P.wideT=12;pop(P.x,P.y-30,'WIDE PADDLE',C.color);}
  else if(c.type==='M'){
    const src=balls.find(b=>!b.stuck);
    if(src)for(const da of[-0.5,0.5]){
      if(balls.length>=6)break;
      const s=Math.hypot(src.vx,src.vy)||src.speed,a=Math.atan2(src.vy,src.vx)+da;
      balls.push({x:src.x,y:src.y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,r:7,stuck:false,speed:src.speed,trail:[],dead:false});
    }
    pop(P.x,P.y-30,'MULTIBALL',C.color);
  }
  else if(c.type==='S'){G.ballSlow=6;pop(P.x,P.y-30,'SLOW BALL',C.color);}
  else{G.lives=Math.min(5,G.lives+1);pop(P.x,P.y-30,'+1 LIFE',C.color);toneAt(990,0.15,0.12,'square',0.1);}
}
function updateCapsules(dt){
  for(const c of capsules){
    c.t+=dt;c.y+=c.vy*dt;c.x+=Math.sin(c.t*3)*16*dt;
    if((G.state==='play'||G.state==='ready')&&c.y+9>P.y-P.h/2&&c.y-9<P.y+P.h/2&&Math.abs(c.x-P.x)<P.w/2+16){
      apply(c);c.dead=true;
    }else if(c.y-12>H)c.dead=true;
  }
  capsules=capsules.filter(c=>!c.dead);
}

/* ================= update ================= */
function update(rdt,dt){
  T+=rdt;
  if(G.paused)return;
  G.stateT+=rdt;G.levelT+=rdt;
  G.shake*=Math.exp(-7*rdt);if(G.shake<0.02)G.shake=0;
  G.flash=Math.max(0,G.flash-rdt*2.2);
  G.comboPop=Math.max(0,G.comboPop-rdt*4);
  G.wf.l=Math.max(0,G.wf.l-rdt*5);G.wf.r=Math.max(0,G.wf.r-rdt*5);G.wf.t=Math.max(0,G.wf.t-rdt*5);
  if(G.timeScale<1)G.timeScale=Math.min(1,G.timeScale+rdt*1.5);
  if(G.ballSlow>0)G.ballSlow-=rdt;
  G.dispScore+=(G.score-G.dispScore)*Math.min(1,rdt*12);
  if(G.score-G.dispScore<1)G.dispScore=G.score;

  // paddle
  if(useMouse)P.x+=(mouseX-P.x)*Math.min(1,rdt*14);
  else P.x+=((keyR?1:0)-(keyL?1:0))*640*rdt;
  P.squash=Math.max(0,P.squash-rdt*6);
  if(P.wideT>0)P.wideT=Math.max(0,P.wideT-rdt);
  P.w+=(P.baseW*(P.wideT>0?1.55:1)-P.w)*Math.min(1,rdt*9);
  P.x=clamp(P.x,FIELD.left+P.w/2,FIELD.right-P.w/2);
  for(const b of balls)if(b.stuck){b.x=P.x;b.y=P.y-P.h/2-b.r-2;}

  for(const k of bricks)if(k.flash>0)k.flash=Math.max(0,k.flash-rdt*5);

  if(G.state==='intro'&&G.stateT>1.3){G.state='ready';G.stateT=0;}
  if(G.state==='intro'||G.state==='ready'||G.state==='play'||(G.state==='clear'&&G.stateT<0.7))updateBalls(dt);
  if(G.state==='play'||G.state==='ready'||G.state==='clear'||G.state==='dead')updateCapsules(dt);
  if(G.state==='dead'&&G.stateT>1.05){
    if(G.lives>0){G.state='ready';G.stateT=0;balls=[newBall()];}
    else{G.state='over';G.stateT=0;endGame();}
  }
  if(G.state==='clear'&&G.stateT>2.7)advanceLevel();

  for(const q of parts){q.t+=dt;q.x+=q.vx*dt;q.y+=q.vy*dt;q.vy+=1000*dt;q.rot+=q.vr*dt;}
  parts=parts.filter(q=>q.t<q.life);
  for(const p of pops)p.t+=dt;
  pops=pops.filter(p=>p.t<p.life);
  for(const d of dust){d.y+=d.v*rdt;if(d.y>H){d.y=-2;d.x=rand(0,W);}}
}

/* ================= render ================= */
function txt(s,x,y,size,color,align='center',weight=700,alpha=1){
  ctx.globalAlpha=alpha;ctx.fillStyle=color;
  ctx.font=weight+' '+size+'px '+FONT;
  ctx.textAlign=align;ctx.textBaseline='middle';
  ctx.fillText(s,x,y);ctx.globalAlpha=1;
}
function dim(a){ctx.fillStyle=`rgba(6,8,12,${a})`;ctx.fillRect(0,0,W,H);}

function drawWalls(){
  ctx.fillStyle=hexA(ACCENT,0.28);
  ctx.fillRect(FIELD.left-5,FIELD.top-5,FIELD.right-FIELD.left+10,5);
  ctx.fillRect(FIELD.left-5,FIELD.top-5,5,H-FIELD.top+5);
  ctx.fillRect(FIELD.right,FIELD.top-5,5,H-FIELD.top+5);
  if(G.wf.t>0){ctx.fillStyle=hexA('#ffffff',G.wf.t*0.5);ctx.fillRect(FIELD.left-5,FIELD.top-5,FIELD.right-FIELD.left+10,5);}
  if(G.wf.l>0){ctx.fillStyle=hexA(ACCENT,0.3+G.wf.l*0.6);ctx.fillRect(FIELD.left-5,FIELD.top-5,5,H-FIELD.top+5);}
  if(G.wf.r>0){ctx.fillStyle=hexA(ACCENT,0.3+G.wf.r*0.6);ctx.fillRect(FIELD.right,FIELD.top-5,5,H-FIELD.top+5);}
  ctx.strokeStyle='rgba(255,107,107,0.25)';ctx.lineWidth=2;ctx.setLineDash([8,10]);
  ctx.beginPath();ctx.moveTo(FIELD.left,H-6);ctx.lineTo(FIELD.right,H-6);ctx.stroke();ctx.setLineDash([]);
}
function drawBricks(){
  for(const k of bricks){
    if(k.dead)continue;
    const t=clamp((G.levelT-k.delay)/0.35,0,1);if(t<=0)continue;
    const sc=easeOutBack(t);
    ctx.save();ctx.translate(k.x+k.w/2,k.y+k.h/2);ctx.scale(sc,sc);
    ctx.fillStyle=k.X?'#3f4854':PAL[clamp(k.hp,1,3)-1];
    rr(-k.w/2,-k.h/2,k.w,k.h,5);ctx.fill();
    ctx.fillStyle='rgba(255,255,255,0.2)';rr(-k.w/2+3,-k.h/2+3,k.w-6,3,1.5);ctx.fill();
    ctx.fillStyle='rgba(0,0,0,0.25)';ctx.fillRect(-k.w/2+4,k.h/2-5,k.w-8,3);
    if(k.X){ctx.strokeStyle='rgba(15,19,26,0.8)';ctx.lineWidth=2;ctx.beginPath();
      for(let o=-1;o<=1;o++){ctx.moveTo(o*14-6,k.h/2-5);ctx.lineTo(o*14+6,-k.h/2+5);}ctx.stroke();}
    else if(k.hp<k.maxHp){
      ctx.strokeStyle='rgba(10,13,19,0.5)';ctx.lineWidth=1.5;ctx.beginPath();
      ctx.moveTo(-k.w*0.18,-k.h/2+3);ctx.lineTo(-k.w*0.02,0);ctx.lineTo(-k.w*0.24,k.h/2-3);ctx.stroke();
      if(k.hp<k.maxHp-1){ctx.beginPath();ctx.moveTo(k.w*0.2,-k.h/2+4);ctx.lineTo(k.w*0.05,2);ctx.stroke();}
    }
    if(k.flash>0){ctx.fillStyle=hexA('#ffffff',k.flash*0.85);rr(-k.w/2,-k.h/2,k.w,k.h,5);ctx.fill();}
    ctx.restore();
  }
}
function drawCapsules(){
  for(const c of capsules){
    const C=CAP[c.type];
    ctx.save();ctx.translate(c.x,c.y);ctx.rotate(Math.sin(c.t*3)*0.15);
    ctx.shadowColor=C.color;ctx.shadowBlur=12;
    ctx.fillStyle=C.color;rr(-16,-9,32,18,6);ctx.fill();
    ctx.shadowBlur=0;
    ctx.fillStyle='#0a0d13';ctx.font='700 11px '+FONT;
    ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText(C.label,0,0.5);
    ctx.restore();
  }
}
function drawParticles(){
  for(const q of parts){
    ctx.save();ctx.globalAlpha=1-q.t/q.life;ctx.translate(q.x,q.y);ctx.rotate(q.rot);
    ctx.fillStyle=q.color;ctx.fillRect(-q.s/2,-q.s/2,q.s,q.s*0.75);ctx.restore();
  }
}
function drawBalls(){
  for(const b of balls){
    const n=b.trail.length;
    for(let i=0;i<n;i++){const p=b.trail[i],f=(i+1)/n;
      ctx.fillStyle=`rgba(255,255,255,${(f*0.16).toFixed(3)})`;
      ctx.beginPath();ctx.arc(p.x,p.y,b.r*f*0.85,0,TAU);ctx.fill();}
    ctx.save();
    ctx.shadowColor=G.ballSlow>0?'#b197fc':ACCENT;ctx.shadowBlur=15;
    ctx.fillStyle=G.ballSlow>0?'#d6c7ff':'#f5f7fb';
    ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,TAU);ctx.fill();ctx.restore();
    if(b.stuck&&G.state==='ready'){
      ctx.strokeStyle=`rgba(255,255,255,${0.18+0.14*Math.sin(T*5)})`;ctx.lineWidth=1.5;
      ctx.beginPath();ctx.arc(b.x,b.y,b.r+5+2*Math.sin(T*5),0,TAU);ctx.stroke();
    }
  }
}
function drawPaddle(){
  ctx.save();ctx.translate(P.x,P.y);
  const s=P.squash;ctx.scale(1+s*0.22,1-s*0.3);
  ctx.shadowColor=ACCENT;ctx.shadowBlur=13;
  ctx.fillStyle='#eef1f6';rr(-P.w/2,-P.h/2,P.w,P.h,8);ctx.fill();
  ctx.shadowBlur=0;
  let a=0.9;
  if(P.wideT>0&&P.wideT<2)a=0.35+0.55*Math.abs(Math.sin(T*8));
  ctx.fillStyle=hexA(ACCENT,a);
  rr(-P.w/2+9,P.h/2-6,P.w-18,3.5,2);ctx.fill();
  ctx.restore();
}
function drawCombo(){
  if(!(G.state==='play'||G.state==='clear')||G.combo<2)return;
  const s=1+G.comboPop*0.4;
  ctx.save();ctx.translate(FIELD.right-14,FIELD.top+26);ctx.scale(s,s);
  txt('COMBO ×'+Math.min(G.combo,9),0,0,15,ACCENT,'right');ctx.restore();
}
function drawHUD(){
  ctx.fillStyle='#141a26';ctx.fillRect(0,HUD-1,W,1);
  txt(sp('SCORE'),26,25,10,'#5c667b','left');
  txt(pad(G.dispScore),26,45,20,'#e7ecf3','left');
  txt(sp('BEST'),220,25,10,'#5c667b','left');
  txt(pad(G.hi),220,45,16,'#8b93a7','left');
  txt(sp('LEVEL '+(G.level+1)+' / '+LEVELS.length),W/2,25,10,'#5c667b');
  txt(LEVELS[G.level].name,W/2,45,15,ACCENT);
  txt(sp('LIVES'),W-26,25,10,'#5c667b','right');
  for(let i=0;i<G.lives;i++){const x=W-26-(i+1)*20-i*6;ctx.fillStyle='#dfe5ee';rr(x,40,20,7,3);ctx.fill();}
  let iy=FIELD.top+26;
  if(P.wideT>0){txt('WIDE',FIELD.left+14,iy,11,hexA('#ffd43b',0.85),'left');iy+=20;}
  if(G.ballSlow>0){txt('SLOW',FIELD.left+14,iy,11,hexA('#b197fc',0.85),'left');}
  if(muted)txt('SOUND OFF',W-16,H-14,10,'rgba(255,107,107,0.5)','right');
}
function drawOverlays(){
  const st=G.state,t=G.stateT;
  if(G.paused){dim(0.6);txt('PAUSED',W/2,H/2-16,34,'#e7ecf3');
    txt(sp('P TO RESUME'),W/2,H/2+26,12,'#8b93a7');return;}
  if(st==='title'){
    dim(0.74);
    ctx.font='700 56px '+FONT;ctx.textBaseline='middle';ctx.textAlign='left';
    const w1=ctx.measureText('NEON').width,w2=ctx.measureText('BREAKER').width,gap=20;
    const x0=W/2-(w1+gap+w2)/2;
    ctx.fillStyle=ACCENT;ctx.fillText('NEON',x0,212);
    ctx.fillStyle='#e7ecf3';ctx.fillText('BREAKER',x0+w1+gap,212);
    ctx.fillStyle=hexA(ACCENT,0.5);ctx.fillRect(W/2-130,246,260,3);
    txt(sp('SIX LEVELS · ONE BALL · ZERO MERCY'),W/2,286,12,'#7d879c');
    txt('MOUSE · ARROWS · A D      MOVE',W/2,338,12,'#98a2b8');
    txt('SPACE · CLICK      LAUNCH',W/2,362,12,'#98a2b8');
    txt('P      PAUSE            M      SOUND',W/2,386,12,'#98a2b8');
    if(G.hi>0)txt('BEST  '+pad(G.hi),W/2,432,14,'#ffd43b');
    txt('CLICK OR PRESS SPACE TO PLAY',W/2,496,15,ACCENT,'center',700,0.45+0.45*Math.sin(T*4.5));
    return;}
  if(st==='intro'){
    const a=t<0.12?t/0.12:t>0.95?Math.max(0,(1.3-t)/0.35):1;
    const s=easeOutBack(Math.min(1,t/0.3));
    ctx.save();ctx.translate(W/2,392);ctx.scale(s,s);ctx.globalAlpha=a;
    txt('LEVEL '+(G.level+1),0,-24,32,'#e7ecf3');
    txt(LEVELS[G.level].name,0,18,17,ACCENT);ctx.restore();return;}
  if(st==='clear'&&t>0.45){
    const a=Math.min(1,(t-0.45)/0.3),s=easeOutBack(Math.min(1,(t-0.45)/0.35));
    ctx.save();ctx.translate(W/2,H/2-30);ctx.scale(s,s);ctx.globalAlpha=a;
    txt('LEVEL CLEAR',0,-16,36,ACCENT);
    txt('+'+G.bonus+' BONUS',0,28,16,'#e7ecf3');ctx.restore();return;}
  if(st==='dead'){txt('BALL LOST',W/2,H/2-40,22,'#ff8787','center',700,Math.max(0,1-t*1.5));return;}
  if(st==='ready'){txt('CLICK OR PRESS SPACE TO LAUNCH',W/2,H-96,12,'#9aa4b8','center',700,0.45+0.4*Math.sin(T*5));return;}
  if(st==='over'){
    dim(0.78);const a=Math.min(1,t*1.5);
    txt('GAME OVER',W/2,268,46,'#ff6b6b','center',700,a);
    txt('SCORE  '+pad(G.score),W/2,328,20,'#e7ecf3','center',700,a);
    if(G.newBest)txt('NEW BEST!',W/2,366,15,'#ffd43b','center',700,a*(0.6+0.4*Math.sin(T*6)));
    else txt('BEST  '+pad(G.hi),W/2,366,13,'#8b93a7','center',700,a);
    txt('REACHED LEVEL '+(G.level+1),W/2,398,12,'#6d7688','center',700,a);
    if(t>0.8)txt('CLICK OR PRESS SPACE TO RETRY',W/2,468,14,ACCENT,'center',700,0.45+0.45*Math.sin(T*4.5));
    return;}
  if(st==='won'){
    dim(0.8);const a=Math.min(1,t*1.5);
    txt('ALL LEVELS CLEARED',W/2,252,38,'#ffd43b','center',700,a);
    txt('FINAL SCORE  '+pad(G.score),W/2,318,20,'#e7ecf3','center',700,a);
    if(G.newBest)txt('NEW BEST!',W/2,356,15,'#ffd43b','center',700,a*(0.6+0.4*Math.sin(T*6)));
    else txt('BEST  '+pad(G.hi),W/2,356,13,'#8b93a7','center',700,a);
    if(t>0.8)txt('CLICK OR PRESS SPACE TO PLAY AGAIN',W/2,440,14,ACCENT,'center',700,0.45+0.45*Math.sin(T*4.5));
  }
}
function render(){
  ctx.setTransform(DPR,0,0,DPR,0,0);
  ctx.drawImage(bgCv,0,0,W,H);
  ctx.fillStyle='#aeb8cf';
  for(const d of dust){ctx.globalAlpha=d.a;ctx.fillRect(d.x,d.y,d.r,d.r);}
  ctx.globalAlpha=1;
  ctx.save();
  if(G.shake>0.2)ctx.translate(rand(-G.shake,G.shake),rand(-G.shake,G.shake));
  drawWalls();drawBricks();drawCapsules();drawParticles();drawBalls();drawPaddle();
  for(const p of pops)txt(p.text,p.x,p.y-p.t*46,13,p.color,'center',700,1-p.t/p.life);
  drawCombo();
  ctx.restore();
  drawHUD();
  if(G.flash>0){ctx.fillStyle=`rgba(255,255,255,${G.flash.toFixed(3)})`;ctx.fillRect(0,0,W,H);}
  drawOverlays();
}

/* ================= input ================= */
function action(){
  if(G.paused){G.paused=false;return;}
  switch(G.state){
    case 'title':startGame();break;
    case 'ready':launch();break;
    case 'clear':if(G.stateT>1.0)advanceLevel();break;
    case 'over':case 'won':startGame();break;
  }
}
function setMouse(t){const r=canvas.getBoundingClientRect();mouseX=(t.clientX-r.left)/r.width*W;useMouse=true;}
canvas.addEventListener('mousemove',e=>setMouse(e));
canvas.addEventListener('mousedown',e=>{e.preventDefault();audio();action();});
canvas.addEventListener('contextmenu',e=>e.preventDefault());
canvas.addEventListener('touchstart',e=>{e.preventDefault();audio();setMouse(e.touches[0]);if(G.state!=='play')action();},{passive:false});
canvas.addEventListener('touchmove',e=>{e.preventDefault();setMouse(e.touches[0]);},{passive:false});
window.addEventListener('keydown',e=>{
  if(['ArrowLeft','ArrowRight','ArrowUp','ArrowDown',' '].includes(e.key))e.preventDefault();
  audio();
  if(e.key==='ArrowLeft'||e.key==='a'||e.key==='A'){keyL=true;useMouse=false;}
  if(e.key==='ArrowRight'||e.key==='d'||e.key==='D'){keyR=true;useMouse=false;}
  if((e.key===' '||e.key==='Enter')&&!e.repeat)action();
  if(e.key==='p'||e.key==='P'){if(G.state==='play'||G.state==='ready')G.paused=!G.paused;}
  if(e.key==='m'||e.key==='M')muted=!muted;
});
window.addEventListener('keyup',e=>{
  if(e.key==='ArrowLeft'||e.key==='a'||e.key==='A')keyL=false;
  if(e.key==='ArrowRight'||e.key==='d'||e.key==='D')keyR=false;
});
window.addEventListener('blur',()=>{if(G.state==='play'&&!G.paused)G.paused=true;});

/* ================= boot ================= */
for(let i=0;i<42;i++)dust.push({x:rand(0,W),y:rand(0,H),v:rand(8,26),a:rand(0.04,0.12),r:rand(1,2.2)});
loadLevel(0);G.levelT=10;G.state='title';
let curCursor='';
function setCursor(c){if(c!==curCursor){curCursor=c;canvas.style.cursor=c;}}
let last=performance.now();
requestAnimationFrame(function frame(now){
  const rdt=Math.min(0.033,(now-last)/1000)||0;last=now;
  let dt=rdt*G.timeScale;
  if(G.freeze>0){G.freeze-=rdt;dt=0;}   // micro hit-stop
  update(rdt,dt);
  render();
  setCursor(G.state==='play'&&!G.paused?'none':'crosshair');
  requestAnimationFrame(frame);
});
</script>
</body>
</html>

Compare with

Fugu Max: other attempts