Models 44

Muse Ember

Open in new window

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

How it went

Total time
30.5 s
Cost
$0.0084
Output tokens
16,717
Spent thinking
8,304 tokens
Speed
587.9 tok/s
Console errors
0
First token after
2.0 s
First visible text after
19.9 s
Prompt tokens
375
Code size
22.7 KB
Finish reason
stop
Served by
Meta
Model id
meta/muse-ember
Reasoning sent
{"effort":"xhigh"}
Max output
943,718 tokens
Generated
Nov 12, 2026

What it was asked

The prompt
“i want a breakout game where the paddle is a hamburger and every brick you break explodes into tons of little hearts”
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 · 483 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hamburger Breakout — Heart Burst</title>
<style>
  *{box-sizing:border-box}
  html,body{margin:0;padding:0;overflow-x:hidden}
  body{
    font-family:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    background:linear-gradient(160deg,#fff3d6 0%,#ffe3ec 55%,#ffd6a5 100%);
    color:#4a2a12; min-height:100vh;
  }
  .wrap{max-width:980px;margin:0 auto;padding:10px 14px 14px}
  header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:6px 2px 10px}
  .logo{display:flex;align-items:center;gap:12px}
  .logoIcon{
    width:56px;height:56px;border-radius:16px;background:#fff;border:3px solid #ffb703;
    display:flex;align-items:center;justify-content:center;font-size:34px;box-shadow:0 6px 0 #e09e00;
    animation:floaty 2.4s ease-in-out infinite;
  }
  @keyframes floaty{0%,100%{transform:translateY(0) rotate(-3deg)}50%{transform:translateY(-6px) rotate(3deg)}}
  h1{margin:0;font-size:32px;line-height:1;letter-spacing:.5px;font-weight:900;color:#5a2d0c}
  h1 span{color:#ff2e63}
  .tag{margin:4px 0 0;font-size:14px;font-weight:700;color:#8a5a2a}
  .stats{display:flex;gap:8px;flex-wrap:wrap}
  .stat{background:#fff;border-radius:14px;padding:7px 12px;min-width:92px;text-align:center;box-shadow:0 4px 0 rgba(0,0,0,.08);border:2px solid #ffe8c2}
  .stat b{display:block;font-size:20px;line-height:1;color:#ff2e63}
  .stat small{font-size:10px;letter-spacing:1.2px;font-weight:800;color:#9c6b3a;text-transform:uppercase}
  .stage{position:relative;border-radius:20px;overflow:hidden;box-shadow:0 18px 40px rgba(90,40,0,.28),0 0 0 6px #fff,0 0 0 9px #ffb703;background:#fff7e6}
  canvas{display:block;width:100%;height:auto;cursor:crosshair;touch-action:none}
  #overlay{
    position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
    background:rgba(255,248,235,.38);z-index:5;padding:14px;
  }
  .card{
    background:rgba(255,255,255,.96);border:3px solid #ffb703;border-radius:22px;
    padding:18px 22px 16px;text-align:center;max-width:430px;width:100%;
    box-shadow:0 16px 30px rgba(0,0,0,.18);animation:pop .35s ease;
  }
  @keyframes pop{from{transform:scale(.92);opacity:0}to{transform:scale(1);opacity:1}}
  .burger{font-size:56px;line-height:1;animation:floaty 2s ease-in-out infinite}
  .card h2{margin:8px 0 4px;font-size:28px;letter-spacing:.5px;color:#5a2d0c}
  .card h2 span{color:#ff2e63}
  .card p{margin:4px 0;font-weight:700;color:#8a5a2a;font-size:15px}
  .startText{
    margin:14px auto 8px;background:#ff2e63;color:#fff;font-weight:900;letter-spacing:.6px;
    padding:12px 16px;border-radius:999px;font-size:16px;box-shadow:0 5px 0 #c9184a;
    animation:pulse 1.2s ease-in-out infinite;
  }
  @keyframes pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.05)}}
  .hint{font-size:13px !important;color:#a07a4a !important}
  #toast{
    position:absolute;top:14px;left:50%;transform:translateX(-50%) translateY(-8px);
    background:#5a2d0c;color:#fff;font-weight:900;padding:9px 16px;border-radius:999px;
    font-size:15px;opacity:0;pointer-events:none;transition:.25s;z-index:6;white-space:nowrap;
    box-shadow:0 8px 20px rgba(0,0,0,.25);
  }
  #toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
  .bar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:10px 2px 0}
  .controlsHint{font-size:13.5px;font-weight:700;color:#7a4a22}
  .controlsHint b{background:#fff;padding:2px 7px;border-radius:7px;border:1.5px solid #ffd8a8;box-shadow:0 2px 0 #ffd8a8}
  .btns{display:flex;gap:8px;flex-wrap:wrap}
  button{
    border:2px solid #ffb703;background:#fff;color:#5a2d0c;font-weight:900;font-size:13px;
    padding:8px 14px;border-radius:999px;cursor:pointer;box-shadow:0 4px 0 #e09e00;letter-spacing:.4px;
  }
  button:active{transform:translateY(3px);box-shadow:0 1px 0 #e09e00}
  button.primary{background:#ff2e63;border-color:#c9184a;color:#fff;box-shadow:0 4px 0 #c9184a}
  @media(max-width:640px){
    h1{font-size:24px}.tag{font-size:12px}.logoIcon{width:44px;height:44px;font-size:26px;border-radius:12px}
    .stat{min-width:74px;padding:6px 8px}.stat b{font-size:17px}
    .card{padding:14px}.card h2{font-size:22px}.burger{font-size:44px}.startText{font-size:14px}
    .controlsHint{font-size:12px}
  }
</style>
</head>
<body>
<div class="wrap">
  <header>
    <div class="logo">
      <div class="logoIcon">🍔</div>
      <div>
        <h1>HAMBURGER <span>BREAKOUT</span></h1>
        <p class="tag">Every brick explodes into a burst of hearts ♥</p>
      </div>
    </div>
    <div class="stats">
      <div class="stat"><b id="scoreVal">0</b><small>Score</small></div>
      <div class="stat"><b id="livesVal" style="color:#ff2e63">♥♥♥</b><small>Lives</small></div>
      <div class="stat"><b id="levelVal" style="color:#ff8c42">1</b><small>Level</small></div>
      <div class="stat"><b id="heartsVal" style="color:#ff7096">0</b><small>Hearts</small></div>
      <div class="stat"><b id="bricksVal" style="color:#6ac84f">60</b><small>Bricks</small></div>
    </div>
  </header>

  <div class="stage" id="stage">
    <canvas id="game" width="960" height="560"></canvas>
    <div id="toast"></div>
    <div id="overlay">
      <div class="card">
        <div class="burger">🍔</div>
        <h2>HAMBURGER <span>BREAKOUT</span></h2>
        <p id="ovSub">Smash bricks with your burger paddle — they burst into tons of little hearts!</p>
        <div class="startText">► Click or press a key to start</div>
        <p class="hint">Move: Mouse / ← → / A D / touch &nbsp;•&nbsp; Launch: Space / Click &nbsp;•&nbsp; Pause: P</p>
      </div>
    </div>
  </div>

  <div class="bar">
    <div class="controlsHint"><b>Mouse</b> or <b>← →</b> move the burger &nbsp;•&nbsp; <b>Space</b> launch &nbsp;•&nbsp; Watch it play itself in demo mode!</div>
    <div class="btns">
      <button id="pauseBtn">⏸ Pause</button>
      <button id="restartBtn">↺ Restart</button>
      <button id="soundBtn">Sound: On</button>
    </div>
  </div>
</div>

<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const W = 960, H = 560;
const overlay = document.getElementById('overlay');
const toastEl = document.getElementById('toast');
const scoreEl = document.getElementById('scoreVal');
const livesEl = document.getElementById('livesVal');
const levelEl = document.getElementById('levelVal');
const heartsEl = document.getElementById('heartsVal');
const bricksEl = document.getElementById('bricksVal');
const ovSub = document.getElementById('ovSub');

let started = false, paused = false, soundOn = true;
let score=0, lives=3, level=1, heartsBurst=0, combo=0, shake=0;
let bricks=[], particles=[], popups=[], trail=[];
let stuckTimer=0, toastTimer=null;
let targetX = W/2;

const paddle = {x:W/2, y:H-52, w:122, h:38, vx:0};
const ball = {x:W/2, y:H-70, r:10, vx:0, vy:0, speed:390, stuck:true};
const keys = {};

let AC=null;
function ensureAudio(){ if(!AC){ try{ AC=new (window.AudioContext||window.webkitAudioContext)(); }catch(e){} } if(AC && AC.state==='suspended') AC.resume(); }
function tone(f1,f2,dur,type,vol){
  if(!soundOn||!AC) return;
  type=type||'sine'; vol=vol||0.1;
  try{
    const o=AC.createOscillator(), g=AC.createGain();
    o.type=type; o.frequency.setValueAtTime(f1,AC.currentTime);
    o.frequency.exponentialRampToValueAtTime(Math.max(30,f2),AC.currentTime+dur);
    g.gain.setValueAtTime(vol,AC.currentTime); g.gain.exponentialRampToValueAtTime(0.001,AC.currentTime+dur);
    o.connect(g); g.connect(AC.destination); o.start(); o.stop(AC.currentTime+dur);
  }catch(e){}
}

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();
}
function clamp(v,a,b){ return Math.max(a,Math.min(b,v)); }
function rand(a,b){ return a+Math.random()*(b-a); }

const rowColors = [
  {fill:'#ff4757', dark:'#d92d3a'},
  {fill:'#ff8c42', dark:'#e56f1d'},
  {fill:'#ffb703', dark:'#e09e00'},
  {fill:'#ff6b9d', dark:'#e04a7d'},
  {fill:'#6ac84f', dark:'#4ea63a'},
  {fill:'#4ecdc4', dark:'#2fb3a8'}
];
const heartColors = ['#ff2e63','#ff5d8f','#ff8fab','#e63946','#ff7096','#ff4d6d','#ffb3c6'];

function buildBricks(){
  bricks=[]; const cols=10, rows=6, gap=6, top=62, left=20;
  const bw=(W-left*2-(cols-1)*gap)/cols, bh=27;
  for(let r=0;r<rows;r++){
    for(let c=0;c<cols;c++){
      const gold = (r===1 && c===4) || (r===3 && c===7) || (Math.random()<0.06);
      bricks.push({x:left+c*(bw+gap), y:top+r*(bh+gap), w:bw, h:bh, cx:left+c*(bw+gap)+bw/2, cy:top+r*(bh+gap)+bh/2,
        color: gold? {fill:'#ffd60a',dark:'#e0a800'} : rowColors[r], alive:true, gold:gold});
    }
  }
  updateHUD();
}
function resetBall(){
  ball.stuck=true; ball.vx=0; ball.vy=0; ball.x=paddle.x; ball.y=paddle.y-ball.r-3; ball.speed=390+ (level-1)*22; trail=[]; stuckTimer=0;
}
function resetGame(){
  score=0; lives=3; level=1; heartsBurst=0; combo=0; particles=[]; popups=[];
  paddle.x=W/2; targetX=W/2; buildBricks(); resetBall(); updateHUD();
}
function updateHUD(){
  scoreEl.textContent=score; levelEl.textContent=level; heartsEl.textContent=heartsBurst;
  livesEl.textContent='♥'.repeat(Math.max(0,lives))+'♡'.repeat(Math.max(0,3-lives));
  bricksEl.textContent=bricks.filter(b=>b.alive).length;
}
function showToast(msg){
  toastEl.textContent=msg; toastEl.classList.add('show');
  clearTimeout(toastTimer); toastTimer=setTimeout(()=>toastEl.classList.remove('show'),1500);
}
function launch(){
  if(!ball.stuck) return;
  const ang = rand(-0.6,0.6);
  ball.vx = Math.sin(ang)*ball.speed; ball.vy = -Math.cos(ang)*ball.speed; ball.stuck=false;
  combo=0; tone(500,900,0.12,'sine',0.08);
}
function spawnHearts(x,y,count,boost){
  for(let i=0;i<count;i++){
    const a=rand(0,Math.PI*2), sp=rand(70,330)*(boost||1);
    particles.push({x:x,y:y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp-60,size:rand(6,15)*(boost?1.2:1),
      rot:rand(-0.7,0.7),vr:rand(-3,3),life:rand(0.9,1.7),max:1.7,color:heartColors[Math.floor(Math.random()*heartColors.length)],
      phase:rand(0,6), shape: Math.random()<0.82?'heart':'dot'});
  }
  // crumbs
  for(let i=0;i<7;i++){
    const a=rand(0,Math.PI*2), sp=rand(60,220);
    particles.push({x:x,y:y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp-40,size:rand(2.5,5),rot:rand(0,3),vr:rand(-6,6),
      life:rand(0.5,0.9),max:0.9,color:Math.random()<0.5?'#c98a3d':'#ffb703',phase:rand(0,6),shape:'dot'});
  }
  heartsBurst+=count;
}
function addPopup(x,y,text,color){
  popups.push({x:x,y:y,text:text,life:1,color:color||'#ff2e63'});
}
function breakBrick(b){
  b.alive=false; combo++;
  const pts=10+(combo-1)*2; score+=pts;
  spawnHearts(b.cx,b.cy, b.gold?38:24, b.gold?1.35:1);
  addPopup(b.cx,b.cy,'+'+pts, b.gold?'#e09e00':'#ff2e63');
  if(combo>=4) addPopup(W/2,120,'COMBO x'+combo+' ♥','#ff2e63');
  shake=Math.min(0.22, shake+0.1);
  ball.speed=Math.min(640, ball.speed+4);
  tone(b.gold?900:680,180,0.16,'square',0.07); tone(1100,300,0.1,'sine',0.06);
  updateHUD();
  if(bricks.every(x=>!x.alive)){
    level++; score+=100; spawnHearts(W/2,220,90,1.5); showToast('LEVEL CLEAR!  Level '+level+'  +100');
    tone(600,1200,0.25,'sine',0.1); buildBricks(); resetBall();
  }
}
function loseBall(){
  lives--; tone(400,70,0.4,'sawtooth',0.1); updateHUD();
  if(lives<=0){
    const fs=score, fh=heartsBurst;
    ovSub.textContent='Game over! You scored '+fs+' and burst '+fh+' hearts. Click to play again!';
    overlay.style.display='flex'; started=false; paused=false;
    document.getElementById('pauseBtn').textContent='⏸ Pause';
    resetGame();
  } else {
    showToast('Ouch! '+(lives)+' '+(lives===1?'life':'lives')+' left'); resetBall();
  }
}
function startGame(){
  if(started) return;
  started=true; paused=false; ensureAudio();
  overlay.style.display='none';
  resetGame(); showToast('Go! Break bricks, burst hearts ♥');
  tone(600,1000,0.15,'sine',0.1);
}
overlay.addEventListener('click', startGame);

function canvasX(clientX){ const r=canvas.getBoundingClientRect(); return (clientX-r.left)/r.width*W; }
canvas.addEventListener('mousemove', e=>{ targetX=clamp(canvasX(e.clientX),10,W-10); });
canvas.addEventListener('click', ()=>{ if(!started){startGame();} else launch(); });
canvas.addEventListener('touchstart', e=>{ if(!started) startGame(); else { targetX=clamp(canvasX(e.touches[0].clientX),10,W-10); launch(); } e.preventDefault(); },{passive:false});
canvas.addEventListener('touchmove', e=>{ targetX=clamp(canvasX(e.touches[0].clientX),10,W-10); e.preventDefault(); },{passive:false});

window.addEventListener('keydown', e=>{
  if(!started){ startGame(); return; }
  const k=e.key.toLowerCase();
  if(['arrowleft','arrowright','arrowup','arrowdown',' '].includes(k)) e.preventDefault();
  keys[e.key]=true; keys[k]=true;
  if(e.key===' '||k===' ') launch();
  if(k==='p'){ togglePause(); }
  if(k==='r'){ resetGame(); showToast('Restarted!'); }
});
window.addEventListener('keyup', e=>{ keys[e.key]=false; keys[e.key.toLowerCase()]=false; });

function togglePause(){
  if(!started) return; paused=!paused;
  document.getElementById('pauseBtn').textContent = paused?'▶ Resume':'⏸ Pause';
  showToast(paused?'Paused':'Resumed');
}
document.getElementById('pauseBtn').addEventListener('click', ()=>{ if(!started) startGame(); else togglePause(); });
document.getElementById('restartBtn').addEventListener('click', ()=>{ if(!started) startGame(); else { resetGame(); showToast('Restarted!'); } });
document.getElementById('soundBtn').addEventListener('click', function(){ soundOn=!soundOn; this.textContent='Sound: '+(soundOn?'On':'Off'); if(soundOn){ensureAudio(); tone(700,900,0.1);} });

// ambient hearts
let ambient=[];
for(let i=0;i<8;i++) ambient.push({x:rand(30,W-30),y:rand(0,H),s:rand(10,22),sp:rand(14,34),ph:rand(0,6),c:heartColors[i%heartColors.length]});

function drawHeartShape(x,y,s,rot,alpha,color){
  ctx.save(); ctx.globalAlpha=alpha; ctx.translate(x,y); ctx.rotate(rot); ctx.fillStyle=color;
  ctx.beginPath();
  ctx.moveTo(0,s*0.3);
  ctx.bezierCurveTo(0,0,-s*0.5,0,-s*0.5,s*0.3);
  ctx.bezierCurveTo(-s*0.5,0,0,s*0.4,0,s*0.85);
  ctx.bezierCurveTo(0,s*0.4,s*0.5,0,s*0.5,s*0.3);
  ctx.bezierCurveTo(s*0.5,0,0,0,0,s*0.3);
  ctx.fill();
  ctx.globalAlpha=alpha*0.55; ctx.fillStyle='#fff'; ctx.beginPath(); ctx.ellipse(-s*0.18,s*0.05,s*0.09,s*0.14,-0.4,0,Math.PI*2); ctx.fill();
  ctx.restore();
}

function drawPaddle(){
  const w=paddle.w,h=paddle.h,cx=paddle.x,y=paddle.y,x=cx-w/2;
  ctx.save();
  ctx.fillStyle='rgba(90,40,0,0.22)'; ctx.beginPath(); ctx.ellipse(cx,y+h+7,w*0.48,7,0,0,Math.PI*2); ctx.fill();
  const tilt=clamp(paddle.vx*0.00012,-0.09,0.09);
  ctx.translate(cx,y); ctx.rotate(tilt); ctx.translate(-cx,-y);
  // bottom bun
  rr(x+8,y+h-11,w-16,11,6); ctx.fillStyle='#f6c177'; ctx.fill(); ctx.strokeStyle='#c98a3d'; ctx.lineWidth=1.5; ctx.stroke();
  // patty
  rr(x+2,y+h-20,w-4,11,6); ctx.fillStyle='#7b3f1d'; ctx.fill(); ctx.strokeStyle='#5d2e12'; ctx.stroke();
  // cheese drips
  ctx.fillStyle='#ffb703'; ctx.beginPath();
  ctx.moveTo(x+4,y+h-24); ctx.lineTo(x+w-4,y+h-24); ctx.lineTo(x+w-4,y+h-18); ctx.lineTo(x+w-22,y+h-18);
  ctx.lineTo(x+w-28,y+h-12); ctx.lineTo(x+w-36,y+h-18); ctx.lineTo(x+30,y+h-18); ctx.lineTo(x+23,y+h-12);
  ctx.lineTo(x+14,y+h-18); ctx.lineTo(x+4,y+h-18); ctx.closePath(); ctx.fill(); ctx.strokeStyle='#e09e00'; ctx.stroke();
  // lettuce
  rr(x+1,y+h-30,w-2,7,4); ctx.fillStyle='#6ac84f'; ctx.fill(); ctx.strokeStyle='#4ea63a'; ctx.stroke();
  // tomato
  rr(x+10,y+h-34,w-20,6,3); ctx.fillStyle='#ff4b4b'; ctx.fill(); ctx.strokeStyle='#d92d2d'; ctx.stroke();
  // top bun dome
  ctx.fillStyle='#ffd08a'; ctx.beginPath(); ctx.ellipse(cx,y+14,w/2-2,15,0,Math.PI,0); ctx.closePath(); ctx.fill();
  ctx.strokeStyle='#c98a3d'; ctx.lineWidth=2; ctx.stroke();
  rr(x+2,y+11,w-4,6,3); ctx.fillStyle='#f6c177'; ctx.fill(); ctx.strokeStyle='#c98a3d'; ctx.lineWidth=1; ctx.stroke();
  // sesame
  ctx.fillStyle='#fff3d6';
  [[-34,-8],[-14,-12],[12,-11],[32,-7],[0,-7]].forEach(p=>{ ctx.save(); ctx.translate(cx+p[0],y+14+p[1]); ctx.rotate(0.6); ctx.beginPath(); ctx.ellipse(0,0,4.5,2.2,0,0,Math.PI*2); ctx.fill(); ctx.restore(); });
  // face
  ctx.fillStyle='#3a1d08';
  ctx.beginPath(); ctx.arc(cx-15,y+5,3.4,0,Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.arc(cx+15,y+5,3.4,0,Math.PI*2); ctx.fill();
  ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(cx-14,y+4,1.2,0,Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.arc(cx+16,y+4,1.2,0,Math.PI*2); ctx.fill();
  ctx.strokeStyle='#3a1d08'; ctx.lineWidth=1.8; ctx.beginPath(); ctx.arc(cx,y+7,5,0.15*Math.PI,0.85*Math.PI); ctx.stroke();
  ctx.fillStyle='rgba(255,107,157,0.55)'; ctx.beginPath(); ctx.ellipse(cx-25,y+9,5,3,0,0,Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.ellipse(cx+25,y+9,5,3,0,0,Math.PI*2); ctx.fill();
  ctx.restore();
}

function drawBall(){
  // trail
  for(let i=0;i<trail.length;i++){
    const t=trail[i]; ctx.globalAlpha=i/trail.length*0.22; ctx.fillStyle='#ff2e63';
    ctx.beginPath(); ctx.arc(t.x,t.y,ball.r*(0.4+0.5*i/trail.length),0,Math.PI*2); ctx.fill();
  }
  ctx.globalAlpha=1;
  const g=ctx.createRadialGradient(ball.x-3,ball.y-4,2,ball.x,ball.y,ball.r);
  g.addColorStop(0,'#ff8fab'); g.addColorStop(0.5,'#ff2e63'); g.addColorStop(1,'#c9184a');
  ctx.fillStyle=g; ctx.beginPath(); ctx.arc(ball.x,ball.y,ball.r,0,Math.PI*2); ctx.fill();
  ctx.strokeStyle='#a4133c'; ctx.lineWidth=1.5; ctx.stroke();
  ctx.fillStyle='rgba(255,255,255,0.85)'; ctx.beginPath(); ctx.ellipse(ball.x-3.5,ball.y-4,3.5,2,-0.6,0,Math.PI*2); ctx.fill();
  ctx.fillStyle='#2fb344'; ctx.beginPath(); ctx.ellipse(ball.x,ball.y-ball.r+1,4,2,0,0,Math.PI*2); ctx.fill();
}

function draw(){
  ctx.save();
  if(shake>0){ ctx.translate(rand(-shake,shake)*14, rand(-shake,shake)*10); shake*=0.88; if(shake<0.01) shake=0; }
  // bg
  const bg=ctx.createLinearGradient(0,0,0,H); bg.addColorStop(0,'#fff7e6'); bg.addColorStop(1,'#ffe3ec');
  ctx.fillStyle=bg; ctx.fillRect(-20,-20,W+40,H+40);
  ctx.fillStyle='rgba(255,107,157,0.05)';
  for(let x=0;x<W;x+=44) for(let y=0;y<H;y+=44) if((x+y)/44%2===0) ctx.fillRect(x,y,44,44);
  // ambient hearts
  ambient.forEach(a=>{ drawHeartShape(a.x+Math.sin(a.ph)*8, a.y, a.s, Math.sin(a.ph)*0.2, 0.18, a.c); });
  // table strip
  ctx.fillStyle='rgba(201,138,61,0.12)'; ctx.fillRect(0,H-28,W,28);
  // bricks
  bricks.forEach(b=>{
    if(!b.alive) return;
    rr(b.x,b.y,b.w,b.h,7); ctx.fillStyle=b.color.fill; ctx.fill(); ctx.strokeStyle=b.color.dark; ctx.lineWidth=2; ctx.stroke();
    ctx.fillStyle='rgba(255,255,255,0.35)'; rr(b.x+4,b.y+3,b.w-8,7,4); ctx.fill();
    if(b.gold){ ctx.fillStyle='#fff'; ctx.font='12px sans-serif'; ctx.textAlign='center'; ctx.fillText('★',b.cx,b.cy+4); }
    else { ctx.globalAlpha=0.18; drawHeartShape(b.cx,b.cy-5,13,0,0.5,'#fff'); ctx.globalAlpha=1; }
  });
  // particles
  particles.forEach(p=>{
    const a=clamp(p.life/p.max,0,1);
    if(p.shape==='heart') drawHeartShape(p.x,p.y,p.size,p.rot,a,p.color);
    else { ctx.save(); ctx.globalAlpha=a; ctx.translate(p.x,p.y); ctx.rotate(p.rot); ctx.fillStyle=p.color; ctx.fillRect(-p.size/2,-p.size/2,p.size,p.size); ctx.restore(); }
  });
  drawPaddle();
  drawBall();
  // popups
  popups.forEach(p=>{
    ctx.globalAlpha=clamp(p.life,0,1); ctx.font='900 17px system-ui,sans-serif'; ctx.textAlign='center';
    ctx.lineWidth=4; ctx.strokeStyle='#fff'; ctx.strokeText(p.text,p.x,p.y); ctx.fillStyle=p.color; ctx.fillText(p.text,p.x,p.y); ctx.globalAlpha=1;
  });
  // combo / hint when stuck
  if(ball.stuck){
    ctx.font='800 15px system-ui,sans-serif'; ctx.textAlign='center'; ctx.fillStyle='rgba(90,45,12,0.75)';
    ctx.fillText(started?'Click or press Space to launch!':'Demo mode — the burger plays itself ♥', W/2, paddle.y-90);
  }
  if(paused){
    ctx.fillStyle='rgba(255,255,255,0.65)'; ctx.fillRect(0,0,W,H);
    ctx.font='900 42px system-ui,sans-serif'; ctx.textAlign='center'; ctx.fillStyle='#5a2d0c'; ctx.fillText('PAUSED',W/2,H/2);
  }
  ctx.restore();
}

function update(dt){
  // ambient
  ambient.forEach(a=>{ a.y-=a.sp*dt; a.ph+=dt*1.5; if(a.y<-30){a.y=H+20; a.x=rand(30,W-30);} });
  if(paused) return;

  // paddle control
  const oldX=paddle.x;
  if(!started){
    // AI
    if(ball.stuck){ targetX=W/2 + Math.sin(performance.now()*0.001)*60; }
    else {
      let pred = ball.x + ball.vx*0.28;
      if(ball.vy>0) targetX=clamp(pred, paddle.w/2, W-paddle.w/2);
    }
    paddle.x += (targetX-paddle.x)*Math.min(1,10*dt);
  } else {
    let dir=0;
    if(keys['ArrowLeft']||keys['a']||keys['A']) dir-=1;
    if(keys['ArrowRight']||keys['d']||keys['D']) dir+=1;
    if(dir!==0){ paddle.x += dir*700*dt; targetX=paddle.x; }
    else paddle.x += (targetX-paddle.x)*Math.min(1,22*dt);
  }
  paddle.x=clamp(paddle.x,paddle.w/2+4,W-paddle.w/2-4);
  paddle.vx=(paddle.x-oldX)/Math.max(dt,0.001);

  // ball
  if(ball.stuck){
    ball.x=paddle.x; ball.y=paddle.y-ball.r-3;
    if(!started){ stuckTimer+=dt; if(stuckTimer>0.7) launch(); }
  } else {
    const prevX=ball.x, prevY=ball.y;
    ball.x+=ball.vx*dt; ball.y+=ball.vy*dt;
    trail.push({x:ball.x,y:ball.y}); if(trail.length>10) trail.shift();
    // walls
    if(ball.x-ball.r<4){ ball.x=4+ball.r; ball.vx=Math.abs(ball.vx); tone(220,180,0.06,'sine',0.05); }
    if(ball.x+ball.r>W-4){ ball.x=W-4-ball.r; ball.vx=-Math.abs(ball.vx); tone(220,180,0.06,'sine',0.05); }
    if(ball.y-ball.r<4){ ball.y=4+ball.r; ball.vy=Math.abs(ball.vy); tone(220,180,0.06,'sine',0.05); }
    if(ball.y-ball.r>H){ loseBall(); }
    // paddle
    if(ball.vy>0 && ball.y+ball.r>=paddle.y && ball.y-ball.r<=paddle.y+paddle.h+8 && Math.abs(ball.x-paddle.x)<=paddle.w/2+ball.r){
      const rel=clamp((ball.x-paddle.x)/(paddle.w/2),-1,1);
      const ang=rel*1.05; // ~60deg
      const sp=Math.hypot(ball.vx,ball.vy)||ball.speed;
      const ns=Math.max(sp, ball.speed);
      ball.vx=Math.sin(ang)*ns; ball.vy=-Math.cos(ang)*ns;
      ball.y=paddle.y-ball.r-1; combo=0;
      spawnHearts(ball.x,ball.y,3,0.5);
      tone(320,220,0.08,'sine',0.08);
    }
    // bricks
    for(const b of bricks){
      if(!b.alive) continue;
      const cx=clamp(ball.x,b.x,b.x+b.w), cy=clamp(ball.y,b.y,b.y+b.h);
      const dx=ball.x-cx, dy=ball.y-cy;
      if(dx*dx+dy*dy < ball.r*ball.r){
        if(prevY+ball.r<=b.y || prevY-ball.r>=b.y+b.h) ball.vy*=-1; else if(prevX+ball.r<=b.x || prevX-ball.r>=b.x+b.w) ball.vx*=-1; else ball.vy*=-1;
        breakBrick(b); break;
      }
    }
  }
  // particles
  particles.forEach(p=>{
    p.life-=dt; p.x+=p.vx*dt + Math.sin((p.max-p.life)*6+p.phase)*14*dt; p.y+=p.vy*dt; p.rot+=p.vr*dt;
    p.vx*=(1-1.6*dt); p.vy*=(1-1.2*dt); p.vy-=55*dt;
  });
  particles=particles.filter(p=>p.life>0 && p.x>-30 && p.x<W+30 && p.y>-30 && p.y<H+30);
  popups.forEach(p=>{ p.y-=34*dt; p.life-=dt*1.1; });
  popups=popups.filter(p=>p.life>0);
}

let last=performance.now();
function loop(now){
  const dt=Math.min(0.033,(now-last)/1000); last=now;
  update(dt); draw();
  requestAnimationFrame(loop);
}
resetGame();
requestAnimationFrame(loop);
</script>
</body>
</html>
Reasoning trace (8,304 tokens)
Designing a single-file 1280x800 canvas breakout game with hamburger paddle, heart particle explosions, and self-playing demo mode.Extending the design with responsive layout, brick colors, particle physics, and AI paddle behavior.Adding collision handling, particle effects, UI elements, and audio with input controls.Implementing DPR-aware canvas sizing, heart path drawing, and overlay UI with demo mode and level progression.Adding translucent overlay card, HUD controls, pause handling, and input smoothing for paddle and ball.Implementing brick collision, paddle physics, AI movement, and responsive styling with particle limits.Adding burger face rendering, brick color patterns, background effects, and input handling for controls.Implementing audio context init, tone effects, heart particles, and level clear handling.Adding burger-themed paddle and tomato ball rendering with tilt, shadows, and face details.