Hamburger breakoutOne-line
Muse Ember
by MetaReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 18.7 s
- Cost
- $0.0041
- Output tokens
- 8,074
- Spent thinking
- 657 tokens
- Speed
- 518.7 tok/s
- Console errors
- 0
- First token after
- 3.2 s
- First visible text after
- 4.2 s
- Prompt tokens
- 375
- Code size
- 20.4 KB
- Finish reason
- stop
- Served by
- Meta
- Model id
meta/muse-ember- Reasoning sent
{"effort":"low"}- 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 · 391 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>🍔💖 Hamburger Breakout — Heart Explosion</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{height:100%}
body{
font-family:ui-rounded, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: radial-gradient(1200px 700px at 20% -10%, #ffe4ec 0%, transparent 55%),
radial-gradient(1000px 600px at 110% 10%, #fff3c4 0%, transparent 50%),
linear-gradient(180deg,#fff8e7 0%,#ffeef3 55%,#ffe0e8 100%);
color:#3d1f2a; overflow:hidden; display:flex; flex-direction:column; align-items:center;
min-height:100vh;
}
/* ambient floating hearts bg */
.bg-heart{position:fixed;pointer-events:none;opacity:.14;animation:floatUp linear infinite;z-index:0;line-height:1}
@keyframes floatUp{from{transform:translateY(110vh) rotate(0deg)}to{transform:translateY(-10vh) rotate(340deg)}}
.wrap{position:relative;z-index:1;width:min(920px,100vw);height:100vh;height:100dvh;display:flex;flex-direction:column;padding:10px 12px 8px;gap:8px}
header{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.logo{display:flex;align-items:center;gap:10px}
.logo-emoji{font-size:42px;line-height:1;filter:drop-shadow(0 4px 0 rgba(0,0,0,.08));animation:wobble 2.4s ease-in-out infinite}
@keyframes wobble{0%,100%{transform:rotate(-4deg) scale(1)}50%{transform:rotate(5deg) scale(1.06)}}
h1{font-size:clamp(22px,4.5vw,34px);font-weight:900;letter-spacing:-.5px;line-height:.95}
h1 span{color:#ff3b6e}
.sub{font-size:12px;font-weight:700;opacity:.65;margin-top:3px;letter-spacing:.4px}
.stats{display:flex;gap:8px;flex-wrap:wrap}
.card{background:#fff;border:2.5px solid #3d1f2a;border-radius:16px;padding:6px 12px;min-width:92px;box-shadow:3px 3px 0 #3d1f2a;text-align:center}
.card b{display:block;font-size:20px;line-height:1}
.card label{font-size:10px;font-weight:900;letter-spacing:1.2px;opacity:.6}
.card.pink{background:#ffd6e1}.card.yellow{background:#fff1a8}.card.white{background:#fff}
.lives{font-size:16px;letter-spacing:1px}
.stage{position:relative;flex:1;min-height:0;display:flex;align-items:center;justify-content:center}
.frame{position:relative;width:100%;max-width:860px;background:#2b1332;border:3.5px solid #3d1f2a;border-radius:22px;box-shadow:8px 8px 0 #3d1f2a, 0 20px 50px rgba(255,59,110,.25);overflow:hidden}
canvas{display:block;width:100%;height:auto;aspect-ratio:800/580;touch-action:none;cursor:none;background:linear-gradient(180deg,#3a1a4d 0%,#5b245e 52%,#8a2f5b 100%)}
@media(max-width:640px){canvas{cursor:default}}
/* overlay */
#overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;background:linear-gradient(180deg,rgba(43,19,50,.55),rgba(43,19,50,.78));backdrop-filter:blur(2px);z-index:5;padding:20px;transition:opacity .35s;color:#fff;cursor:pointer}
#overlay.hide{opacity:0;pointer-events:none}
.big-burger{font-size:76px;line-height:1;animation:bounce 1s ease-in-out infinite;filter:drop-shadow(0 8px 0 rgba(0,0,0,.25))}
@keyframes bounce{0%,100%{transform:translateY(0) rotate(-3deg)}50%{transform:translateY(-12px) rotate(3deg)}}
#overlay h2{font-size:clamp(26px,6vw,44px);font-weight:900;line-height:.95;margin:10px 0 6px;text-shadow:3px 3px 0 #ff3b6e}
#overlay p{font-size:15px;font-weight:700;opacity:.92;max-width:520px;line-height:1.4}
.pill{display:inline-flex;gap:6px;align-items:center;background:#ffdf3d;color:#3d1f2a;border:2.5px solid #3d1f2a;border-radius:999px;padding:10px 18px;font-weight:900;font-size:15px;box-shadow:3px 3px 0 #3d1f2a;margin-top:16px;animation:pulse 1.2s infinite}
@keyframes pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.05)}}
.mini-row{display:flex;gap:8px;margin-top:14px;flex-wrap:wrap;justify-content:center}
.mini{background:rgba(255,255,255,.95);color:#3d1f2a;border-radius:999px;padding:6px 11px;font-size:12px;font-weight:800;border:2px solid #3d1f2a}
/* bottom bar */
footer{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap}
.hint{font-size:12px;font-weight:800;opacity:.7}
.btns{display:flex;gap:8px}
button{font-family:inherit;font-weight:900;font-size:13px;border:2.5px solid #3d1f2a;border-radius:999px;padding:8px 14px;background:#fff;box-shadow:3px 3px 0 #3d1f2a;cursor:pointer;transition:transform .08s, box-shadow .08s}
button:active{transform:translate(2px,2px);box-shadow:1px 1px 0 #3d1f2a}
button.primary{background:#ff3b6e;color:#fff}
button.yellow{background:#ffdf3d}
#toast{position:absolute;left:50%;top:14px;transform:translateX(-50%) translateY(-8px);background:#fff;border:2.5px solid #3d1f2a;border-radius:999px;padding:7px 14px;font-weight:900;font-size:13px;box-shadow:3px 3px 0 #3d1f2a;opacity:0;pointer-events:none;transition:.25s;z-index:6;white-space:nowrap}
#toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
</style>
</head>
<body>
<div id="bg"></div>
<div class="wrap">
<header>
<div class="logo">
<div class="logo-emoji">🍔</div>
<div>
<h1>HAMBURGER<br><span>HEART BREAKOUT</span></h1>
<div class="sub">EVERY BRICK BURSTS INTO HEARTS 💖</div>
</div>
</div>
<div class="stats">
<div class="card pink"><label>SCORE</label><b id="score">0</b></div>
<div class="card yellow"><label>💖 HEARTS</label><b id="hearts">0</b></div>
<div class="card white"><label>LIVES</label><b class="lives" id="lives">🍔🍔🍔</b></div>
<div class="card white"><label>LEVEL</label><b id="level">1</b></div>
</div>
</header>
<div class="stage">
<div class="frame" id="frame">
<canvas id="game" width="800" height="580"></canvas>
<div id="toast">+100 ❤️</div>
<div id="overlay">
<div class="big-burger">🍔💥💖</div>
<h2>CLICK TO PLAY!</h2>
<p>The paddle is a <b style="color:#ffdf3d">hamburger</b>. Smash the bricks —<br>each one explodes into <b style="color:#ff9ec0">TONS of little hearts!</b></p>
<div class="pill">👆 CLICK or PRESS ANY KEY TO START</div>
<div class="mini-row">
<span class="mini">🖱️ Mouse / Touch to move</span>
<span class="mini">⌨️ ← → or A / D</span>
<span class="mini">⏸️ Space = Pause</span>
<span class="mini">🤖 Demo AI is playing now</span>
</div>
</div>
</div>
</div>
<footer>
<div class="hint">💡 Tip: Hit the ball with the edges of the burger for a sharper angle!</div>
<div class="btns">
<button id="pauseBtn">⏸ Pause</button>
<button id="resetBtn" class="yellow">↺ Restart</button>
<button id="startBtn" class="primary">▶ Start Game</button>
</div>
</footer>
</div>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const W = 800, H = 580;
const $ = id => document.getElementById(id);
const scoreEl=$('score'), heartsEl=$('hearts'), livesEl=$('lives'), levelEl=$('level'), overlay=$('overlay'), toast=$('toast');
let demo = true, paused=false, started=false;
let score=0, heartCount=0, lives=3, level=1;
let keys={}, mouseX=W/2, shake=0, flash=0, combo=0, comboTimer=0;
const paddle = {x:W/2, y:H-42, w:124, h:46, vx:0, tx:W/2};
const ball = {x:W/2, y:H-90, r:9, vx:4.2, vy:-5.2, trail:[]};
let bricks=[], particles=[], floaters=[], ambient=[];
const rowColors = [
{c:'#ff3b6e', d:'#d61e52'}, {c:'#ff8a3d', d:'#e06a1f'}, {c:'#ffd93d', d:'#e0b91f'},
{c:'#6be15e', d:'#3fb33a'}, {c:'#3ec6ff', d:'#1a9ed6'}, {c:'#b78cff', d:'#8a5fe6'}
];
const heartColors = ['#ff3b6e','#ff6b9d','#ff8fae','#ffb3c6','#ffdf3d','#ffffff','#ff5c7a','#ff2e63'];
function makeBricks(lv){
bricks=[];
const rows = Math.min(5+lv, 8);
const cols = 10;
const gap=6, top=58, bw=(W-40-(cols-1)*gap)/cols, bh=22;
for(let r=0;r<rows;r++){
for(let c=0;c<cols;c++){
// checker holes for variety on higher levels
if(lv>1 && (r===rows-1 && (c===2||c===7))) continue;
bricks.push({x:20+c*(bw+gap), y:top+r*(bh+gap), w:bw, h:bh, hp: (lv>2 && r===0 && c%3===0)?2:1, col:rowColors[r%rowColors.length], dead:false, pop:0});
}
}
}
function resetBall(){
ball.x=paddle.x; ball.y=paddle.y-34; const a=-Math.PI/2 + (Math.random()-.5)*.6; const s=6.2+level*.3;
ball.vx=Math.cos(a)*s; ball.vy=Math.sin(a)*s; ball.trail=[];
}
function resetGame(){
score=0; heartCount=0; lives=3; level=1; combo=0;
makeBricks(1); resetBall(); particles=[]; floaters=[]; updateHUD();
}
function updateHUD(){
scoreEl.textContent=score; heartsEl.textContent=heartCount; levelEl.textContent=level;
livesEl.textContent = '🍔'.repeat(Math.max(0,lives)) || '—';
if(lives===0) livesEl.textContent='💔';
}
function showToast(txt){ toast.textContent=txt; toast.classList.add('show'); clearTimeout(showToast.t); showToast.t=setTimeout(()=>toast.classList.remove('show'),1100); }
/* ---------- HEARTS ---------- */
function drawHeart(x,y,s,rot,color,alpha){
ctx.save(); ctx.translate(x,y); ctx.rotate(rot); ctx.globalAlpha=alpha;
ctx.fillStyle=color; ctx.beginPath();
const u=s/16;
ctx.moveTo(0, 6*u);
ctx.bezierCurveTo(-10*u,-2*u, -6*u,-9*u, 0,-4.5*u);
ctx.bezierCurveTo( 6*u,-9*u, 10*u,-2*u, 0, 6*u);
ctx.fill();
// highlight
ctx.fillStyle='rgba(255,255,255,.55)'; ctx.beginPath(); ctx.ellipse(-3.2*u,-3.5*u,2.2*u,1.3*u,-.6,0,Math.PI*2); ctx.fill();
ctx.restore();
}
function explodeHearts(x,y,count,power=1){
for(let i=0;i<count;i++){
const a=Math.random()*Math.PI*2, sp=(1.5+Math.random()*5.5)*power;
particles.push({
x:x+(Math.random()-.5)*30, y:y+(Math.random()-.5)*14,
vx:Math.cos(a)*sp, vy:Math.sin(a)*sp - 2.2,
s:5+Math.random()*13, rot:(Math.random()-.5)*1.4, vr:(Math.random()-.5)*.22,
life:1, decay:.008+Math.random()*.012, color:heartColors[Math.floor(Math.random()*heartColors.length)],
grav: .06+Math.random()*.05
});
}
}
function breakBrick(b){
b.dead=true; combo++; comboTimer=90;
const pts = 50 + combo*10;
score+=pts; heartCount+= 26;
explodeHearts(b.x+b.w/2, b.y+b.h/2, 28, 1);
explodeHearts(b.x+b.w/2, b.y+b.h/2, 12, .7);
floaters.push({x:b.x+b.w/2,y:b.y,txt:'+'+pts+' 💖',life:1});
shake=Math.min(7,shake+2.5); flash=.25;
if(combo>=3) showToast('🔥 COMBO x'+combo+' +'+pts+' ❤️');
updateHUD();
if(bricks.every(x=>x.dead)){
level++; score+=250; makeBricks(level);
explodeHearts(W/2,140,70,1.4);
showToast('🎉 LEVEL '+level+'! +250 bonus');
updateHUD();
}
}
/* ---------- HAMBURGER PADDLE ---------- */
function drawBurger(x,y,w,h){
const bob = Math.sin(Date.now()/180)*1.2;
y+=bob;
ctx.save(); ctx.translate(x,y);
ctx.shadowColor='rgba(0,0,0,.35)'; ctx.shadowBlur=10; ctx.shadowOffsetY=5;
// bottom bun
ctx.fillStyle='#e8a04c'; rr(-w/2,-h/2+22,w,16,8); ctx.fill();
ctx.fillStyle='#c97d2a'; rr(-w/2,-h/2+22,w,5,3); ctx.fill();
// patty
ctx.fillStyle='#6b3a1f'; rr(-w/2+3,-h/2+16,w-6,10,5); ctx.fill();
ctx.fillStyle='#8a4f2a'; rr(-w/2+3,-h/2+16,w-6,4,2); ctx.fill();
// cheese drips
ctx.fillStyle='#ffcc00';
rr(-w/2+2,-h/2+10,w-4,7,3); ctx.fill();
ctx.beginPath(); ctx.moveTo(-w/2+14,-h/2+16); ctx.lineTo(-w/2+22,-h/2+24); ctx.lineTo(-w/2+30,-h/2+16); ctx.fill();
ctx.beginPath(); ctx.moveTo(w/2-30,-h/2+16); ctx.lineTo(w/2-22,-h/2+25); ctx.lineTo(w/2-14,-h/2+16); ctx.fill();
// tomato
ctx.fillStyle='#ff4d4d'; rr(-w/2+5,-h/2+6,w-10,6,3); ctx.fill();
ctx.fillStyle='#ff8a8a'; rr(-w/2+8,-h/2+7,w-16,2,1); ctx.fill();
// lettuce wavy
ctx.fillStyle='#4ecb5a'; ctx.beginPath();
const ly=-h/2+7;
ctx.moveTo(-w/2+1,ly);
for(let i=0;i<6;i++){ ctx.quadraticCurveTo(-w/2+ (i+.5)*(w/6), ly-5, -w/2+(i+1)*(w/6), ly); }
ctx.lineTo(w/2-1,ly+4); ctx.lineTo(-w/2+1,ly+4); ctx.closePath(); ctx.fill();
// top bun
ctx.shadowBlur=0; ctx.shadowOffsetY=0;
const g=ctx.createLinearGradient(0,-h/2-4,0,ly+2); g.addColorStop(0,'#ffc46b'); g.addColorStop(1,'#e8973a');
ctx.fillStyle=g; ctx.beginPath();
ctx.moveTo(-w/2, ly+1);
ctx.bezierCurveTo(-w/2,-h/2-8, -w/4,-h/2-10, 0,-h/2-10);
ctx.bezierCurveTo(w/4,-h/2-10, w/2,-h/2-8, w/2,ly+1);
ctx.closePath(); ctx.fill();
// sesame
ctx.fillStyle='#fff3d6';
[[-28,-14],[-10,-18],[12,-16],[30,-12],[0,-11],[-20,-9],[20,-8]].forEach(p=>{ ctx.beginPath(); ctx.ellipse(p[0],p[1],4,2.2,.5,0,Math.PI*2); ctx.fill(); });
// cute face
ctx.fillStyle='#3d1f2a';
ctx.beginPath(); ctx.arc(-14,-2,2.2,0,Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(14,-2,2.2,0,Math.PI*2); ctx.fill();
ctx.strokeStyle='#3d1f2a'; ctx.lineWidth=1.8; ctx.beginPath(); ctx.arc(0,0,5, .15*Math.PI, .85*Math.PI); ctx.stroke();
ctx.fillStyle='rgba(255,120,150,.55)'; ctx.beginPath(); ctx.ellipse(-22,2,5,3,0,0,Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.ellipse(22,2,5,3,0,0,Math.PI*2); ctx.fill();
ctx.restore();
}
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(); }
/* ---------- GAME LOOP ---------- */
function update(){
// paddle
if(demo){
const target = ball.vy>0 ? ball.x : W/2 + Math.sin(Date.now()/600)*90;
paddle.tx += (target - paddle.tx)*.14;
if(Math.abs(ball.x-paddle.x)<6 && ball.vy>0) paddle.tx=ball.x;
} else {
if(keys['ArrowLeft']||keys['a']) paddle.tx-=10;
if(keys['ArrowRight']||keys['d']) paddle.tx+=10;
paddle.tx = mouseX;
}
paddle.tx = Math.max(paddle.w/2, Math.min(W-paddle.w/2, paddle.tx));
paddle.vx = paddle.tx - paddle.x;
paddle.x += paddle.vx*.38;
paddle.x = Math.max(paddle.w/2, Math.min(W-paddle.w/2, paddle.x));
if(!paused){
ball.trail.push({x:ball.x,y:ball.y}); if(ball.trail.length>10) ball.trail.shift();
ball.x+=ball.vx; ball.y+=ball.vy;
if(ball.x<ball.r+4){ball.x=ball.r+4; ball.vx*=-1;}
if(ball.x>W-ball.r-4){ball.x=W-ball.r-4; ball.vx*=-1;}
if(ball.y<ball.r+4){ball.y=ball.r+4; ball.vy*=-1;}
// paddle collision
if(ball.vy>0 && Math.abs(ball.x-paddle.x)<paddle.w/2+ball.r && Math.abs(ball.y-(paddle.y-6))<16){
const rel=(ball.x-paddle.x)/(paddle.w/2);
const ang=-Math.PI/2 + rel*.95;
const sp=Math.min(11, Math.hypot(ball.vx,ball.vy)+.15);
ball.vx=Math.cos(ang)*sp; ball.vy=Math.sin(ang)*sp;
ball.y=paddle.y-22; combo=0;
explodeHearts(ball.x,ball.y,6,.5);
}
// bricks
for(const b of bricks){
if(b.dead) continue;
if(ball.x+ball.r>b.x && ball.x-ball.r<b.x+b.w && ball.y+ball.r>b.y && ball.y-ball.r<b.y+b.h){
const overlapL = (ball.x+ball.r)-b.x, overlapR=(b.x+b.w)-(ball.x-ball.r);
const overlapT = (ball.y+ball.r)-b.y, overlapB=(b.y+b.h)-(ball.y-ball.r);
const min=Math.min(overlapL,overlapR,overlapT,overlapB);
if(min===overlapT||min===overlapB) ball.vy*=-1; else ball.vx*=-1;
if(b.hp>1){ b.hp--; b.pop=1; explodeHearts(ball.x,ball.y,10,.6); score+=10; updateHUD(); }
else breakBrick(b);
break;
}
}
if(ball.y>H+20){
lives--; combo=0; updateHUD();
if(lives<=0){ showToast('💔 GAME OVER — Restarting!'); setTimeout(()=>{resetGame(); resetBall();},900); }
else { showToast('💔 Ouch! '+lives+' burger'+(lives>1?'s':'')+' left'); resetBall(); }
ball.y=H-90;
}
if(comboTimer>0){comboTimer--; if(comboTimer===0) combo=0;}
}
// particles
for(let i=particles.length-1;i>=0;i--){
const p=particles[i]; p.x+=p.vx; p.y+=p.vy; p.vy+=p.grav; p.vx*=.985; p.rot+=p.vr; p.life-=p.decay;
if(p.life<=0) particles.splice(i,1);
}
for(let i=floaters.length-1;i>=0;i--){ const f=floaters[i]; f.y-=1.1; f.life-=.018; if(f.life<=0) floaters.splice(i,1); }
for(const b of bricks) if(b.pop>0) b.pop-=.08;
if(shake>0) shake*=.85; if(flash>0) flash-=.02;
// ambient hearts
if(Math.random()<.12) ambient.push({x:Math.random()*W,y:H+20,s:6+Math.random()*10,vy:-(.6+Math.random()*1.2),vx:(Math.random()-.5)*.4,rot:Math.random()*6,vr:(Math.random()-.5)*.03,c:heartColors[Math.floor(Math.random()*heartColors.length)]});
for(let i=ambient.length-1;i>=0;i--){ const a=ambient[i]; a.x+=a.vx; a.y+=a.vy; a.rot+=a.vr; if(a.y<-30) ambient.splice(i,1); }
}
function draw(){
ctx.save();
if(shake>.4) ctx.translate((Math.random()-.5)*shake,(Math.random()-.5)*shake);
// bg deco
ctx.fillStyle='rgba(255,255,255,.05)';
for(let i=0;i<5;i++){ ctx.beginPath(); ctx.arc(100+i*160, 500-((i*37)%120), 50,0,Math.PI*2); ctx.fill(); }
ctx.fillStyle='rgba(255,223,61,.08)'; ctx.font='28px serif'; ctx.fillText('🍔', 30, 320); ctx.fillText('🍔', 740, 180);
for(const a of ambient) drawHeart(a.x,a.y,a.s,a.rot,a.c,.35);
// bricks
for(const b of bricks){
if(b.dead) continue;
const s=1+Math.max(0,b.pop)*.08;
ctx.save(); ctx.translate(b.x+b.w/2,b.y+b.h/2); ctx.scale(s,s); ctx.translate(-b.w/2,-b.h/2);
ctx.shadowColor='rgba(0,0,0,.3)'; ctx.shadowBlur=5; ctx.shadowOffsetY=3;
ctx.fillStyle=b.col.c; rr(0,0,b.w,b.h,7); ctx.fill();
ctx.shadowBlur=0; ctx.fillStyle='rgba(255,255,255,.35)'; rr(3,3,b.w-6,7,4); ctx.fill();
ctx.fillStyle='rgba(0,0,0,.12)'; rr(3,b.h-7,b.w-6,4,2); ctx.fill();
ctx.font='11px serif'; ctx.textAlign='center'; ctx.fillText(b.hp>1?'💖':'❤️', b.w/2, b.h/2+4);
ctx.restore();
}
// ball trail
ball.trail.forEach((t,i)=>{ ctx.globalAlpha=i/ball.trail.length*.35; drawHeart(t.x,t.y,10+i*.6,0,'#ff8fae',i/ball.trail.length*.4); });
ctx.globalAlpha=1;
// ball as mini heart orb
ctx.save(); ctx.shadowColor='#ff3b6e'; ctx.shadowBlur=16;
const bg=ctx.createRadialGradient(ball.x-3,ball.y-3,1,ball.x,ball.y,ball.r+4); bg.addColorStop(0,'#fff'); bg.addColorStop(.4,'#ff6b9d'); bg.addColorStop(1,'#ff2e63');
ctx.fillStyle=bg; ctx.beginPath(); ctx.arc(ball.x,ball.y,ball.r,0,Math.PI*2); ctx.fill(); ctx.restore();
drawHeart(ball.x,ball.y-1,13,Math.sin(Date.now()/200)*.15,'#ffffff',.95);
// paddle
drawBurger(paddle.x,paddle.y,paddle.w,paddle.h);
// particles on top
for(const p of particles) drawHeart(p.x,p.y,p.s,p.rot,p.color,Math.max(0,p.life));
// floaters
ctx.font='900 16px ui-rounded, system-ui'; ctx.textAlign='center';
for(const f of floaters){ ctx.globalAlpha=Math.max(0,f.life); ctx.fillStyle='#fff'; ctx.strokeStyle='#3d1f2a'; ctx.lineWidth=3; ctx.strokeText(f.txt,f.x,f.y); ctx.fillText(f.txt,f.x,f.y); }
ctx.globalAlpha=1;
if(flash>0){ ctx.fillStyle=`rgba(255,150,180,${flash})`; ctx.fillRect(0,0,W,H); }
// demo label
if(demo){ ctx.fillStyle='rgba(255,255,255,.9)'; ctx.font='800 11px system-ui'; ctx.textAlign='left'; ctx.fillText('🤖 DEMO MODE — click to play',12,H-10); }
if(paused && !demo){ ctx.fillStyle='rgba(0,0,0,.45)'; ctx.fillRect(0,0,W,H); ctx.fillStyle='#fff'; ctx.font='900 34px system-ui'; ctx.textAlign='center'; ctx.fillText('⏸ PAUSED',W/2,H/2); }
ctx.restore();
}
function loop(){ update(); draw(); requestAnimationFrame(loop); }
/* ---------- INPUT ---------- */
function startGame(){
if(started && !demo) return;
demo=false; started=true; overlay.classList.add('hide');
resetGame(); resetBall(); showToast('🍔 GO! Smash for HEARTS 💖');
}
overlay.addEventListener('click',startGame);
document.addEventListener('keydown',e=>{
keys[e.key]=true;
if(!started) startGame();
if(e.key===' '){ e.preventDefault(); if(started){paused=!paused; $('pauseBtn').textContent=paused?'▶ Resume':'⏸ Pause';} }
});
document.addEventListener('keyup',e=>keys[e.key]=false);
function toGameX(clientX){ const r=canvas.getBoundingClientRect(); return (clientX-r.left)/r.width*W; }
canvas.addEventListener('mousemove',e=>{ mouseX=toGameX(e.clientX); if(!started) startGame(); });
canvas.addEventListener('touchmove',e=>{ mouseX=toGameX(e.touches[0].clientX); if(!started) startGame(); e.preventDefault(); },{passive:false});
canvas.addEventListener('touchstart',e=>{ mouseX=toGameX(e.touches[0].clientX); if(!started) startGame(); },{passive:true});
$('startBtn').addEventListener('click',startGame);
$('pauseBtn').addEventListener('click',()=>{ if(!started) return startGame(); paused=!paused; $('pauseBtn').textContent=paused?'▶ Resume':'⏸ Pause'; });
$('resetBtn').addEventListener('click',()=>{ if(!started) startGame(); resetGame(); resetBall(); paused=false; $('pauseBtn').textContent='⏸ Pause'; showToast('↺ Restarted!'); });
/* ambient bg hearts */
const bg=document.getElementById('bg');
for(let i=0;i<14;i++){ const d=document.createElement('div'); d.className='bg-heart'; d.textContent=['💖','❤️','💕','🍔'][i%4]; d.style.left=Math.random()*100+'vw'; d.style.fontSize=(14+Math.random()*22)+'px'; d.style.animationDuration=(9+Math.random()*14)+'s'; d.style.animationDelay=(-Math.random()*14)+'s'; bg.appendChild(d); }
makeBricks(1); updateHUD(); loop();
</script>
</body>
</html>Reasoning trace (657 tokens)
Implementing a single-file canvas breakout with hamburger paddle, heart particles on brick break, demo autopilot, and responsive UI.