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
- 39.2 s
- Cost
- $0.011
- Output tokens
- 21,679
- Spent thinking
- 9,307 tokens
- Speed
- 575.6 tok/s
- Console errors
- 0
- First token after
- 1.5 s
- First visible text after
- 24.1 s
- Prompt tokens
- 375
- Code size
- 35.8 KB
- Finish reason
- stop
- Served by
- Meta
- Model id
meta/muse-ember- Reasoning sent
model default- 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 · 875 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 Edition</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
font-family: ui-rounded, "Segoe UI", system-ui, -apple-system, Arial, sans-serif;
background:
radial-gradient(circle at 15% 20%, rgba(255,77,109,.14) 0 120px, transparent 121px),
radial-gradient(circle at 85% 15%, rgba(255,197,61,.22) 0 140px, transparent 141px),
linear-gradient(180deg,#FFF8E7 0%, #FFE5EC 55%, #FFF0D6 100%);
color:#161616;
min-height:100vh;
overflow-x:hidden;
}
.wrap{max-width:1240px;margin:0 auto;padding:16px 16px 22px}
header{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:16px}
.logo h1{margin:0;font-size:clamp(28px,4.2vw,44px);line-height:.95;font-weight:900;letter-spacing:.5px;text-shadow:3px 3px 0 #FFD166}
.logo h1 span{color:#FF2E4D}
.logo p{margin:6px 0 0;font-weight:800;letter-spacing:3px;font-size:12px;color:#7a3b12}
.logo p b{background:#161616;color:#FFD166;padding:3px 8px;border-radius:999px;letter-spacing:2px}
.stats{display:flex;gap:10px;flex-wrap:wrap}
.stat{background:#fff;border:3px solid #161616;border-radius:14px;box-shadow:4px 4px 0 #161616;padding:8px 14px;min-width:98px;text-align:center}
.stat label{display:block;font-size:10px;font-weight:900;letter-spacing:2px;color:#888}
.stat strong{display:block;font-size:23px;line-height:1.1;font-weight:900}
.stat .lives{color:#FF2E4D;font-size:20px;letter-spacing:2px}
main{display:grid;grid-template-columns:1fr 284px;gap:18px;align-items:start}
@media(max-width:920px){main{grid-template-columns:1fr}}
.game-shell{position:relative;background:#fff;border:4px solid #161616;border-radius:22px;box-shadow:8px 8px 0 #161616;overflow:hidden}
canvas{width:100%;display:block;aspect-ratio:820/560;background:linear-gradient(180deg,#FFF8E7,#FFE5EC);touch-action:none;cursor:crosshair}
.badge{position:absolute;top:12px;left:12px;display:flex;align-items:center;gap:7px;background:#161616;color:#fff;font-weight:900;font-size:11px;letter-spacing:1.6px;padding:7px 11px;border-radius:999px;border:2px solid #fff;box-shadow:3px 3px 0 rgba(0,0,0,.25);z-index:5}
.dot{width:9px;height:9px;background:#FF2E4D;border-radius:50%;animation:pulse 1s infinite}
@keyframes pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.5)}}
.badge.manual .dot{background:#2ED573}
#overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(255,248,231,.28);z-index:6;cursor:pointer;padding:16px}
#overlay.hidden{display:none}
.card{background:#fff;border:3px solid #161616;border-radius:18px;box-shadow:6px 6px 0 #161616;padding:18px 22px;max-width:470px;width:92%;text-align:center}
.card h2{margin:0 0 6px;font-size:26px;font-weight:900}
.card h2 span{color:#FF2E4D}
.card p{margin:0 0 12px;font-weight:700;color:#555;line-height:1.4}
.card .big{font-size:13px;letter-spacing:2px;font-weight:900;background:#FFD166;display:inline-block;padding:7px 12px;border:2px solid #161616;border-radius:999px;box-shadow:3px 3px 0 #161616}
.card small{display:block;margin-top:10px;font-weight:800;color:#999;letter-spacing:1px}
.controls{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}
button{font:inherit;font-weight:900;letter-spacing:.5px;background:#FFD166;color:#161616;border:3px solid #161616;border-radius:12px;padding:10px 16px;box-shadow:4px 4px 0 #161616;cursor:pointer;transition:transform .06s, box-shadow .06s}
button:active{transform:translate(3px,3px);box-shadow:1px 1px 0 #161616}
button.red{background:#FF2E4D;color:#fff}
button.white{background:#fff}
button.pink{background:#FF8FAB}
.hint{margin-top:10px;font-weight:800;color:#8a6a4a;font-size:13px;text-align:center}
.hint kbd{background:#161616;color:#fff;padding:2px 7px;border-radius:6px;font-size:11px}
aside{display:flex;flex-direction:column;gap:14px}
.panel{background:#fff;border:3px solid #161616;border-radius:16px;box-shadow:5px 5px 0 #161616;padding:16px}
.panel h3{margin:0 0 10px;font-size:15px;letter-spacing:1.5px;font-weight:900}
.panel p{margin:0 0 10px;line-height:1.5;color:#444;font-weight:600;font-size:14px}
.panel ul{margin:0;padding-left:18px;line-height:1.7;font-weight:700;color:#333;font-size:14px}
.heart-num{font-size:38px;font-weight:900;color:#FF2E4D;line-height:1;text-align:center}
.heart-num small{display:block;font-size:11px;letter-spacing:2px;color:#888;margin-top:4px}
.bar{height:16px;background:#FFE5EC;border:3px solid #161616;border-radius:999px;overflow:hidden;margin-top:10px}
.bar i{display:block;height:100%;width:0%;background:linear-gradient(90deg,#FF2E4D,#FF8FAB,#FFD166);transition:width .2s}
.bar-label{display:flex;justify-content:space-between;font-size:11px;font-weight:900;letter-spacing:1px;color:#888;margin-top:6px}
.pow{display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:2px dashed #FFE5EC;font-weight:800;font-size:14px}
.pow:last-child{border-bottom:none}
.pow .ic{width:34px;height:34px;flex:0 0 34px;display:grid;place-items:center;border:2px solid #161616;border-radius:50%;font-weight:900;color:#fff}
.toast{background:#FFF3CD;border:2px dashed #FFB703;border-radius:10px;padding:9px 10px;font-weight:900;font-size:13px;text-align:center;color:#7a4a00}
footer{text-align:center;margin-top:16px;font-weight:800;color:#a07a5a;font-size:13px;letter-spacing:.5px}
@media(max-width:420px){
.wrap{padding:10px}
.stat{min-width:78px;padding:6px 10px}
.stat strong{font-size:19px}
.card{padding:14px}
.card h2{font-size:21px}
}
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="logo">
<h1>HAMBURGER<br><span>BREAKOUT</span> ♥</h1>
<p><b>HEART-BURST</b> EDITION</p>
</div>
<div class="stats">
<div class="stat"><label>SCORE</label><strong id="score">0</strong></div>
<div class="stat"><label>LEVEL</label><strong id="level">1</strong></div>
<div class="stat"><label>LIVES</label><strong class="lives" id="lives">♥♥♥</strong></div>
<div class="stat"><label>BEST</label><strong id="best">0</strong></div>
</div>
</header>
<main>
<section>
<div class="game-shell">
<canvas id="game" width="820" height="560"></canvas>
<div class="badge" id="badge"><span class="dot"></span><span id="badgeText">AUTO DEMO</span></div>
<div id="overlay">
<div class="card">
<h2 id="ovTitle">DEMO MODE <span>♥</span></h2>
<p id="ovSub">The hamburger is auto-playing! Every brick you break explodes into tons of little hearts.</p>
<span class="big" id="ovBtn">▶ CLICK OR PRESS A KEY TO START</span>
<small>MOUSE / TOUCH / ← → TO MOVE • SPACE TO LAUNCH</small>
</div>
</div>
</div>
<div class="controls">
<button class="red" id="btnPause">⏸ PAUSE</button>
<button id="btnLaunch">🚀 LAUNCH BALL</button>
<button class="white" id="btnRestart">↺ RESTART</button>
<button class="pink" id="btnSound">🔊 SOUND ON</button>
</div>
<div class="hint"><kbd>←</kbd> <kbd>→</kbd> or mouse to move the burger <kbd>SPACE</kbd> launch <kbd>P</kbd> pause Catch <span style="color:#FF2E4D">golden hearts</span> for power-ups!</div>
</section>
<aside>
<div class="panel">
<h3>💖 HEART BURST METER</h3>
<div class="heart-num"><span id="heartsCount">0</span><small>LITTLE HEARTS EXPLODED</small></div>
<div class="bar"><i id="progFill"></i></div>
<div class="bar-label"><span>BRICKS BROKEN</span><span id="progText">0%</span></div>
</div>
<div class="panel">
<h3>🍔 HOW TO PLAY</h3>
<ul>
<li>Move the <b>hamburger paddle</b> to bounce the cherry ball.</li>
<li>Break every brick to clear the level.</li>
<li>Each brick <b style="color:#FF2E4D">explodes into 30+ hearts</b> — chain breaks for combo points!</li>
<li>Don't let the ball fall. You have 3 lives.</li>
</ul>
</div>
<div class="panel">
<h3>✨ GOLDEN HEART POWER-UPS</h3>
<div class="pow"><span class="ic" style="background:#FF9F1A">W</span> BIG BURGER — wider paddle</div>
<div class="pow"><span class="ic" style="background:#4D96FF">S</span> SLOW KETCHUP — slower ball</div>
<div class="pow"><span class="ic" style="background:#FF2E4D">♥</span> EXTRA LIFE — one more heart</div>
<div class="pow"><span class="ic" style="background:#9B5DE5">★</span> BONUS — instant +250 pts</div>
</div>
<div class="panel">
<h3>📣 LAST POWER-UP</h3>
<div class="toast" id="toast">Catch a golden heart to power up!</div>
</div>
</aside>
</main>
<footer>Every brick = a burst of love. Made with canvas, hearts & 100% beef. ♥</footer>
</div>
<script>
(() => {
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const W = 820, H = 560;
let dpr = 1;
const scoreEl = document.getElementById('score');
const levelEl = document.getElementById('level');
const livesEl = document.getElementById('lives');
const bestEl = document.getElementById('best');
const heartsEl = document.getElementById('heartsCount');
const progFill = document.getElementById('progFill');
const progText = document.getElementById('progText');
const overlay = document.getElementById('overlay');
const ovTitle = document.getElementById('ovTitle');
const ovSub = document.getElementById('ovSub');
const ovBtn = document.getElementById('ovBtn');
const badge = document.getElementById('badge');
const badgeText = document.getElementById('badgeText');
const toast = document.getElementById('toast');
function resize(){
dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = W * dpr;
canvas.height = H * dpr;
}
resize();
window.addEventListener('resize', resize);
// ---------- Audio ----------
let audioCtx = null, muted = false;
function ensureAudio(){
if(!audioCtx){ try{ audioCtx = new (window.AudioContext||window.webkitAudioContext)(); }catch(e){} }
if(audioCtx && audioCtx.state === 'suspended') audioCtx.resume();
}
function blip(f, dur=0.09, type='triangle', vol=0.07){
if(muted || !audioCtx) return;
try{
const o = audioCtx.createOscillator(), g = audioCtx.createGain();
o.type = type; o.frequency.value = f;
g.gain.setValueAtTime(vol, audioCtx.currentTime);
g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + dur);
o.connect(g); g.connect(audioCtx.destination);
o.start(); o.stop(audioCtx.currentTime + dur);
}catch(e){}
}
// ---------- Helpers ----------
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 HEART_COLORS = ['#FF2E4D','#FF4D6D','#FF6B9D','#FF8FAB','#FFB3C1','#FFD166','#FF3B30'];
function drawHeart(x,y,size,color,alpha,rot){
ctx.save();
ctx.translate(x,y); ctx.rotate(rot||0);
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(0, size*0.45);
ctx.bezierCurveTo(-size*0.95, -size*0.25, -size*0.5, -size*0.95, 0, -size*0.35);
ctx.bezierCurveTo( size*0.5, -size*0.95, size*0.95, -size*0.25, 0, size*0.45);
ctx.closePath(); ctx.fill();
ctx.restore();
}
const rand = (a,b)=>a+Math.random()*(b-a);
const clamp = (v,a,b)=>Math.max(a,Math.min(b,v));
const pick = arr => arr[Math.floor(Math.random()*arr.length)];
// ---------- Game State ----------
let demo = true;
let paused = false;
let state = 'serve'; // serve | playing | over | levelclear
let score = 0, best = 0, lives = 3, level = 1;
let combo = 1, comboTimer = 0;
let heartsBurst = 0;
let shake = 0, time = 0;
let stuckTimer = 1, overTimer = 0, levelTimer = 0;
let speedBonus = 0;
let wideTimer = 0, slowTimer = 0;
let overlayMode = 'demo';
const paddle = { x: W/2-56, y: H-42, w: 112, h: 40, baseW: 112, squash: 0 };
const ball = { x: W/2, y: paddle.y-12, vx:0, vy:0, r: 10, stuck:true };
let bricks = [], particles = [], floaters = [], powerups = [];
let totalBricks = 0;
let mouseTargetX = W/2, lastInput = 'mouse', useMouse = false;
const keys = {};
let trailTimer = 0;
const ROW_COLORS = ['#FF4D6D','#FF8A3D','#FFC53D','#6BCB77','#4D96FF','#B983FF','#FF2E8A'];
function buildLevel(lvl){
bricks = [];
const cols = 10;
const rows = lvl === 1 ? 5 : Math.min(5 + Math.floor((lvl-1)/2), 6);
const gap = 6, marginX = 30, marginY = 64;
const bw = (W - marginX*2 - gap*(cols-1)) / cols;
const bh = 22;
for(let r=0;r<rows;r++){
for(let c=0;c<cols;c++){
let include = true;
if(lvl % 3 === 2){
// diamond-ish
if(r===0 && (c<1||c>8)) include = false;
if(r===rows-1 && (c<1||c>8)) include = false;
if(c===0 && r>0 && r<rows-1 && (r+c)%2===0) include = false;
} else if(lvl % 3 === 0 && lvl > 2){
if(c % 3 === 2 && r % 2 === 1) include = false;
}
if(!include) continue;
const hp = (lvl>=3 && r===0) ? 2 : (lvl>=4 && Math.random()<0.18 ? 2 : 1);
bricks.push({
x: marginX + c*(bw+gap),
y: marginY + r*(bh+gap),
w: bw, h: bh,
color: ROW_COLORS[r % ROW_COLORS.length],
hp, maxHp: hp, alive:true
});
}
}
totalBricks = bricks.length;
}
function currentSpeed(){
let s = 345 + (level-1)*28;
s = Math.min(s, 545) + speedBonus;
if(slowTimer > 0) s *= 0.62;
return s;
}
function normalizeBall(){
const m = Math.hypot(ball.vx, ball.vy) || 1;
const s = currentSpeed();
ball.vx = ball.vx/m*s; ball.vy = ball.vy/m*s;
}
function resetBallToPaddle(){
ball.x = paddle.x + paddle.w/2;
ball.y = paddle.y - ball.r - 2;
ball.vx = 0; ball.vy = 0; ball.stuck = true; state = 'serve';
}
function launch(){
if(!ball.stuck) return;
const ang = rand(-0.38, 0.38);
const s = currentSpeed();
ball.vx = Math.sin(ang)*s;
ball.vy = -Math.cos(ang)*s;
ball.stuck = false; state = 'playing';
blip(520, .12, 'square', .06);
}
function resetGame(){
score = 0; lives = 3; level = 1; combo = 1; comboTimer = 0;
heartsBurst = 0; speedBonus = 0; wideTimer = 0; slowTimer = 0;
paddle.w = paddle.baseW; paddle.x = W/2 - paddle.w/2;
particles = []; floaters = []; powerups = [];
buildLevel(1);
resetBallToPaddle();
stuckTimer = demo ? 0.9 : 999;
updateHUD();
}
function nextLevel(){
level++;
buildLevel(level);
speedBonus = 0;
resetBallToPaddle();
stuckTimer = demo ? 0.8 : 999;
addFloater(W/2, 250, 'LEVEL ' + level + '!', '#FF2E4D', 30);
updateHUD();
}
// ---------- Particles ----------
function explode(x,y,baseColor,count=30,power=1){
for(let i=0;i<count;i++){
const a = Math.random()*Math.PI*2;
const sp = rand(35, 260)*power;
particles.push({
type:'heart', x, y,
vx: Math.cos(a)*sp, vy: Math.sin(a)*sp - 55,
size: rand(4,13), rot: rand(-.6,.6), vr: rand(-3,3),
color: Math.random()<0.35 ? baseColor : pick(HEART_COLORS),
life:0, max: rand(0.95, 2.1), sway: rand(0, Math.PI*2)
});
}
for(let i=0;i<8;i++){
const a = Math.random()*Math.PI*2, sp = rand(60,200)*power;
particles.push({type:'dot', x,y, vx:Math.cos(a)*sp, vy:Math.sin(a)*sp, size:rand(2,4), rot:0, vr:0, color:'#FFD166', life:0, max:rand(.4,.8), sway:0});
}
heartsBurst += count;
if(particles.length > 650) particles.splice(0, particles.length-650);
}
function addFloater(x,y,text,color='#161616',size=18){
floaters.push({x,y,text,color,size,life:0,max:1.15});
}
// ---------- Powerups ----------
function maybeSpawnPowerup(x,y){
if(Math.random() > 0.20) return;
let type;
const r = Math.random();
if(r < 0.42) type='wide';
else if(r < 0.74) type='slow';
else if(r < 0.86 && lives < 5) type='life';
else type='points';
powerups.push({x,y,vy:135,type,rot:0,sway:rand(0,6)});
}
function applyPowerup(p){
ensureAudio();
if(p.type==='wide'){
paddle.w = 160; wideTimer = 12;
toast.textContent = '🍔 BIG BURGER! Wider paddle for 12s';
addFloater(paddle.x+paddle.w/2, paddle.y-18, 'WIDE!', '#FF9F1A', 20);
blip(700,.1,'square',.07); setTimeout(()=>blip(900,.12,'square',.07),90);
} else if(p.type==='slow'){
slowTimer = 8; normalizeBall();
toast.textContent = '🍅 SLOW KETCHUP! Ball slowed for 8s';
addFloater(paddle.x+paddle.w/2, paddle.y-18, 'SLOW!', '#4D96FF', 20);
blip(440,.12,'sine',.08);
} else if(p.type==='life'){
lives = Math.min(5, lives+1);
toast.textContent = '♥ EXTRA LIFE! +1 heart';
addFloater(paddle.x+paddle.w/2, paddle.y-18, '+1 ♥', '#FF2E4D', 22);
blip(880,.12,'triangle',.08); setTimeout(()=>blip(1100,.14,'triangle',.08),90);
} else {
score += 250;
toast.textContent = '★ BONUS! +250 points';
addFloater(paddle.x+paddle.w/2, paddle.y-18, '+250', '#9B5DE5', 20);
blip(990,.1,'square',.06);
}
explode(p.x, p.y, '#FFD166', 16, 0.7);
updateHUD();
}
// ---------- HUD / Overlay ----------
function updateHUD(){
scoreEl.textContent = score;
levelEl.textContent = level;
livesEl.textContent = '♥'.repeat(Math.max(0,lives)) + '♡'.repeat(Math.max(0, 3-lives));
if(score > best){ best = score; }
bestEl.textContent = best;
heartsEl.textContent = heartsBurst;
const alive = bricks.filter(b=>b.alive).length;
const pct = totalBricks ? Math.round((totalBricks-alive)/totalBricks*100) : 0;
progFill.style.width = pct + '%';
progText.textContent = pct + '%';
}
function showOverlay(mode, title, sub, btn){
overlayMode = mode;
ovTitle.innerHTML = title;
ovSub.textContent = sub;
ovBtn.textContent = btn;
overlay.classList.remove('hidden');
}
function hideOverlay(){ overlay.classList.add('hidden'); overlayMode='hidden'; }
function setDemoUI(){
badge.classList.toggle('manual', !demo);
badgeText.textContent = demo ? 'AUTO DEMO' : 'YOU ARE PLAYING';
}
function startManual(){
if(!demo && overlayMode==='hidden') return;
ensureAudio();
demo = false; paused = false;
setDemoUI(); hideOverlay();
resetGame();
addFloater(W/2, 300, 'GO! BREAK THOSE BRICKS ♥', '#FF2E4D', 22);
}
// ---------- Input ----------
function canvasX(e){
const r = canvas.getBoundingClientRect();
return (e.clientX - r.left) * (W / r.width);
}
canvas.addEventListener('pointermove', e=>{
mouseTargetX = canvasX(e); lastInput='mouse'; useMouse=true;
});
canvas.addEventListener('pointerdown', e=>{
ensureAudio();
mouseTargetX = canvasX(e); lastInput='mouse'; useMouse=true;
if(demo){ startManual(); return; }
if(overlayMode==='demo'){ startManual(); return; }
if(overlayMode==='over'){ startManual(); return; }
if(overlayMode==='pause'){ paused=false; hideOverlay(); return; }
if(ball.stuck) launch();
});
window.addEventListener('keydown', e=>{
const k = e.key.toLowerCase();
if(['arrowleft','arrowright',' '].includes(k) || e.key===' ') e.preventDefault();
keys[k]=true; keys[e.key]=true;
if(e.key==='ArrowLeft'||e.key==='ArrowRight'||k==='a'||k==='d') lastInput='keys';
if(demo){ startManual(); return; }
if(overlayMode==='over'){ startManual(); return; }
if(overlayMode==='pause' && (k==='p'||e.key===' ')){ paused=false; hideOverlay(); return; }
if(e.key===' '){ if(ball.stuck) launch(); }
if(k==='p'){ togglePause(); }
if(e.key==='Enter' && overlayMode!=='hidden'){ startManual(); }
});
window.addEventListener('keyup', e=>{ keys[e.key.toLowerCase()]=false; keys[e.key]=false; });
overlay.addEventListener('click', ()=>{
ensureAudio();
if(overlayMode==='demo'||overlayMode==='over') startManual();
else if(overlayMode==='pause'){ paused=false; hideOverlay(); }
});
function togglePause(){
if(demo) return;
paused = !paused;
document.getElementById('btnPause').textContent = paused ? '▶ RESUME' : '⏸ PAUSE';
if(paused) showOverlay('pause','PAUSED ⏸','Take a breath. The burger is waiting.','▶ CLICK TO RESUME');
else hideOverlay();
}
document.getElementById('btnPause').addEventListener('click', ()=>{ ensureAudio(); togglePause(); });
document.getElementById('btnLaunch').addEventListener('click', ()=>{
ensureAudio();
if(demo){ startManual(); return; }
if(ball.stuck) launch();
});
document.getElementById('btnRestart').addEventListener('click', ()=>{
ensureAudio();
if(demo){ startManual(); return; }
paused=false; document.getElementById('btnPause').textContent='⏸ PAUSE';
hideOverlay(); resetGame();
});
document.getElementById('btnSound').addEventListener('click', function(){
muted = !muted;
this.textContent = muted ? '🔇 SOUND OFF' : '🔊 SOUND ON';
if(!muted) ensureAudio();
});
// ---------- Update ----------
function update(dt){
time += dt;
// timers
if(comboTimer>0){ comboTimer-=dt; if(comboTimer<=0) combo=1; }
if(wideTimer>0){ wideTimer-=dt; if(wideTimer<=0) paddle.w=paddle.baseW; }
if(slowTimer>0){ slowTimer-=dt; if(slowTimer<=0) normalizeBall(); }
shake = Math.max(0, shake - dt*14);
paddle.squash = Math.max(0, paddle.squash - dt*6);
// paddle control
const center = paddle.x + paddle.w/2;
if(demo){
let target;
if(ball.stuck) target = W/2 + Math.sin(time*0.9)*85;
else if(ball.vy > 0) target = ball.x + ball.vx*0.16;
else target = ball.x*0.65 + W*0.17;
target += Math.sin(time*2.2)*5;
const maxStep = 540*dt;
const delta = clamp(target-center, -maxStep, maxStep);
paddle.x += delta;
} else {
let moved = false;
const spd = 560;
if(keys['arrowleft']||keys['a']){ paddle.x -= spd*dt; moved=true; }
if(keys['arrowright']||keys['d']){ paddle.x += spd*dt; moved=true; }
if(moved){ mouseTargetX = paddle.x + paddle.w/2; }
else if(useMouse){
const desired = clamp(mouseTargetX - paddle.w/2, 6, W-paddle.w-6);
paddle.x += (desired - paddle.x) * Math.min(1, dt*20);
}
}
paddle.x = clamp(paddle.x, 6, W-paddle.w-6);
// serve / state timers
if(ball.stuck){
ball.x = paddle.x + paddle.w/2;
ball.y = paddle.y - ball.r - 2;
if(demo){ stuckTimer-=dt; if(stuckTimer<=0) launch(); }
}
if(state==='over'){
overTimer-=dt;
if(overTimer<=0){
if(demo){ resetGame(); }
else showOverlay('over', 'GAME OVER <span>♥</span>', 'Final score: ' + score + ' • Hearts burst: ' + heartsBurst + '. The burger believes in you!', '↺ CLICK TO PLAY AGAIN');
}
}
if(state==='levelclear'){
levelTimer-=dt;
if(levelTimer<=0) nextLevel();
}
// ball physics
if(!ball.stuck && state==='playing'){
const prevX = ball.x, prevY = ball.y;
ball.x += ball.vx*dt;
ball.y += ball.vy*dt;
// trail hearts
trailTimer -= dt;
if(trailTimer<=0){
trailTimer = 0.07;
particles.push({type:'heart', x:ball.x+rand(-4,4), y:ball.y+rand(-4,4), vx:rand(-12,12), vy:rand(8,28), size:rand(3,6), rot:rand(-.4,.4), vr:rand(-1,1), color:pick(HEART_COLORS), life:0, max:.55, sway:rand(0,6)});
}
// walls
if(ball.x < ball.r){ ball.x=ball.r; ball.vx=Math.abs(ball.vx); blip(220,.05,'square',.04); }
if(ball.x > W-ball.r){ ball.x=W-ball.r; ball.vx=-Math.abs(ball.vx); blip(220,.05,'square',.04); }
if(ball.y < ball.r){ ball.y=ball.r; ball.vy=Math.abs(ball.vy); blip(220,.05,'square',.04); }
// paddle
if(ball.vy>0 &&
ball.y+ball.r >= paddle.y &&
ball.y < paddle.y+paddle.h+6 &&
ball.x >= paddle.x-ball.r && ball.x <= paddle.x+paddle.w+ball.r){
const rel = clamp((ball.x - (paddle.x+paddle.w/2)) / (paddle.w/2), -1, 1);
const ang = rel * (Math.PI/3.1);
const s = currentSpeed();
ball.vx = Math.sin(ang)*s;
ball.vy = -Math.cos(ang)*s;
ball.y = paddle.y - ball.r;
paddle.squash = 1;
blip(320,.08,'square',.06);
explode(ball.x, paddle.y, '#FFD166', 5, 0.35);
}
// bricks
for(const b of bricks){
if(!b.alive) 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) continue;
// determine side from previous position
const fromTop = prevY + ball.r <= b.y + 4;
const fromBottom = prevY - ball.r >= b.y + b.h - 4;
const fromLeft = prevX + ball.r <= b.x + 4;
const fromRight = prevX - ball.r >= b.x + b.w - 4;
if(fromTop||fromBottom) ball.vy = -ball.vy;
else if(fromLeft||fromRight) ball.vx = -ball.vx;
else ball.vy = -ball.vy;
b.hp--;
speedBonus = Math.min(55, speedBonus+2.2);
normalizeBall();
if(b.hp > 0){
explode(b.x+b.w/2, b.y+b.h/2, b.color, 10, 0.5);
addFloater(b.x+b.w/2, b.y, 'CRACK!', '#161616', 13);
blip(300,.07,'square',.06);
} else {
b.alive = false;
// combo & score
if(comboTimer>0) combo++; else combo=1;
comboTimer = 2;
const pts = 50 * combo;
score += pts;
explode(b.x+b.w/2, b.y+b.h/2, b.color, 30, 1);
addFloater(b.x+b.w/2, b.y+b.h/2, '+'+pts, '#FF2E4D', combo>=3?20:16);
if(combo>=3) addFloater(W/2, 130, 'COMBO x'+combo+' ♥', '#FF2E4D', 24);
shake = Math.min(7, 3 + combo*0.4);
blip(rand(520,900), .13, 'triangle', .09);
maybeSpawnPowerup(b.x+b.w/2, b.y+b.h/2);
}
updateHUD();
break;
}
// lose
if(ball.y - ball.r > H+10){
lives--;
explode(ball.x, H-30, '#999999', 22, 0.8);
addFloater(W/2, H-120, 'OUCH! -1 ♥', '#161616', 24);
blip(140,.3,'sawtooth',.08);
shake = 8;
updateHUD();
if(lives<=0){ state='over'; overTimer=2.0; ball.stuck=true; }
else { resetBallToPaddle(); stuckTimer = demo?0.9:999; }
}
// level clear
if(state==='playing' && bricks.every(b=>!b.alive)){
state='levelclear'; levelTimer=1.6;
score += 500*level;
explode(W/2, 200, '#FF2E4D', 70, 1.4);
explode(W/2-120, 260, '#FFD166', 40, 1.1);
explode(W/2+120, 260, '#FF8FAB', 40, 1.1);
addFloater(W/2, 300, 'LEVEL CLEAR! +'+(500*level), '#FF2E4D', 28);
blip(660,.12,'square',.07); setTimeout(()=>blip(880,.12,'square',.07),110); setTimeout(()=>blip(1100,.18,'square',.07),220);
updateHUD();
}
}
// powerups
for(let i=powerups.length-1;i>=0;i--){
const p = powerups[i];
p.y += p.vy*dt; p.x += Math.sin(time*3+p.sway)*28*dt; p.rot += dt*2;
if(p.y > paddle.y && p.y < paddle.y+paddle.h+14 && p.x > paddle.x-10 && p.x < paddle.x+paddle.w+10){
applyPowerup(p); powerups.splice(i,1); continue;
}
if(p.y > H+20) powerups.splice(i,1);
}
// particles
for(let i=particles.length-1;i>=0;i--){
const p = particles[i];
p.life += dt;
if(p.life>=p.max){ particles.splice(i,1); continue; }
p.vy -= 26*dt; // float up
p.vx *= (1 - 1.4*dt); p.vy *= (1 - 0.6*dt);
p.x += p.vx*dt + Math.sin(p.life*6 + p.sway)*22*dt;
p.y += p.vy*dt;
p.rot += p.vr*dt;
}
// floaters
for(let i=floaters.length-1;i>=0;i--){
const f = floaters[i];
f.life += dt; f.y -= 42*dt;
if(f.life>=f.max) floaters.splice(i,1);
}
updateHUD();
}
// ---------- Draw ----------
function drawHamburger(){
const x = paddle.x, y = paddle.y, w = paddle.w, h = paddle.h;
const squashScale = 1 - paddle.squash*0.18;
ctx.save();
ctx.translate(x+w/2, y+h/2);
ctx.scale(1 + paddle.squash*0.08, squashScale);
ctx.translate(-(x+w/2), -(y+h/2));
// shadow
ctx.fillStyle='rgba(0,0,0,.16)';
ctx.beginPath(); ctx.ellipse(x+w/2, y+h+7, w*0.46, 6, 0, 0, Math.PI*2); ctx.fill();
ctx.lineWidth=2; ctx.strokeStyle='#C47A12';
// bottom bun
rr(x+5, y+h-12, w-10, 12, 6); ctx.fillStyle='#F4B860'; ctx.fill(); ctx.stroke();
// patty
rr(x+2, y+h-21, w-4, 10, 5); ctx.fillStyle='#7B3F1D'; ctx.fill(); ctx.strokeStyle='#5A2A0E'; ctx.stroke();
// cheese with drips
ctx.beginPath();
ctx.moveTo(x+5, y+h-27); ctx.lineTo(x+w-5, y+h-27); ctx.lineTo(x+w-5, y+h-21);
ctx.lineTo(x+w*0.78, y+h-21); ctx.lineTo(x+w*0.72, y+h-16); ctx.lineTo(x+w*0.62, y+h-21);
ctx.lineTo(x+w*0.38, y+h-21); ctx.lineTo(x+w*0.32, y+h-15); ctx.lineTo(x+w*0.22, y+h-21);
ctx.lineTo(x+5, y+h-21); ctx.closePath();
ctx.fillStyle='#FFD166'; ctx.fill(); ctx.strokeStyle='#E8A317'; ctx.stroke();
// tomato
rr(x+6, y+h-32, w-12, 6, 3); ctx.fillStyle='#FF5964'; ctx.fill(); ctx.strokeStyle='#D92B36'; ctx.stroke();
// lettuce wavy
ctx.beginPath();
ctx.moveTo(x+4, y+h-31);
for(let i=0;i<=6;i++){
const lx = x+4 + (w-8)*i/6;
ctx.quadraticCurveTo(lx+ (w-8)/12, y+h-38, lx + (w-8)/6, y+h-31);
}
ctx.lineTo(x+w-4, y+h-27); ctx.lineTo(x+4, y+h-27); ctx.closePath();
ctx.fillStyle='#6BCB77'; ctx.fill(); ctx.strokeStyle='#3A9A4A'; ctx.stroke();
// top bun dome
ctx.beginPath();
ctx.moveTo(x+3, y+17);
ctx.bezierCurveTo(x+3, y-4, x+w*0.3, y-6, x+w/2, y-6);
ctx.bezierCurveTo(x+w*0.7, y-6, x+w-3, y-4, x+w-3, y+17);
ctx.closePath();
ctx.fillStyle='#FFD07A'; ctx.fill(); ctx.strokeStyle='#D99A2B'; ctx.lineWidth=2.2; ctx.stroke();
// sesame seeds
ctx.fillStyle='#FFF3D6';
const seeds=[[0.22,0.18,-.4],[0.78,0.16,.5],[0.14,0.42,.2],[0.84,0.44,-.3],[0.62,0.08,.1]];
for(const [sx,sy,rot] of seeds){
ctx.save(); ctx.translate(x+w*sx, y+16*sy); ctx.rotate(rot);
ctx.beginPath(); ctx.ellipse(0,0,4.5,2.4,0,0,Math.PI*2); ctx.fill(); ctx.restore();
}
// kawaii face
ctx.fillStyle='#161616';
ctx.beginPath(); ctx.arc(x+w*0.42, y+9, 2.6, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x+w*0.58, y+9, 2.6, 0, Math.PI*2); ctx.fill();
ctx.strokeStyle='#161616'; ctx.lineWidth=1.8;
ctx.beginPath(); ctx.arc(x+w/2, y+10, 5, 0.15*Math.PI, 0.85*Math.PI); ctx.stroke();
ctx.fillStyle='rgba(255,77,109,.5)';
ctx.beginPath(); ctx.arc(x+w*0.36, y+13, 3.5, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x+w*0.64, y+13, 3.5, 0, Math.PI*2); ctx.fill();
ctx.restore();
}
function drawBall(){
const x=ball.x, y=ball.y, r=ball.r;
// shadow
ctx.fillStyle='rgba(0,0,0,.12)';
ctx.beginPath(); ctx.ellipse(x, y+r+3, r*0.8, 3, 0, 0, Math.PI*2); ctx.fill();
// cherry tomato body
const g = ctx.createRadialGradient(x-3,y-3,2,x,y,r+2);
g.addColorStop(0,'#FF8A8A'); g.addColorStop(0.55,'#FF2E4D'); g.addColorStop(1,'#C9183A');
ctx.fillStyle=g; ctx.beginPath(); ctx.arc(x,y,r,0,Math.PI*2); ctx.fill();
ctx.strokeStyle='#8F0F2A'; ctx.lineWidth=1.6; ctx.stroke();
// stem
ctx.strokeStyle='#2E7D32'; ctx.lineWidth=2.4; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(x,y-r+2); ctx.quadraticCurveTo(x+3,y-r-5,x+7,y-r-4); ctx.stroke();
ctx.fillStyle='#6BCB77';
ctx.beginPath(); ctx.ellipse(x+8,y-r-3,5,2.4,-.5,0,Math.PI*2); ctx.fill();
// shine
ctx.fillStyle='rgba(255,255,255,.75)';
ctx.beginPath(); ctx.ellipse(x-3.5,y-3.5,3.2,2, -0.6,0,Math.PI*2); ctx.fill();
// cute face on ball too
ctx.fillStyle='#161616';
ctx.beginPath(); ctx.arc(x-2.6,y+0.5,1.1,0,Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x+2.6,y+0.5,1.1,0,Math.PI*2); ctx.fill();
}
function draw(){
ctx.setTransform(dpr,0,0,dpr,0,0);
ctx.clearRect(0,0,W,H);
ctx.save();
if(shake>0) ctx.translate(rand(-shake,shake), rand(-shake,shake));
// background
const bg = ctx.createLinearGradient(0,0,0,H);
bg.addColorStop(0,'#FFF8E7'); bg.addColorStop(0.55,'#FFE5EC'); bg.addColorStop(1,'#FFD6E0');
ctx.fillStyle=bg; ctx.fillRect(-10,-10,W+20,H+20);
// polka dots
ctx.fillStyle='rgba(255,77,109,.07)';
for(let y=28;y<H;y+=52) for(let x=((y/52)%2? 24:52); x<W; x+=64){ ctx.beginPath(); ctx.arc(x,y,9,0,Math.PI*2); ctx.fill(); }
// decorative hearts floating in bg
for(let i=0;i<6;i++){
const bx = 70 + i*135, by = 36 + Math.sin(time*0.8+i)*5;
drawHeart(bx,by,9,'#FF8FAB',0.28, Math.sin(time+i)*0.15);
}
// table line
ctx.fillStyle='#FFD166'; ctx.fillRect(0,H-22,W,22);
ctx.fillStyle='rgba(0,0,0,.08)'; ctx.fillRect(0,H-22,W,3);
// bricks
for(const b of bricks){
if(!b.alive) continue;
ctx.save();
rr(b.x,b.y,b.w,b.h,6);
ctx.fillStyle=b.color; ctx.fill();
ctx.lineWidth=2; ctx.strokeStyle='rgba(0,0,0,.75)'; ctx.stroke();
// highlight
ctx.fillStyle='rgba(255,255,255,.35)'; rr(b.x+3,b.y+3,b.w-6,7,4); ctx.fill();
// heart emblem
drawHeart(b.x+b.w/2, b.y+b.h/2+1, 6.5, '#FFFFFF', b.hp>1?0.95:0.45, 0);
if(b.hp>1){
ctx.strokeStyle='rgba(0,0,0,.55)'; ctx.lineWidth=1.4;
ctx.beginPath(); ctx.moveTo(b.x+8,b.y+4); ctx.lineTo(b.x+16,b.y+16); ctx.moveTo(b.x+b.w-10,b.y+5); ctx.lineTo(b.x+b.w-18,b.y+17); ctx.stroke();
}
ctx.restore();
}
// powerups
for(const p of powerups){
const col = p.type==='wide'?'#FF9F1A':p.type==='slow'?'#4D96FF':p.type==='life'?'#FF2E4D':'#9B5DE5';
const label = p.type==='wide'?'W':p.type==='slow'?'S':p.type==='life'?'♥':'★';
ctx.save();
ctx.translate(p.x,p.y);
ctx.shadowColor='rgba(0,0,0,.25)'; ctx.shadowBlur=6; ctx.shadowOffsetY=3;
drawHeart(0,0,15,col,1,p.rot*0.25);
ctx.shadowBlur=0;
ctx.fillStyle='#fff'; ctx.font='900 13px Arial'; ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.fillText(label,0,0);
ctx.restore();
}
// particles (hearts)
for(const p of particles){
const a = 1 - p.life/p.max;
if(p.type==='heart'){
const s = p.size * (0.7 + 0.3*a);
drawHeart(p.x,p.y,s,p.color, a, p.rot);
} else {
ctx.globalAlpha=a; ctx.fillStyle=p.color;
ctx.beginPath(); ctx.arc(p.x,p.y,p.size,0,Math.PI*2); ctx.fill(); ctx.globalAlpha=1;
}
}
drawHamburger();
drawBall();
// wide / slow indicators
if(wideTimer>0 || slowTimer>0){
ctx.font='900 11px Arial'; ctx.textAlign='left';
let tx=10, ty=H-34;
if(wideTimer>0){ ctx.fillStyle='#B85C00'; ctx.fillText('WIDE '+Math.ceil(wideTimer)+'s',tx,ty); ty-=14; }
if(slowTimer>0){ ctx.fillStyle='#2456A6'; ctx.fillText('SLOW '+Math.ceil(slowTimer)+'s',tx,ty); }
}
if(combo>1 && comboTimer>0){
ctx.font='900 16px Arial'; ctx.textAlign='right';
ctx.fillStyle='#FF2E4D'; ctx.strokeStyle='#fff'; ctx.lineWidth=4;
const txt='COMBO x'+combo+' ♥';
ctx.strokeText(txt,W-12,26); ctx.fillText(txt,W-12,26);
}
// floaters
for(const f of floaters){
const a = 1 - f.life/f.max;
ctx.globalAlpha=a;
ctx.font='900 '+f.size+'px Arial'; ctx.textAlign='center';
ctx.lineWidth=5; ctx.strokeStyle='#fff'; ctx.strokeText(f.text,f.x,f.y);
ctx.fillStyle=f.color; ctx.fillText(f.text,f.x,f.y);
ctx.globalAlpha=1;
}
// stuck hint
if(ball.stuck && state==='serve'){
ctx.font='800 13px Arial'; ctx.textAlign='center';
ctx.fillStyle='rgba(22,22,22,.55)';
ctx.fillText(demo ? 'launching…' : 'click or press SPACE to launch', W/2, paddle.y-42);
}
if(paused){
ctx.fillStyle='rgba(255,248,231,.55)'; ctx.fillRect(0,0,W,H);
}
ctx.restore();
}
// ---------- Main Loop ----------
let last = performance.now();
function loop(now){
const dt = Math.min(0.033, (now-last)/1000 || 0.016);
last = now;
if(!paused) update(dt);
draw();
requestAnimationFrame(loop);
}
resetGame();
setDemoUI();
showOverlay('demo','DEMO MODE <span>♥</span>','The hamburger is auto-playing! Every brick you break explodes into tons of little hearts.','▶ CLICK OR PRESS A KEY TO START');
requestAnimationFrame(loop);
})();
</script>
</body>
</html>Reasoning trace (9,307 tokens)
Designing a single-file responsive canvas breakout with hamburger paddle and heart particle effects, plus click-to-start AI demo mode.Adding cherry ball sprite, heart particles, screen shake, and AI demo with manual input switching.Extending the canvas implementation with responsive layout, level patterns, paddle rendering, particle effects, powerups, and audio handling.Detailing the HTML layout, canvas sizing, game state, collision logic, and hamburger paddle rendering.Adding cherry tomato ball rendering, heart particles, screen shake, audio, and input handling.Adding overlay click handling, responsive layout tweaks, and paddle-ball collision logic with previous-position checks.Implementing brick collision response with damage, single-hit guard, particle explosions, and powerup timers.Adding overlay state handling, demo badge, side panel UI, audio, combo scoring, and particle limits.Extending UI styling with chunky borders, shadows, and controls plus adding detailed canvas drawing helpers and touch handling.Implementing level generation, powerups, particles, state timers, and input handling for the game loop.