Hamburger breakoutOne-line
MiniMax M3
by MiniMaxreasoning defaultClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 1 min 12 s
- Cost
- $0.0066
- Output tokens
- 6,788
- Spent thinking
- 0 tokens
- Speed
- 98.3 tok/s
- Console errors
- 0
- First token after
- 2.7 s
- First visible text after
- 2.7 s
- Prompt tokens
- 504
- Code size
- 19.7 KB
- Finish reason
- stop
- Served by
- GMICloud
- Model id
minimax/minimax-m3- Reasoning sent
model default- Max output
- 512,000 tokens
- Generated
- Nov 13, 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 · 695 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Heart Burger Breakout</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #130719;
font-family: "Trebuchet MS", Arial, sans-serif;
color: #fff;
}
#game {
position: relative;
width: 100%;
height: 100%;
min-height: 560px;
display: flex;
justify-content: center;
align-items: center;
background:
radial-gradient(circle at 50% -20%, #5e1a6b 0%, #2a0d3c 45%, #130719 100%);
}
canvas {
display: block;
max-width: 100%;
max-height: 100%;
touch-action: none;
box-shadow: 0 0 60px rgba(255, 70, 150, .22);
}
#overlay {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
pointer-events: none;
background: radial-gradient(circle, rgba(20,5,30,.42), rgba(20,5,30,.08));
transition: opacity .25s;
}
#overlay.hidden { opacity: 0; }
h1 {
font-size: clamp(30px, 6vw, 66px);
line-height: .95;
letter-spacing: -2px;
color: #fff5fa;
text-shadow: 0 5px 0 #a71961, 0 8px 24px #ff4d9d;
transform: rotate(-2deg);
}
h1 span { color: #ff77b4; }
.subtitle {
margin-top: 18px;
font-size: clamp(13px, 2vw, 20px);
color: #ffd8e8;
font-weight: bold;
letter-spacing: 1px;
}
.prompt {
margin-top: 28px;
padding: 13px 25px;
border-radius: 30px;
background: #ff4b96;
color: white;
font-size: clamp(14px, 2.2vw, 20px);
font-weight: bold;
box-shadow: 0 6px 0 #a71b61, 0 10px 25px rgba(255,75,150,.35);
animation: bob 1.2s ease-in-out infinite;
}
@keyframes bob {
50% { transform: translateY(-5px) scale(1.03); }
}
#hud {
position: absolute;
top: 16px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 12px;
align-items: center;
pointer-events: none;
z-index: 3;
}
.pill {
background: rgba(20,4,30,.72);
border: 2px solid rgba(255,122,181,.55);
border-radius: 20px;
padding: 7px 15px;
color: #ffe5f0;
font-weight: bold;
font-size: 14px;
box-shadow: 0 4px 16px rgba(0,0,0,.25);
backdrop-filter: blur(5px);
}
@media (max-width: 500px) {
#hud { top: 8px; gap: 6px; }
.pill { padding: 5px 9px; font-size: 12px; }
}
</style>
</head>
<body>
<div id="game">
<canvas id="canvas"></canvas>
<div id="hud">
<div class="pill">SCORE <span id="score">0</span></div>
<div class="pill">♥ <span id="hearts">0</span></div>
<div class="pill">LEVEL <span id="level">1</span></div>
</div>
<div id="overlay">
<h1>HEART BURGER <span>BREAKOUT</span></h1>
<div class="subtitle">Catch the burger. Smash the bricks. Make love explode.</div>
<div class="prompt">CLICK OR PRESS A KEY TO START</div>
</div>
</div>
<script>
(() => {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');
const scoreEl = document.getElementById('score');
const heartsEl = document.getElementById('hearts');
const levelEl = document.getElementById('level');
let W, H, scale;
let running = false;
let score = 0, heartCount = 0, level = 1;
let bricks = [], particles = [], floaters = [], trails = [];
let paddle, ball, keys = {};
let shake = 0, flash = 0, levelFlash = 0;
let particlesEnabled = true;
let started = false;
let aiTimer = 0;
const brickRows = [
{ color: '#ff3f7f', dark: '#ad1852' },
{ color: '#ff5d9e', dark: '#b82c65' },
{ color: '#ff7ab3', dark: '#bf3b73' },
{ color: '#ffa0c8', dark: '#b9557e' },
{ color: '#d9367c', dark: '#941747' },
{ color: '#f04492', dark: '#991d56' }
];
function resize() {
const rect = document.getElementById('game').getBoundingClientRect();
const maxW = Math.min(rect.width, 1000);
const maxH = Math.min(rect.height, 760);
const aspect = 4 / 3;
let cw = maxW, ch = maxH;
if (cw / ch > aspect) cw = ch * aspect;
else ch = cw / aspect;
const dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.style.width = cw + 'px';
canvas.style.height = ch + 'px';
canvas.width = Math.floor(cw * dpr);
canvas.height = Math.floor(ch * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
W = cw; H = ch; scale = cw / 900;
paddle = paddle || { x: W / 2, y: H - 55 * scale, w: 125 * scale, h: 28 * scale, vx: 0 };
if (!started) {
paddle.x = W / 2;
paddle.y = H - 55 * scale;
}
ball = ball || { x: W / 2, y: paddle.y - 17 * scale, vx: 0, vy: 0, r: 9 * scale };
layoutBricks();
}
function layoutBricks() {
if (!W || !H) return;
const cols = W < 520 ? 7 : 10;
const rows = 5;
const margin = 38 * scale;
const gap = 7 * scale;
const top = 76 * scale;
const bw = (W - margin * 2 - gap * (cols - 1)) / cols;
const bh = 24 * scale;
bricks = [];
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
bricks.push({
x: margin + c * (bw + gap),
y: top + r * (bh + gap),
w: bw,
h: bh,
color: brickRows[(r + level) % brickRows.length].color,
dark: brickRows[(r + level) % brickRows.length].dark,
hp: 1,
alive: true,
pulse: Math.random() * Math.PI * 2
});
}
}
}
function resetGame() {
score = 0; heartCount = 0; level = 1;
particles = []; floaters = []; trails = [];
paddle.w = 125 * scale;
paddle.x = W / 2;
paddle.y = H - 55 * scale;
ball.x = W / 2;
ball.y = paddle.y - 20 * scale;
ball.vx = 0;
ball.vy = 0;
layoutBricks();
updateHud();
running = true;
started = true;
overlay.classList.add('hidden');
}
function updateHud() {
scoreEl.textContent = score;
heartsEl.textContent = heartCount;
levelEl.textContent = level;
}
function launchBall() {
const speed = 335 * scale;
const angle = (-Math.PI / 2) + (Math.random() - .5) * .65;
ball.vx = Math.cos(angle) * speed;
ball.vy = Math.sin(angle) * speed;
}
function startOrLaunch() {
if (!started) {
resetGame();
return;
}
if (!running) {
running = true;
overlay.classList.add('hidden');
return;
}
if (ball.vx === 0 && ball.vy === 0) launchBall();
}
function heartParticle(x, y, big = false) {
const size = (big ? 8 : 3.2 + Math.random() * 5.4) * scale;
particles.push({
type: 'heart',
x, y,
vx: (Math.random() - .5) * (big ? 300 : 260) * scale,
vy: (big ? -80 - Math.random() * 130 : -40 - Math.random() * 160) * scale,
life: big ? 1.15 : .65 + Math.random() * .8,
maxLife: big ? 1.15 : 1.45,
size,
rot: Math.random() * Math.PI * 2,
vr: (Math.random() - .5) * 7,
color: ['#ff4c91', '#ff77ae', '#ff9bc5', '#e92d78', '#ffc0db'][Math.floor(Math.random()*5)]
});
}
function sparkle(x, y, color) {
for (let i = 0; i < 5; i++) {
particles.push({
type: 'spark',
x, y,
vx: (Math.random() - .5) * 180 * scale,
vy: (Math.random() - .5) * 180 * scale,
life: .35 + Math.random() * .3,
maxLife: .65,
size: 1.5 + Math.random() * 2,
color
});
}
}
function explodeBrick(b) {
const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
const amount = particlesEnabled ? (W < 500 ? 28 : 42) : 0;
for (let i = 0; i < amount; i++) heartParticle(cx, cy);
for (let i = 0; i < 10; i++) sparkle(cx, cy, '#ffd3e5');
floaters.push({ x: cx, y: cy, text: '+10 ♥', life: .9, maxLife: .9, color: '#fff' });
score += 10;
heartCount += amount + 1;
shake = Math.max(shake, 6);
flash = .18;
}
function nextLevel() {
level++;
levelFlash = 1;
layoutBricks();
ball.x = paddle.x;
ball.y = paddle.y - 20 * scale;
ball.vx = 0;
ball.vy = 0;
paddle.w = Math.max(88 * scale, paddle.w - 7 * scale);
updateHud();
}
function drawHeart(x, y, size, color, rot = 0, alpha = 1) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rot);
ctx.scale(size / 10, size / 10);
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(0, 7);
ctx.bezierCurveTo(-12, -2, -7, -11, 0, -5);
ctx.bezierCurveTo(7, -11, 12, -2, 0, 7);
ctx.fill();
ctx.restore();
}
function drawBurger() {
const x = paddle.x, y = paddle.y, w = paddle.w, h = paddle.h;
const bx = x - w/2, by = y - h/2;
ctx.save();
// glow
const g = ctx.createRadialGradient(x, y, 3, x, y, w*.65);
g.addColorStop(0, 'rgba(255,100,160,.32)');
g.addColorStop(1, 'rgba(255,100,160,0)');
ctx.fillStyle = g;
ctx.fillRect(bx-w*.3, by-h, w*1.6, h*3);
// top bun
ctx.fillStyle = '#f6a940';
ctx.beginPath();
ctx.moveTo(bx, by+h*.42);
ctx.bezierCurveTo(bx+w*.02, by-h*.35, bx+w*.28, by-h*.48, bx+w*.5, by-h*.48);
ctx.bezierCurveTo(bx+w*.72, by-h*.48, bx+w*.98, by-h*.35, bx+w, by+h*.42);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#a95a14';
ctx.lineWidth = 2 * scale;
ctx.stroke();
// sesame
ctx.fillStyle = '#fff0bd';
const seeds = [[.22,-.22],[.4,-.31],[.59,-.28],[.76,-.17],[.31,-.12],[.66,-.1]];
seeds.forEach(([sx,sy]) => {
ctx.beginPath();
ctx.ellipse(bx+w*sx, by+h*sy, 2.1*scale, 1.1*scale, -.4, 0, Math.PI*2);
ctx.fill();
});
// lettuce
ctx.fillStyle = '#67d34d';
ctx.beginPath();
ctx.moveTo(bx, by+h*.4);
for (let i=0; i<=10; i++) {
const px = bx + w*i/10;
const py = by+h*(.5 + (i%2 ? .14 : 0));
ctx.lineTo(px, py);
}
ctx.lineTo(bx+w, by+h*.64);
ctx.lineTo(bx, by+h*.64);
ctx.closePath();
ctx.fill();
// patty
ctx.fillStyle = '#673321';
ctx.beginPath();
ctx.roundRect(bx+w*.02, by+h*.55, w*.96, h*.33, 5*scale);
ctx.fill();
ctx.fillStyle = '#85492f';
ctx.fillRect(bx+w*.04, by+h*.57, w*.92, h*.09);
// cheese
ctx.fillStyle = '#ffd43b';
ctx.beginPath();
ctx.moveTo(bx+w*.02, by+h*.52);
ctx.lineTo(bx+w*.98, by+h*.52);
ctx.lineTo(bx+w*.9, by+h*.82);
ctx.lineTo(bx+w*.78, by+h*.65);
ctx.lineTo(bx+w*.65, by+h*.83);
ctx.lineTo(bx+w*.52, by+h*.64);
ctx.lineTo(bx+w*.38, by+h*.82);
ctx.lineTo(bx+w*.25, by+h*.63);
ctx.lineTo(bx+w*.1, by+h*.78);
ctx.closePath();
ctx.fill();
// bottom bun
ctx.fillStyle = '#e99735';
ctx.beginPath();
ctx.roundRect(bx, by+h*.72, w, h*.34, 7*scale);
ctx.fill();
ctx.strokeStyle = '#a95a14';
ctx.lineWidth = 2 * scale;
ctx.stroke();
// little eyes make it adorable
ctx.fillStyle = '#3c221b';
ctx.beginPath();
ctx.arc(bx+w*.39, by+h*.9, 2*scale, 0, Math.PI*2);
ctx.arc(bx+w*.61, by+h*.9, 2*scale, 0, Math.PI*2);
ctx.fill();
ctx.restore();
}
function drawBrick(b, time) {
const p = 1 + Math.sin(time*3 + b.pulse)*.025;
const x = b.x, y = b.y, w = b.w*p, h = b.h*p;
ctx.save();
ctx.fillStyle = 'rgba(0,0,0,.18)';
ctx.beginPath();
ctx.roundRect(x+2*scale, y+4*scale, w, h, 5*scale);
ctx.fill();
const grad = ctx.createLinearGradient(x, y, x, y+h);
grad.addColorStop(0, '#fff');
grad.addColorStop(.12, b.color);
grad.addColorStop(1, b.dark);
ctx.globalAlpha = .35;
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.roundRect(x, y, w, h, 5*scale);
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = 'rgba(255,255,255,.45)';
ctx.lineWidth = 1.2*scale;
ctx.beginPath();
ctx.roundRect(x, y, w, h, 5*scale);
ctx.stroke();
// tiny heart mark
drawHeart(x+w/2, y+h/2+1*scale, 8*scale, 'rgba(255,255,255,.55)');
ctx.restore();
}
function update(dt) {
if (!running) return;
time += dt;
// Paddle input and gentle demo AI
const targetSpeed = 620 * scale;
if (keys.ArrowLeft || keys.a || keys.A) {
paddle.x -= targetSpeed*dt;
keys.demo = false;
} else if (keys.ArrowRight || keys.d || keys.D) {
paddle.x += targetSpeed*dt;
keys.demo = false;
} else {
aiTimer -= dt;
if (aiTimer <= 0) {
aiTimer = .12;
if (ball.vy < 0) {
const desired = ball.x;
const diff = desired - paddle.x;
if (Math.abs(diff) > 18*scale) {
paddle.x += Math.sign(diff) * Math.min(Math.abs(diff), 230*scale*dt);
keys.demo = true;
}
} else {
const desired = W/2 + Math.sin(time*.8)*W*.2;
const diff = desired - paddle.x;
paddle.x += Math.sign(diff) * Math.min(Math.abs(diff), 190*scale*dt);
}
}
}
paddle.x = Math.max(paddle.w/2+4, Math.min(W-paddle.w/2-4, paddle.x));
if (ball.vx === 0 && ball.vy === 0) {
ball.x = paddle.x;
ball.y = paddle.y - 20*scale;
}
// Ball trail
if (ball.vx || ball.vy) {
trails.push({x:ball.x,y:ball.y,r:ball.r,life:.18,maxLife:.18});
}
ball.x += ball.vx*dt;
ball.y += ball.vy*dt;
if (ball.x-ball.r < 0) { ball.x=ball.r; ball.vx=Math.abs(ball.vx); sparkle(0,ball.y,'#ff70ae'); }
if (ball.x+ball.r > W) { ball.x=W-ball.r; ball.vx=-Math.abs(ball.vx); sparkle(W,ball.y,'#ff70ae'); }
if (ball.y-ball.r < 0) { ball.y=ball.r; ball.vy=Math.abs(ball.vy); sparkle(ball.x,0,'#ff70ae'); }
// Paddle collision
if (ball.vy > 0 &&
ball.y+ball.r >= paddle.y-paddle.h/2 &&
ball.y-ball.r <= paddle.y+paddle.h/2 &&
ball.x >= paddle.x-paddle.w/2-ball.r &&
ball.x <= paddle.x+paddle.w/2+ball.r) {
ball.y = paddle.y-paddle.h/2-ball.r;
const hit = (ball.x-paddle.x)/(paddle.w/2);
const speed = Math.min(430*scale, Math.hypot(ball.vx,ball.vy)*1.015);
const angle = -Math.PI/2 + hit*1.05;
ball.vx = Math.cos(angle)*speed;
ball.vy = Math.sin(angle)*speed;
for(let i=0;i<7;i++) sparkle(ball.x, paddle.y, '#ffd45b');
heartParticle(ball.x, paddle.y);
}
// Brick collisions
for (const b of bricks) {
if (!b.alive) continue;
const nearestX = Math.max(b.x, Math.min(ball.x, b.x+b.w));
const nearestY = Math.max(b.y, Math.min(ball.y, b.y+b.h));
const dx = ball.x-nearestX, dy = ball.y-nearestY;
if (dx*dx+dy*dy < ball.r*ball.r) {
const overlapX = (b.w/2+ball.r)-Math.abs(ball.x-(b.x+b.w/2));
const overlapY = (b.h/2+ball.r)-Math.abs(ball.y-(b.y+b.h/2));
if (overlapX < overlapY) {
ball.vx *= -1;
ball.x += ball.x < b.x+b.w/2 ? -overlapX : overlapX;
} else {
ball.vy *= -1;
ball.y += ball.y < b.y+b.h/2 ? -overlapY : overlapY;
}
b.alive = false;
explodeBrick(b);
break;
}
}
// Particles
for (let i=particles.length-1;i>=0;i--) {
const p=particles[i];
p.life -= dt;
if (p.life<=0) { particles.splice(i,1); continue; }
p.x += p.vx*dt;
p.y += p.vy*dt;
if (p.type==='heart') {
p.vy += 290*scale*dt;
p.vx *= (1-dt*.8);
p.rot += p.vr*dt;
} else {
p.vx *= (1-dt*3);
p.vy *= (1-dt*3);
}
}
for (let i=trails.length-1;i>=0;i--) {
trails[i].life -= dt;
if (trails[i].life<=0) trails.splice(i,1);
}
for (let i=floaters.length-1;i>=0;i--) {
floaters[i].life -= dt;
floaters[i].y -= 35*scale*dt;
if (floaters[i].life<=0) floaters.splice(i,1);
}
shake = Math.max(0, shake-dt*24);
flash = Math.max(0, flash-dt);
levelFlash = Math.max(0, levelFlash-dt*.8);
if (bricks.every(b=>!b.alive)) {
nextLevel();
}
if (ball.y-ball.r > H+20) {
running = false;
overlay.classList.remove('hidden');
overlay.innerHTML = `<h1>BURGER <span>DROPPED!</span></h1>
<div class="subtitle">You made ${heartCount} hearts explode — incredible.</div>
<div class="prompt">CLICK OR PRESS A KEY TO TRY AGAIN</div>`;
updateHud();
}
updateHud();
}
function render() {
ctx.clearRect(0,0,W,H);
ctx.save();
if (shake>0) {
ctx.translate((Math.random()-.5)*shake,(Math.random()-.5)*shake);
}
// Background confetti
ctx.globalAlpha=.14;
for(let i=0;i<24;i++){
const x=(i*83+time*12*scale)%W;
const y=(i*47+time*24*scale)%H;
ctx.fillStyle=i%2?'#ff6ca7':'#ffd2e5';
ctx.save(); ctx.translate(x,y); ctx.rotate(time+i); ctx.fillRect(-2,-6,4,12); ctx.restore();
}
ctx.globalAlpha=1;
// Trail
for(const t of trails){
ctx.globalAlpha=(t.life/t.maxLife)*.3;
ctx.fillStyle='#ff6fa8';
ctx.beginPath(); ctx.arc(t.x,t.y,t.r*.85,0,Math.PI*2); ctx.fill();
}
ctx.globalAlpha=1;
for(const b of bricks) if(b.alive) drawBrick(b,time);
// Floating text
for(const f of floaters){
ctx.globalAlpha=Math.min(1,f.life/.3);
ctx.fillStyle=f.color;
ctx.font=`bold ${15*scale}px Trebuchet MS`;
ctx.textAlign='center';
ctx.fillText(f.text,f.x,f.y);
}
ctx.globalAlpha=1;
drawBurger();
// Ball
if(ball.vx||ball.vy){
const g=ctx.createRadialGradient(ball.x-3*scale,ball.y-3*scale,1,ball.x,ball.y,ball.r*1.8);
g.addColorStop(0,'#fff');
g.addColorStop(.45,'#ff7eb4');
g.addColorStop(1,'rgba(255,56,140,.1)');
ctx.fillStyle=g;
ctx.beginPath(); ctx.arc(ball.x,ball.y,ball.r*1.5,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#fff4f9';
ctx.beginPath(); ctx.arc(ball.x,ball.y,ball.r,0,Math.PI*2); ctx.fill();
}
// Hearts
for(const p of particles){
const alpha=Math.min(1,p.life/.25);
if(p.type==='heart') drawHeart(p.x,p.y,p.size,p.color,p.rot,alpha);
else {
ctx.globalAlpha=alpha;
ctx.fillStyle=p.color;
ctx.beginPath(); ctx.arc(p.x,p.y,p.size,0,Math.PI*2); ctx.fill();
}
}
ctx.globalAlpha=1;
if(flash>0){
ctx.fillStyle=`rgba(255,255,255,${flash*.8})`;
ctx.fillRect(0,0,W,H);
}
if(levelFlash>0){
ctx.fillStyle=`rgba(255,70,150,${levelFlash*.2})`;
ctx.fillRect(0,0,W,H);
ctx.fillStyle='white';
ctx.font=`bold ${28*scale}px Trebuchet MS`;
ctx.textAlign='center';
ctx.fillText(`LEVEL ${level}!`, W/2, H*.45);
}
ctx.restore();
if(!started || !running){
ctx.fillStyle='rgba(10,2,18,.2)';
ctx.fillRect(0,0,W,H);
}
}
let time=0;
let last=performance.now();
function loop(now){
const dt=Math.min(.033,(now-last)/1000);
last=now;
if(started && running) {
if(ball.vx===0 && ball.vy===0){
aiTimer-=dt;
if(aiTimer<=0){ aiTimer=.65; launchBall(); }
}
}
update(dt);
render();
requestAnimationFrame(loop);
}
window.addEventListener('resize', resize);
window.addEventListener('keydown', e => {
if(['ArrowLeft','ArrowRight',' ','a','d','A','D'].includes(e.key)) e.preventDefault();
keys[e.key]=true;
startOrLaunch();
});
window.addEventListener('keyup', e => { keys[e.key]=false; });
canvas.addEventListener('pointermove', e => {
if(!started) return;
const r=canvas.getBoundingClientRect();
paddle.x=Math.max(paddle.w/2+4,Math.min(W-paddle.w/2-4,(e.clientX-r.left)));
});
canvas.addEventListener('pointerdown', e => {
e.preventDefault();
startOrLaunch();
const r=canvas.getBoundingClientRect();
paddle.x=Math.max(paddle.w/2+4,Math.min(W-paddle.w/2-4,(e.clientX-r.left)));
});
overlay.addEventListener('pointerdown', () => {
if(!started) resetGame();
else if(!running) {
overlay.innerHTML='<h1>HEART BURGER <span>BREAKOUT</span></h1><div class="subtitle">Catch the burger. Smash the bricks. Make love explode.</div><div class="prompt">CLICK OR PRESS A KEY TO START</div>';
resetGame();
}
});
resize();
requestAnimationFrame(loop);
})();
</script>
</body>
</html>