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
- 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 • Launch: Space / Click • Pause: P</p>
</div>
</div>
</div>
<div class="bar">
<div class="controlsHint"><b>Mouse</b> or <b>← →</b> move the burger • <b>Space</b> launch • 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.