Hamburger breakoutOne-line
Gemini 3.8 Flash
by GoogleReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 1 min 18 s
- Cost
- $0.044
- Output tokens
- 11,691
- Spent thinking
- 708 tokens
- Speed
- 154.7 tok/s
- Console errors
- 0
- First token after
- 2.2 s
- First visible text after
- 8.9 s
- Prompt tokens
- 363
- Code size
- 28.1 KB
- Finish reason
- stop
- Served by
- Model id
google/gemini-3.8-flash- Reasoning sent
model default- Max output
- 65,536 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 · 1030 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Burger Breakout - Heart Explosion Edition</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
-webkit-user-select: none;
}
body {
background: radial-gradient(circle at center, #2b1329 0%, #150918 100%);
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
color: #fff;
}
#game-container {
position: relative;
width: 100%;
max-width: 900px;
aspect-ratio: 4 / 3;
max-height: 94vh;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.7), 0 0 30px rgba(255, 105, 180, 0.2);
border-radius: 16px;
overflow: hidden;
border: 3px solid rgba(255, 182, 193, 0.3);
background: #110515;
}
canvas {
width: 100%;
height: 100%;
display: block;
}
#overlay-banner {
position: absolute;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
background: rgba(255, 20, 147, 0.85);
backdrop-filter: blur(8px);
border: 2px solid #fff;
padding: 10px 24px;
border-radius: 30px;
font-size: 16px;
font-weight: 800;
letter-spacing: 1px;
text-transform: uppercase;
box-shadow: 0 4px 20px rgba(255, 20, 147, 0.6);
pointer-events: none;
animation: pulseBanner 1.6s ease-in-out infinite;
z-index: 20;
display: flex;
align-items: center;
gap: 10px;
white-space: nowrap;
}
#overlay-banner span.tag {
background: #ffe600;
color: #331500;
padding: 3px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 900;
}
@keyframes pulseBanner {
0%, 100% {
transform: translateX(-50%) scale(1);
box-shadow: 0 0 15px rgba(255, 105, 180, 0.7);
}
50% {
transform: translateX(-50%) scale(1.05);
box-shadow: 0 0 28px rgba(255, 20, 147, 1);
}
}
@media (max-width: 600px) {
#overlay-banner {
font-size: 12px;
padding: 6px 16px;
bottom: 12px;
}
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="gameCanvas" width="800" height="600"></canvas>
<div id="overlay-banner">
<span class="tag">AI AUTO-PLAY</span>
<span id="banner-text">CLICK OR PRESS ANY KEY TO PLAY</span>
</div>
</div>
<script>
(() => {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const banner = document.getElementById('overlay-banner');
const bannerText = document.getElementById('banner-text');
// --- Audio Synthesis (Web Audio API) ---
let audioCtx = null;
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
}
function playSound(type, pitchModifier = 1) {
if (!audioCtx || audioCtx.state !== 'running') return;
try {
const now = audioCtx.currentTime;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
if (type === 'bounce') {
osc.type = 'triangle';
osc.frequency.setValueAtTime(260 * pitchModifier, now);
osc.frequency.exponentialRampToValueAtTime(120 * pitchModifier, now + 0.12);
gain.gain.setValueAtTime(0.25, now);
gain.gain.linearRampToValueAtTime(0.01, now + 0.12);
osc.start(now);
osc.stop(now + 0.13);
} else if (type === 'brick') {
osc.type = 'sine';
const startFreq = (400 + Math.random() * 200) * pitchModifier;
osc.frequency.setValueAtTime(startFreq, now);
osc.frequency.exponentialRampToValueAtTime(startFreq * 2.2, now + 0.15);
gain.gain.setValueAtTime(0.22, now);
gain.gain.exponentialRampToValueAtTime(0.005, now + 0.18);
osc.start(now);
osc.stop(now + 0.19);
} else if (type === 'chime') {
osc.type = 'sine';
osc.frequency.setValueAtTime((800 + Math.random() * 400) * pitchModifier, now);
gain.gain.setValueAtTime(0.12, now);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.3);
osc.start(now);
osc.stop(now + 0.31);
} else if (type === 'cheer') {
osc.type = 'triangle';
osc.frequency.setValueAtTime(523.25, now);
osc.frequency.setValueAtTime(659.25, now + 0.08);
osc.frequency.setValueAtTime(783.99, now + 0.16);
osc.frequency.setValueAtTime(1046.5, now + 0.24);
gain.gain.setValueAtTime(0.25, now);
gain.gain.linearRampToValueAtTime(0.01, now + 0.45);
osc.start(now);
osc.stop(now + 0.46);
}
} catch(e) {}
}
// --- Game State ---
let isDemo = true;
let score = 0;
let combo = 0;
let heartsCollected = 0;
let lives = 3;
let level = 1;
let screenShake = 0;
// Paddle (The Burger)
const paddle = {
x: 350,
y: 530,
width: 120,
height: 38,
vx: 0,
targetX: 350,
squash: 1, // squash/stretch deformation
stretch: 1,
tilt: 0
};
// Ball (Juicy Cherry Tomato / Love Ball)
const ball = {
x: 400,
y: 400,
radius: 10,
vx: 4.5,
vy: -5.5,
speed: 7.2,
trail: []
};
// Particle systems
const heartParticles = [];
const floatingTexts = [];
// Brick Grid
const BRICK_ROWS = 5;
const BRICK_COLS = 9;
const BRICK_WIDTH = 74;
const BRICK_HEIGHT = 26;
const BRICK_PADDING = 9;
const BRICK_OFFSET_TOP = 65;
const BRICK_OFFSET_LEFT = 31;
const ROW_PALETTES = [
{ fill: '#ff4d79', stroke: '#ff1a53', glow: '#ff3366', name: 'Strawberry' },
{ fill: '#ff85b3', stroke: '#ff4d94', glow: '#ff66a3', name: 'Bubblegum' },
{ fill: '#b86bff', stroke: '#9d3bff', glow: '#a84dff', name: 'Grape' },
{ fill: '#ffb347', stroke: '#ff941a', glow: '#ffa32b', name: 'Peach' },
{ fill: '#ffd166', stroke: '#ffbf2b', glow: '#ffc947', name: 'Honey' }
];
let bricks = [];
function createBricks() {
bricks = [];
for (let r = 0; r < BRICK_ROWS; r++) {
bricks[r] = [];
const pal = ROW_PALETTES[r % ROW_PALETTES.length];
for (let c = 0; c < BRICK_COLS; c++) {
bricks[r][c] = {
x: BRICK_OFFSET_LEFT + c * (BRICK_WIDTH + BRICK_PADDING),
y: BRICK_OFFSET_TOP + r * (BRICK_HEIGHT + BRICK_PADDING),
w: BRICK_WIDTH,
h: BRICK_HEIGHT,
alive: true,
color: pal.fill,
stroke: pal.stroke,
glow: pal.glow,
hp: 1,
pulse: Math.random() * Math.PI * 2
};
}
}
}
createBricks();
// Floating ambient background hearts
const bgHearts = [];
for (let i = 0; i < 25; i++) {
bgHearts.push({
x: Math.random() * 800,
y: Math.random() * 600,
size: 6 + Math.random() * 12,
speed: 0.3 + Math.random() * 0.7,
wobble: Math.random() * Math.PI * 2,
alpha: 0.15 + Math.random() * 0.25
});
}
// --- Helper: Draw Heart Shape ---
function drawHeartPath(ctx, x, y, size) {
const topCurveHeight = size * 0.3;
ctx.beginPath();
ctx.moveTo(x, y + topCurveHeight);
// Left curve
ctx.bezierCurveTo(
x, y,
x - size / 2, y,
x - size / 2, y + topCurveHeight
);
ctx.bezierCurveTo(
x - size / 2, y + (size + topCurveHeight) / 2,
x, y + (size + topCurveHeight) / 1.3,
x, y + size
);
// Right curve
ctx.bezierCurveTo(
x, y + (size + topCurveHeight) / 1.3,
x + size / 2, y + (size + topCurveHeight) / 2,
x + size / 2, y + topCurveHeight
);
ctx.bezierCurveTo(
x + size / 2, y,
x, y,
x, y + topCurveHeight
);
ctx.closePath();
}
// --- Explode Brick Into Tons of Hearts ---
function explodeBrick(brick) {
const heartCount = 35 + Math.floor(Math.random() * 16); // 35 to 50 hearts per brick!
heartsCollected += heartCount;
combo++;
const points = 100 * combo;
score += points;
// Trigger Screen Shake
screenShake = Math.min(screenShake + 5, 12);
// Audio
playSound('brick', 1 + (combo % 8) * 0.12);
playSound('chime', 1 + (combo % 6) * 0.15);
const heartColors = [
'#ff1493', '#ff69b4', '#ff3366', '#ff85b3',
'#ff0055', '#ffffff', '#ffd1dc', '#ff4081', '#ffcc00'
];
const praiseWords = ['SWEET!', 'DELICIOUS!', 'YUM!', 'BURGER LOVE!', 'TASTY!', 'CRUNCH!', 'HEARTY!'];
const word = (combo > 2 && Math.random() < 0.6) ? praiseWords[Math.floor(Math.random() * praiseWords.length)] : null;
if (word) {
floatingTexts.push({
text: `${word} +${points}`,
x: brick.x + brick.w / 2,
y: brick.y,
alpha: 1.0,
scale: 1.4,
vy: -1.6
});
} else {
floatingTexts.push({
text: `+${points}`,
x: brick.x + brick.w / 2,
y: brick.y,
alpha: 1.0,
scale: 1.1,
vy: -1.3
});
}
// Spawn massive cloud of hearts
for (let i = 0; i < heartCount; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 1.5 + Math.random() * 7.5;
const size = 6 + Math.random() * 16;
heartParticles.push({
x: brick.x + brick.w / 2 + (Math.random() - 0.5) * (brick.w * 0.6),
y: brick.y + brick.h / 2 + (Math.random() - 0.5) * (brick.h * 0.6),
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed - (2 + Math.random() * 2), // initial upward pop
gravity: 0.13 + Math.random() * 0.08,
friction: 0.965,
size: size,
rotation: Math.random() * Math.PI * 2,
rotSpeed: (Math.random() - 0.5) * 0.25,
color: heartColors[Math.floor(Math.random() * heartColors.length)],
life: 1.0,
decay: 0.012 + Math.random() * 0.014,
pulseSpeed: 3 + Math.random() * 4,
shimmer: Math.random() > 0.4
});
}
}
// --- Render Hamburger Paddle ---
function drawHamburger(x, y, width, height, squash, stretch, tilt) {
ctx.save();
ctx.translate(x + width / 2, y + height / 2);
ctx.rotate(tilt);
ctx.scale(squash, stretch);
ctx.translate(-(width / 2), -(height / 2));
const w = width;
const h = height;
// Shadow underneath burger
ctx.fillStyle = 'rgba(0, 0, 0, 0.35)';
ctx.beginPath();
ctx.ellipse(w / 2, h + 8, w * 0.5, 7, 0, 0, Math.PI * 2);
ctx.fill();
// 1. Bottom Bun
const bottomBunH = h * 0.24;
const bottomBunY = h - bottomBunH;
const botGrad = ctx.createLinearGradient(0, bottomBunY, 0, h);
botGrad.addColorStop(0, '#f2a953');
botGrad.addColorStop(1, '#b86618');
ctx.fillStyle = botGrad;
ctx.beginPath();
ctx.roundRect(4, bottomBunY, w - 8, bottomBunH, [2, 2, 9, 9]);
ctx.fill();
ctx.lineWidth = 1.5;
ctx.strokeStyle = '#7c3905';
ctx.stroke();
// 2. Patty (Meat)
const pattyH = h * 0.28;
const pattyY = bottomBunY - pattyH + 3;
const pattyGrad = ctx.createLinearGradient(0, pattyY, 0, pattyY + pattyH);
pattyGrad.addColorStop(0, '#592913');
pattyGrad.addColorStop(0.5, '#401a08');
pattyGrad.addColorStop(1, '#2c1003');
ctx.fillStyle = pattyGrad;
ctx.beginPath();
ctx.roundRect(1, pattyY, w - 2, pattyH, 6);
ctx.fill();
// Patty grill marks
ctx.strokeStyle = '#1e0900';
ctx.lineWidth = 2.5;
for (let gx = 12; gx < w - 10; gx += 16) {
ctx.beginPath();
ctx.moveTo(gx, pattyY + 2);
ctx.lineTo(gx + 6, pattyY + pattyH - 2);
ctx.stroke();
}
// 3. Melted Cheese
const cheeseY = pattyY - 2;
ctx.fillStyle = '#ffc800';
ctx.beginPath();
ctx.moveTo(3, cheeseY);
ctx.lineTo(w - 3, cheeseY);
ctx.lineTo(w - 7, cheeseY + 9);
// Cheese drips/drapes over patty
ctx.lineTo(w * 0.78, cheeseY + 12);
ctx.lineTo(w * 0.70, cheeseY + 4);
ctx.lineTo(w * 0.52, cheeseY + 14);
ctx.lineTo(w * 0.40, cheeseY + 5);
ctx.lineTo(w * 0.24, cheeseY + 13);
ctx.lineTo(w * 0.14, cheeseY + 6);
ctx.lineTo(5, cheeseY + 8);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#e69500';
ctx.lineWidth = 1;
ctx.stroke();
// 4. Tomato Slices (2 slices peeking through)
const tomatoY = cheeseY - 4;
ctx.fillStyle = '#ff3333';
ctx.beginPath();
ctx.ellipse(w * 0.32, tomatoY + 2, w * 0.22, 6, 0.05, 0, Math.PI * 2);
ctx.ellipse(w * 0.68, tomatoY + 2, w * 0.22, 6, -0.05, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#ff6666'; // Tomato gloss
ctx.beginPath();
ctx.ellipse(w * 0.32 - 4, tomatoY, w * 0.12, 2.5, 0.05, 0, Math.PI * 2);
ctx.ellipse(w * 0.68 - 4, tomatoY, w * 0.12, 2.5, -0.05, 0, Math.PI * 2);
ctx.fill();
// 5. Crispy Wavy Lettuce
const lettuceY = tomatoY - 2;
ctx.fillStyle = '#4ade80';
ctx.strokeStyle = '#22c55e';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(0, lettuceY + 4);
for (let lx = 0; lx <= w; lx += 10) {
const ly = lettuceY + Math.sin(lx * 0.45) * 4.5;
ctx.lineTo(lx, ly);
}
ctx.lineTo(w, lettuceY + 7);
ctx.lineTo(0, lettuceY + 7);
ctx.closePath();
ctx.fill();
ctx.stroke();
// 6. Plump Top Bun
const topBunH = h * 0.52;
const topBunY = lettuceY - topBunH + 4;
const topGrad = ctx.createLinearGradient(0, topBunY, 0, topBunY + topBunH);
topGrad.addColorStop(0, '#f9be6d');
topGrad.addColorStop(0.4, '#e79438');
topGrad.addColorStop(1, '#b96515');
ctx.fillStyle = topGrad;
ctx.beginPath();
// Round bun dome
ctx.moveTo(2, topBunY + topBunH);
ctx.bezierCurveTo(
-2, topBunY - 4,
w + 2, topBunY - 4,
w - 2, topBunY + topBunH
);
ctx.closePath();
ctx.fill();
ctx.lineWidth = 1.5;
ctx.strokeStyle = '#8a4004';
ctx.stroke();
// Bun Gloss Highlight
ctx.fillStyle = 'rgba(255, 255, 255, 0.35)';
ctx.beginPath();
ctx.ellipse(w * 0.48, topBunY + topBunH * 0.3, w * 0.32, 5, -0.08, 0, Math.PI * 2);
ctx.fill();
// 7. Sesame Seeds on Bun
const seeds = [
{ x: 0.22, y: 0.35, rot: 0.2 },
{ x: 0.35, y: 0.22, rot: -0.3 },
{ x: 0.50, y: 0.16, rot: 0.1 },
{ x: 0.65, y: 0.24, rot: 0.4 },
{ x: 0.78, y: 0.36, rot: -0.2 },
{ x: 0.28, y: 0.50, rot: 0.5 },
{ x: 0.44, y: 0.42, rot: -0.1 },
{ x: 0.58, y: 0.44, rot: 0.3 },
{ x: 0.72, y: 0.52, rot: -0.4 }
];
ctx.fillStyle = '#fff9db';
ctx.strokeStyle = '#d4aa55';
ctx.lineWidth = 0.5;
for (let s of seeds) {
ctx.save();
ctx.translate(w * s.x, topBunY + topBunH * s.y);
ctx.rotate(s.rot);
ctx.beginPath();
ctx.ellipse(0, 0, 3.2, 1.8, 0, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.restore();
}
ctx.restore();
}
// --- Reset Ball After Loss ---
function resetBall() {
ball.x = paddle.x + paddle.width / 2;
ball.y = paddle.y - 18;
const angle = -Math.PI / 2 + (Math.random() - 0.5) * 0.8;
ball.speed = 7.2;
ball.vx = Math.cos(angle) * ball.speed;
ball.vy = Math.sin(angle) * ball.speed;
ball.trail = [];
combo = 0;
}
// --- Start Real Player Mode ---
function enterPlayerMode() {
if (isDemo) {
isDemo = false;
banner.style.display = 'none';
initAudio();
score = 0;
heartsCollected = 0;
combo = 0;
lives = 3;
createBricks();
resetBall();
}
}
window.addEventListener('keydown', (e) => {
initAudio();
if (isDemo) {
enterPlayerMode();
}
if (e.key === 'ArrowLeft' || e.key === 'a') {
paddle.targetX = Math.max(0, paddle.targetX - 35);
} else if (e.key === 'ArrowRight' || e.key === 'd') {
paddle.targetX = Math.min(canvas.width - paddle.width, paddle.targetX + 35);
}
});
window.addEventListener('mousedown', () => {
initAudio();
if (isDemo) enterPlayerMode();
});
window.addEventListener('touchstart', () => {
initAudio();
if (isDemo) enterPlayerMode();
}, { passive: true });
canvas.addEventListener('mousemove', (e) => {
if (isDemo) return;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const mouseX = (e.clientX - rect.left) * scaleX;
paddle.targetX = Math.max(0, Math.min(canvas.width - paddle.width, mouseX - paddle.width / 2));
});
canvas.addEventListener('touchmove', (e) => {
if (isDemo) return;
if (e.touches.length > 0) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const touchX = (e.touches[0].clientX - rect.left) * scaleX;
paddle.targetX = Math.max(0, Math.min(canvas.width - paddle.width, touchX - paddle.width / 2));
}
}, { passive: true });
// --- Main Update Loop ---
let lastTime = performance.now();
function update() {
// 1. AI Logic (for Demo Mode)
if (isDemo) {
// Natural prediction of ball X
const destX = ball.x - paddle.width / 2;
paddle.targetX += (destX - paddle.targetX) * 0.16;
}
// Smooth Paddle Movement
const prevPaddleX = paddle.x;
paddle.x += (paddle.targetX - paddle.x) * 0.28;
paddle.vx = paddle.x - prevPaddleX;
paddle.tilt = Math.max(-0.25, Math.min(0.25, paddle.vx * 0.02));
// Restore squash / stretch gradually
paddle.squash += (1 - paddle.squash) * 0.15;
paddle.stretch += (1 - paddle.stretch) * 0.15;
// Constrain paddle
paddle.x = Math.max(0, Math.min(canvas.width - paddle.width, paddle.x));
// Ball Trails
ball.trail.push({ x: ball.x, y: ball.y, alpha: 0.6 });
if (ball.trail.length > 8) ball.trail.shift();
ball.trail.forEach(t => t.alpha *= 0.82);
// Ball Movement
ball.x += ball.vx;
ball.y += ball.vy;
// Ball Wall Collisions
if (ball.x - ball.radius <= 0) {
ball.x = ball.radius;
ball.vx = Math.abs(ball.vx);
playSound('bounce', 1.4);
} else if (ball.x + ball.radius >= canvas.width) {
ball.x = canvas.width - ball.radius;
ball.vx = -Math.abs(ball.vx);
playSound('bounce', 1.4);
}
if (ball.y - ball.radius <= 0) {
ball.y = ball.radius;
ball.vy = Math.abs(ball.vy);
playSound('bounce', 1.4);
}
// Ball Bottom Out
if (ball.y - ball.radius > canvas.height) {
if (isDemo) {
resetBall();
} else {
lives--;
screenShake = 15;
if (lives <= 0) {
// Game Over -> Reset
score = 0;
lives = 3;
createBricks();
}
resetBall();
}
}
// Paddle Collision (The Burger Bounce!)
const burgerTop = paddle.y;
const burgerBottom = paddle.y + paddle.height;
const burgerLeft = paddle.x;
const burgerRight = paddle.x + paddle.width;
if (
ball.y + ball.radius >= burgerTop &&
ball.y - ball.radius <= burgerBottom &&
ball.x + ball.radius >= burgerLeft &&
ball.x - ball.radius <= burgerRight &&
ball.vy > 0
) {
ball.y = burgerTop - ball.radius;
// Hit point on burger determines angle (-1 to 1)
const hitOffset = (ball.x - (paddle.x + paddle.width / 2)) / (paddle.width / 2);
const angle = hitOffset * (Math.PI / 3); // Max 60 deg angle
ball.speed = Math.min(10.5, ball.speed + 0.08); // Slight speed boost
ball.vx = Math.sin(angle) * ball.speed + paddle.vx * 0.25;
ball.vy = -Math.cos(angle) * ball.speed;
// Squash burger on impact!
paddle.squash = 1.25;
paddle.stretch = 0.72;
combo = 0; // Combo resets on paddle hit
playSound('bounce', 1.1);
// Cute burger sparkle bursts
for (let s = 0; s < 7; s++) {
heartParticles.push({
x: ball.x,
y: burgerTop,
vx: (Math.random() - 0.5) * 5,
vy: -Math.random() * 3.5 - 1,
gravity: 0.1,
friction: 0.94,
size: 5 + Math.random() * 7,
rotation: Math.random() * Math.PI,
rotSpeed: 0.1,
color: '#ffe066',
life: 0.8,
decay: 0.03,
pulseSpeed: 4
});
}
}
// Brick Collisions
let aliveCount = 0;
for (let r = 0; r < BRICK_ROWS; r++) {
for (let c = 0; c < BRICK_COLS; c++) {
const b = bricks[r][c];
if (!b.alive) continue;
aliveCount++;
b.pulse += 0.05;
// AABB vs Circle
const closestX = Math.max(b.x, Math.min(ball.x, b.x + b.w));
const closestY = Math.max(b.y, Math.min(ball.y, b.y + b.h));
const distX = ball.x - closestX;
const distY = ball.y - closestY;
const distSq = distX * distX + distY * distY;
if (distSq < ball.radius * ball.radius) {
// Explode the brick!
b.alive = false;
explodeBrick(b);
// Determine bounce direction
const prevX = ball.x - ball.vx;
const prevY = ball.y - ball.vy;
if (prevX <= b.x || prevX >= b.x + b.w) {
ball.vx = -ball.vx;
} else {
ball.vy = -ball.vy;
}
break;
}
}
}
// Recreate bricks if all cleared
if (aliveCount === 0) {
playSound('cheer');
level++;
createBricks();
resetBall();
}
// Update Hearts Particles
for (let i = heartParticles.length - 1; i >= 0; i--) {
const p = heartParticles[i];
p.x += p.vx;
p.y += p.vy;
p.vx *= p.friction;
p.vy *= p.friction;
p.vy += p.gravity;
p.rotation += p.rotSpeed;
p.life -= p.decay;
if (p.life <= 0) {
heartParticles.splice(i, 1);
}
}
// Update Floating Text
for (let i = floatingTexts.length - 1; i >= 0; i--) {
const t = floatingTexts[i];
t.y += t.vy;
t.alpha -= 0.02;
t.scale += 0.005;
if (t.alpha <= 0) {
floatingTexts.splice(i, 1);
}
}
// Background floating hearts
for (let h of bgHearts) {
h.y -= h.speed;
h.wobble += 0.02;
h.x += Math.sin(h.wobble) * 0.4;
if (h.y < -20) {
h.y = canvas.height + 20;
h.x = Math.random() * canvas.width;
}
}
if (screenShake > 0) {
screenShake *= 0.88;
if (screenShake < 0.2) screenShake = 0;
}
}
// --- Main Render Loop ---
function render() {
ctx.save();
// Screen Shake effect
if (screenShake > 0) {
const sx = (Math.random() - 0.5) * screenShake;
const sy = (Math.random() - 0.5) * screenShake;
ctx.translate(sx, sy);
}
// Clear & Dynamic gradient background
const bgGrad = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, 80,
canvas.width / 2, canvas.height / 2, 500
);
bgGrad.addColorStop(0, '#270a2a');
bgGrad.addColorStop(1, '#0e0312');
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Subtle background grid
ctx.strokeStyle = 'rgba(255, 105, 180, 0.04)';
ctx.lineWidth = 1;
for (let x = 0; x < canvas.width; x += 32) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
for (let y = 0; y < canvas.height; y += 32) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
// Draw ambient floating background hearts
for (let h of bgHearts) {
ctx.save();
ctx.fillStyle = `rgba(255, 105, 180, ${h.alpha})`;
drawHeartPath(ctx, h.x, h.y, h.size);
ctx.fill();
ctx.restore();
}
// Draw Bricks
for (let r = 0; r < BRICK_ROWS; r++) {
for (let c = 0; c < BRICK_COLS; c++) {
const b = bricks[r][c];
if (!b.alive) continue;
const pulseScale = 1 + Math.sin(b.pulse) * 0.02;
const bw = b.w * pulseScale;
const bh = b.h * pulseScale;
const bx = b.x + (b.w - bw) / 2;
const by = b.y + (b.h - bh) / 2;
ctx.save();
// Brick Glow
ctx.shadowColor = b.glow;
ctx.shadowBlur = 10;
// Brick Body Gradient
const bGrad = ctx.createLinearGradient(bx, by, bx, by + bh);
bGrad.addColorStop(0, '#ffffff');
bGrad.addColorStop(0.2, b.color);
bGrad.addColorStop(1, b.stroke);
ctx.fillStyle = bGrad;
ctx.beginPath();
ctx.roundRect(bx, by, bw, bh, 6);
ctx.fill();
ctx.shadowBlur = 0;
ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';
ctx.lineWidth = 1.5;
ctx.stroke();
// Brick mini heart emblem
ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
drawHeartPath(ctx, bx + bw / 2, by + bh * 0.22, 10);
ctx.fill();
ctx.restore();
}
}
// Draw Ball Trail
for (let i = 0; i < ball.trail.length; i++) {
const t = ball.trail[i];
ctx.save();
ctx.fillStyle = `rgba(255, 65, 115, ${t.alpha * 0.5})`;
ctx.beginPath();
ctx.arc(t.x, t.y, ball.radius * (i / ball.trail.length), 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// Draw Ball (Cherry Tomato / Sweet Berry)
ctx.save();
ctx.shadowColor = '#ff1493';
ctx.shadowBlur = 15;
// Tomato Sphere
const tomatoGrad = ctx.createRadialGradient(
ball.x - 3, ball.y - 3, 2,
ball.x, ball.y, ball.radius
);
tomatoGrad.addColorStop(0, '#ff99aa');
tomatoGrad.addColorStop(0.35, '#ff1a53');
tomatoGrad.addColorStop(0.9, '#a6002c');
tomatoGrad.addColorStop(1, '#66001a');
ctx.fillStyle = tomatoGrad;
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fill();
// Stem / green calyx top
ctx.fillStyle = '#48bb78';
ctx.beginPath();
ctx.arc(ball.x, ball.y - ball.radius + 1.5, 3.2, 0, Math.PI * 2);
ctx.fill();
// Highlight sheen
ctx.fillStyle = 'rgba(255, 255, 255, 0.85)';
ctx.beginPath();
ctx.arc(ball.x - 3.5, ball.y - 3.5, 2.8, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// Draw Heart Particles (Exploding Bricks)
for (let p of heartParticles) {
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(p.rotation);
ctx.globalAlpha = Math.max(0, p.life);
if (p.shimmer) {
ctx.shadowColor = p.color;
ctx.shadowBlur = 12;
}
ctx.fillStyle = p.color;
drawHeartPath(ctx, 0, -p.size / 2, p.size);
ctx.fill();
// Inner sparkle highlight
if (p.size > 11) {
ctx.fillStyle = 'rgba(255, 255, 255, 0.65)';
drawHeartPath(ctx, -1, -p.size / 2 + 1, p.size * 0.45);
ctx.fill();
}
ctx.restore();
}
// Draw Floating Score & Words
for (let t of floatingTexts) {
ctx.save();
ctx.font = `bold ${Math.floor(18 * t.scale)}px sans-serif`;
ctx.fillStyle = `rgba(255, 230, 240, ${t.alpha})`;
ctx.shadowColor = '#ff007f';
ctx.shadowBlur = 10;
ctx.textAlign = 'center';
ctx.fillText(t.text, t.x, t.y);
ctx.restore();
}
// Draw Burger Paddle
drawHamburger(
paddle.x,
paddle.y,
paddle.width,
paddle.height,
paddle.squash,
paddle.stretch,
paddle.tilt
);
// Draw HUD (Score, Hearts, Lives)
ctx.save();
ctx.font = '800 18px sans-serif';
ctx.fillStyle = '#ffffff';
ctx.shadowColor = 'rgba(255, 20, 147, 0.8)';
ctx.shadowBlur = 8;
// Score & Hearts Counter
ctx.textAlign = 'left';
ctx.fillText(`SCORE: ${score}`, 24, 34);
// Hearts Icon + Count
ctx.fillStyle = '#ff4d79';
drawHeartPath(ctx, 190, 21, 16);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.fillText(`HEARTS: ${heartsCollected}`, 206, 34);
if (combo > 1) {
ctx.fillStyle = '#ffea00';
ctx.fillText(`${combo}x COMBO!`, 370, 34);
}
// Lives as cute mini-burgers or hearts
ctx.textAlign = 'right';
ctx.fillStyle = '#ffffff';
ctx.fillText(`LIVES:`, canvas.width - 90, 34);
for (let i = 0; i < lives; i++) {
ctx.save();
ctx.fillStyle = '#ff2b66';
drawHeartPath(ctx, canvas.width - 70 + (i * 22), 20, 16);
ctx.fill();
ctx.restore();
}
ctx.restore();
ctx.restore(); // for screen shake
}
// Main Animation Loop
function gameLoop() {
update();
render();
requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
})();
</script>
</body>
</html>Reasoning trace (708 tokens)
**Designing Engaging Breakout Elements** I'm currently refining the visual flair for the breakout experience. My focus is on bringing the hamburger paddle to life with intricate detail and ensuring the heart particle explosions are truly captivating and dynamic, creating a sense of joyous destruction. The AI demo is also being tuned. **Developing Immersive Breakout Mechanics** I'm currently focused on the finer points of gameplay feel. The AI is being tuned for immediate engagement, and I'm iterating on visual elements like the ball's trail and the hamburger paddle's procedural generation to ensure a truly "juicy" experience. The theme "Burger Breakout: Love at First Bite" is shaping the aesthetic. **Enhancing Breakout Visuals** I'm now focusing on the finer details of brick and explosion visuals. I'm planning pastel candy-like colors with gloss and depth for the bricks, and envisioning heart particles in various colors and physics for the explosions, accompanied by shockwaves and celebratory text. Audio integration is also in progress.