Hamburger breakoutOne-line
GPT-6.1 Terra
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 4 min 47 s
- Cost
- $0.109
- Output tokens
- 21,753
- Spent thinking
- 10,473 tokens
- Speed
- 78.7 tok/s
- Console errors
- 0
- First token after
- 10.5 s
- First visible text after
- 3 min 6 s
- Prompt tokens
- 357
- Code size
- 31.6 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6.1-terra- Reasoning sent
{"effort":"xhigh"}- Max output
- 128,000 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 · 752 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</title>
<style>
:root {
--ink: #492b39;
--paper: #fff8ed;
--pink: #f394a2;
--coral: #e86b7c;
--line: #54323f;
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
color: var(--ink);
font-family: Arial, Helvetica, sans-serif;
background-color: #f8eddf;
background-image:
radial-gradient(circle at 8% 12%, rgba(247, 181, 170, .27), transparent 25%),
radial-gradient(circle at 95% 90%, rgba(247, 190, 154, .27), transparent 25%),
linear-gradient(rgba(132, 79, 76, .035) 1px, transparent 1px),
linear-gradient(90deg, rgba(132, 79, 76, .035) 1px, transparent 1px);
background-size: auto, auto, 32px 32px, 32px 32px;
}
button { font: inherit; }
.page { max-width: 1228px; margin: 0 auto; padding: 24px 24px 18px; }
.header {
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 18px;
}
.brand { display: flex; align-items: center; gap: 13px; min-width: 0; }
.brand-mark {
width: 57px; height: 57px; flex: 0 0 57px;
display: grid; place-items: center;
background: #f5a2a6;
border: 2px solid var(--line);
border-radius: 15px;
box-shadow: 3px 3px 0 #e1b8aa;
}
.brand-mark svg { width: 43px; height: 43px; }
.eyebrow {
font-size: 9px; letter-spacing: 2px; font-weight: 800;
text-transform: uppercase; color: #a65d68; margin-bottom: 4px;
}
h1 {
font-family: Georgia, 'Times New Roman', serif;
font-size: 29px; line-height: 1; letter-spacing: -1.1px;
margin: 0; font-weight: 900;
}
.header-right { display: flex; align-items: center; gap: 13px; }
.header-note {
border: 1px solid #e6cabb; background: rgba(255, 250, 241, .72);
border-radius: 30px; padding: 10px 14px;
font-size: 9px; font-weight: 800; letter-spacing: 1.3px;
white-space: nowrap;
}
.header-note span { color: #e46d80; margin-right: 6px; }
.issue {
width: 42px; height: 42px; border: 1.5px solid var(--line);
border-radius: 50%; display: grid; place-items: center;
font-family: Georgia, serif; font-weight: bold; font-size: 15px;
background: #fff7ec;
}
.layout {
display: grid; grid-template-columns: minmax(0, 1fr) 258px;
gap: 18px; align-items: stretch;
}
.game-card, .feature, .instructions {
border: 2px solid var(--line);
box-shadow: 4px 5px 0 #e7c8bb;
border-radius: 17px; overflow: hidden;
}
.game-card { background: var(--paper); min-width: 0; }
.scorebar {
height: 67px; display: grid; grid-template-columns: 1fr 1fr 1fr;
align-items: center; background: #fff9ef;
}
.score-item { padding: 0 25px; min-width: 0; }
.score-item + .score-item { border-left: 1px solid #ead7ca; }
.score-item:nth-child(2) { text-align: center; }
.score-item:last-child { text-align: right; }
.score-label {
font-size: 9px; font-weight: 800; letter-spacing: 1.6px;
color: #ab7a77; margin-bottom: 5px;
}
.score-value {
font-family: 'Courier New', monospace; font-size: 20px;
font-weight: 900; line-height: 1; letter-spacing: 1px;
}
.level-value { font-size: 19px; }
.level-value small { font-family: Arial, sans-serif; font-size: 10px; color: #bd8e87; letter-spacing: 0; }
.lives { white-space: nowrap; font-size: 21px; color: #e96d81; letter-spacing: 3px; line-height: 1; }
.lives span { transition: opacity .2s; }
.lives span.lost { opacity: .18; }
.stage {
width: 100%; aspect-ratio: 900 / 510;
position: relative; overflow: hidden;
background: #402538; touch-action: none; cursor: crosshair;
border-top: 2px solid var(--line); border-bottom: 2px solid var(--line);
}
canvas { display: block; width: 100%; height: 100%; }
.start-card {
position: absolute; z-index: 2; left: 50%; top: 61%;
transform: translateX(-50%);
width: 310px; text-align: center; pointer-events: none;
background: #fff8ed; border: 2px solid #593442;
border-radius: 12px; padding: 10px 12px 9px;
box-shadow: 4px 4px 0 rgba(23, 10, 20, .25);
}
.start-card.hidden { display: none; }
.demo-label {
color: #d75f75; font-size: 9px; font-weight: 900;
letter-spacing: 1.5px; margin-bottom: 5px;
}
.demo-label:before {
content: ''; display: inline-block; width: 6px; height: 6px;
border-radius: 50%; background: #e66a7d; margin-right: 6px;
vertical-align: 1px; box-shadow: 0 0 0 3px #f9dce0;
}
.start-title { font-size: 13px; font-weight: 900; letter-spacing: .1px; }
.start-sub { margin-top: 4px; font-size: 10px; color: #a17575; font-weight: 600; }
.game-bottom {
min-height: 42px; display: flex; align-items: center;
justify-content: space-between; gap: 10px; padding: 0 17px;
background: #fff9ef;
}
.status {
font-size: 9px; font-weight: 800; letter-spacing: 1px;
color: #9c7772; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.status-dot {
display: inline-block; width: 7px; height: 7px; border-radius: 50%;
background: #e66c80; margin-right: 7px; box-shadow: 0 0 0 3px #f8dfe0;
}
.restart {
border: 0; background: none; padding: 8px 0 8px 8px;
color: #8e6264; cursor: pointer; font-size: 9px; font-weight: 900;
letter-spacing: 1px; white-space: nowrap;
}
.restart:hover { color: #d35d73; }
.restart b { font-size: 16px; vertical-align: -1px; margin-right: 3px; }
.sidebar { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.feature {
position: relative; flex: 0 0 275px; background: #f5b6b9;
padding: 17px 18px 0;
}
.section-kicker {
font-size: 9px; letter-spacing: 1.6px; font-weight: 900;
color: #a65363;
}
.feature h2, .instructions h2 {
margin: 8px 0 0; font: bold 25px/.99 Georgia, 'Times New Roman', serif;
letter-spacing: -.7px;
}
.feature h2 { max-width: 160px; }
.stamp {
position: absolute; right: 17px; top: 42px; width: 47px; height: 47px;
border: 1.5px solid #ad6370; border-radius: 50%; transform: rotate(12deg);
display: grid; place-content: center; text-align: center;
font-size: 8px; line-height: 1.25; font-weight: 900; letter-spacing: .5px;
color: #985564;
}
.burger-art { display: block; width: 100%; height: 151px; margin: 4px auto 0; }
.instructions {
flex: 1; background: #fff9ef; padding: 19px 18px 16px;
display: flex; flex-direction: column;
}
.instructions h2 { font-size: 23px; margin: 8px 0 13px; }
.how-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; }
.how-icon {
flex: 0 0 36px; width: 36px; height: 36px; display: grid; place-items: center;
border: 1px solid #ead2c7; border-radius: 10px;
background: #f9e9df; color: #dc6d7d; font-size: 20px; font-weight: 800;
}
.how-icon.arrows { font-size: 18px; color: #83535a; }
.how-copy strong {
display: block; font-size: 9px; letter-spacing: .8px; margin-bottom: 4px;
}
.how-copy span { display: block; font-size: 10px; color: #a17e78; line-height: 1.3; }
.play-button {
margin-top: auto; width: 100%; min-height: 43px;
background: #e66e7f; color: #fff9ef; border: 1.5px solid #643844;
border-radius: 10px; cursor: pointer; padding: 0 13px;
display: flex; justify-content: space-between; align-items: center;
font-size: 10px; letter-spacing: 1.1px; font-weight: 900;
box-shadow: 0 3px 0 #b85968;
}
.play-button:hover { background: #d96074; }
.play-button:active { transform: translateY(2px); box-shadow: 0 1px 0 #b85968; }
.play-button span { font-size: 18px; line-height: 1; }
button:focus-visible { outline: 3px solid #eba252; outline-offset: 3px; }
.page-footer {
display: flex; justify-content: space-between; gap: 12px; padding: 17px 2px 0;
color: #b78c83; font-size: 9px; font-weight: 800; letter-spacing: 1.4px;
}
.page-footer .heart { color: #e57282; }
@media (max-width: 800px) {
.layout { grid-template-columns: minmax(0, 1fr); }
.sidebar { display: grid; grid-template-columns: 1fr 1fr; align-items: stretch; }
.feature { min-height: 275px; }
.instructions { min-height: 275px; }
}
@media (max-width: 540px) {
.page { padding: 15px 14px 20px; }
.header { height: 54px; margin-bottom: 14px; }
.brand { gap: 10px; }
.brand-mark { width: 49px; height: 49px; flex-basis: 49px; border-radius: 13px; }
.brand-mark svg { width: 37px; height: 37px; }
h1 { font-size: 23px; letter-spacing: -.8px; }
.eyebrow { font-size: 8px; letter-spacing: 1.3px; }
.header-note { display: none; }
.issue { width: 35px; height: 35px; font-size: 13px; }
.layout { gap: 14px; }
.scorebar { height: 58px; }
.score-item { padding: 0 12px; }
.score-label { font-size: 8px; margin-bottom: 4px; letter-spacing: 1px; }
.score-value, .level-value { font-size: 16px; }
.lives { font-size: 17px; letter-spacing: 1px; }
.game-bottom { min-height: 38px; padding: 0 11px; }
.status, .restart { font-size: 8px; letter-spacing: .6px; }
.start-card { top: 50%; width: 230px; border-radius: 9px; padding: 5px 8px; border-width: 1.5px; }
.demo-label { font-size: 7px; margin-bottom: 3px; letter-spacing: 1px; }
.demo-label:before { width: 5px; height: 5px; box-shadow: 0 0 0 2px #f9dce0; }
.start-title { font-size: 10px; }
.start-sub { font-size: 8px; margin-top: 2px; }
.sidebar { grid-template-columns: 1fr; gap: 14px; }
.feature { min-height: 155px; height: 155px; padding: 17px; }
.feature h2 { font-size: 23px; max-width: 150px; }
.stamp { right: 46%; top: 91px; width: 38px; height: 38px; font-size: 7px; }
.burger-art { position: absolute; right: 8px; bottom: 0; width: 52%; height: 140px; margin: 0; }
.instructions { min-height: 280px; }
.page-footer { font-size: 8px; letter-spacing: .7px; }
}
</style>
</head>
<body>
<div class="page">
<header class="header">
<div class="brand">
<div class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 48 48" fill="none">
<path d="M6 23C7 14 13 10 24 10s17 4 18 13H6Z" fill="#F8C46F" stroke="#54323F" stroke-width="2" stroke-linejoin="round"/>
<path d="M9 28h30c2 0 3 2 2 4H7c-1-2 0-4 2-4Z" fill="#714138" stroke="#54323F" stroke-width="1.6"/>
<path d="m7 25 5 2 5-2 5 2 5-2 5 2 5-2 4 1" stroke="#6A9B67" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 33h32c0 4-3 6-7 6H15c-4 0-7-2-7-6Z" fill="#ECA75C" stroke="#54323F" stroke-width="2"/>
<path d="m16 17 2-1m6 0 2-1m5 3 2-1" stroke="#FFF1CE" stroke-width="2" stroke-linecap="round"/>
<path d="M24 7C21 4 17 6 18 9c.5 2 6 5 6 5s5.5-3 6-5c1-3-3-5-6-2Z" fill="#EB657B" stroke="#54323F" stroke-width="1"/>
</svg>
</div>
<div>
<div class="eyebrow">The feel-good arcade</div>
<h1>Burger Breakout</h1>
</div>
</div>
<div class="header-right">
<div class="header-note"><span>✦</span> GOOD FOOD. GOOD MOOD.</div>
<div class="issue">01</div>
</div>
</header>
<main class="layout">
<section class="game-card" aria-label="Burger Breakout game">
<div class="scorebar">
<div class="score-item">
<div class="score-label">SCORE</div>
<div class="score-value" id="score">00000</div>
</div>
<div class="score-item">
<div class="score-label">LEVEL</div>
<div class="score-value level-value">01 <small>/ 01</small></div>
</div>
<div class="score-item">
<div class="score-label">LIVES</div>
<div class="lives" aria-label="3 lives"><span>♥</span><span>♥</span><span>♥</span></div>
</div>
</div>
<div class="stage" id="stage">
<canvas id="game" aria-label="Breakout game with a hamburger paddle and heart explosions"></canvas>
<div class="start-card" id="startCard">
<div class="demo-label" id="overlayLabel">AUTO-PLAY IS ON</div>
<div class="start-title" id="overlayTitle">Click or press a key to start</div>
<div class="start-sub" id="overlaySub">Mouse, touch, or arrow keys to move</div>
</div>
</div>
<div class="game-bottom">
<div class="status"><span class="status-dot"></span><span id="statusText">DEMO MODE · PADDLE ON AUTOPILOT</span></div>
<button class="restart" id="restart" type="button"><b>↻</b> RESTART</button>
</div>
</section>
<aside class="sidebar">
<section class="feature">
<div class="section-kicker">✦ THE STAR OF THE SHOW</div>
<h2>One very<br>juicy paddle.</h2>
<div class="stamp">100%<br>CUTE</div>
<svg class="burger-art" viewBox="0 0 220 150" aria-label="Illustration of a cute hamburger">
<defs>
<linearGradient id="bun" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#FFD68B"/><stop offset="1" stop-color="#EAA554"/>
</linearGradient>
</defs>
<ellipse cx="110" cy="137" rx="80" ry="8" fill="#d68f98" opacity=".5"/>
<ellipse cx="110" cy="130" rx="78" ry="10" fill="#fff2df" stroke="#6b4148" stroke-width="2"/>
<path d="M44 105h132c0 13-7 20-18 20H62c-11 0-18-7-18-20Z" fill="#e7a25b" stroke="#6b4148" stroke-width="2.5"/>
<path d="M48 112h124" stroke="#c78246" stroke-width="2" opacity=".6"/>
<rect x="38" y="97" width="144" height="14" rx="7" fill="#704039" stroke="#54323f" stroke-width="2.5"/>
<path d="M45 98h130" stroke="#a8684a" stroke-width="2" stroke-linecap="round"/>
<path d="M46 91h128v9H46z" fill="#ed6b61" stroke="#6b4148" stroke-width="2"/>
<path d="M81 99h47l-20 13Z" fill="#ffd35e" stroke="#ca9842" stroke-width="1.5"/>
<path d="m40 92 10-5 10 5 10-5 10 5 10-5 10 5 10-5 10 5 10-5 10 5 10-5 10 5 10-5 10 5" fill="none" stroke="#699960" stroke-width="7" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M40 87c3-34 26-51 70-51s67 17 70 51c.2 3-2 5-5 5H45c-3 0-5-2-5-5Z" fill="url(#bun)" stroke="#6b4148" stroke-width="2.5"/>
<path d="M53 76c5-20 24-30 43-32" fill="none" stroke="#ffdf9f" stroke-width="3" stroke-linecap="round"/>
<g stroke="#fff0c9" stroke-width="3" stroke-linecap="round">
<path d="m71 59 4-2m20-7 4 2m23-1 4-2m19 9 4 2m-67 9 4 2m49-2 4-2"/>
</g>
<circle cx="99" cy="78" r="2.4" fill="#603b37"/><circle cx="121" cy="78" r="2.4" fill="#603b37"/>
<path d="M106 82q4 5 8 0" fill="none" stroke="#603b37" stroke-width="1.8" stroke-linecap="round"/>
<ellipse cx="91" cy="82" rx="4" ry="2" fill="#ee8d83" opacity=".7"/>
<ellipse cx="129" cy="82" rx="4" ry="2" fill="#ee8d83" opacity=".7"/>
<path d="M110 36V17" stroke="#6b4148" stroke-width="2" stroke-linecap="round"/>
<path d="M110 20c-2-2-6-4-6-7 0-3 4-4 6-1 2-3 6-2 6 1 0 3-4 5-6 7Z" fill="#ea6f82" stroke="#6b4148" stroke-width="1.5"/>
</svg>
</section>
<section class="instructions">
<div class="section-kicker">✦ HOW TO PLAY</div>
<h2>Easy peasy,<br>extra cheesy.</h2>
<div class="how-row">
<div class="how-icon arrows">↔</div>
<div class="how-copy"><strong>MOVE YOUR BURGER</strong><span>Mouse, touch or ← → keys</span></div>
</div>
<div class="how-row">
<div class="how-icon">✦</div>
<div class="how-copy"><strong>BREAK THE BRICKS</strong><span>Keep that ball bouncing</span></div>
</div>
<div class="how-row">
<div class="how-icon">♥</div>
<div class="how-copy"><strong>LET LOVE FLY</strong><span>Every brick bursts into hearts!</span></div>
</div>
<button class="play-button" id="playButton" type="button">LET'S PLAY <span>↗</span></button>
</section>
</aside>
</main>
<footer class="page-footer">
<span>MADE WITH <span class="heart">♥</span> & A LITTLE MAYHEM</span>
<span>BOUNCE. BREAK. REPEAT. ✦</span>
</footer>
</div>
<script>
(() => {
const canvas = document.getElementById('game');
const stage = document.getElementById('stage');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const livesEl = document.querySelector('.lives');
const lifeSpans = [...livesEl.querySelectorAll('span')];
const startCard = document.getElementById('startCard');
const overlayLabel = document.getElementById('overlayLabel');
const overlayTitle = document.getElementById('overlayTitle');
const overlaySub = document.getElementById('overlaySub');
const statusText = document.getElementById('statusText');
const playButton = document.getElementById('playButton');
const W = 900, H = 510;
const paddle = { x: 450, y: 451, width: 132 };
const ball = { x: 450, y: 300, vx: 170, vy: -330, r: 8 };
let bricks = [], particles = [], rings = [], popups = [], trail = [];
let mode = 'demo', score = 0, lives = 3, time = 0, targetX = 450;
let leftDown = false, rightDown = false;
let last = 0;
const brickColors = [
['#ff9eae', '#ee7188'],
['#ffb5ad', '#f38d8f'],
['#ffc991', '#f3a66c'],
['#ffe0a2', '#f5c07b'],
['#f8adc1', '#eb82a1']
];
const heartColors = ['#ff7997', '#ff9eaf', '#ffced1', '#fff0d2', '#ffd080', '#f56a7d'];
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const rand = (a, b) => a + Math.random() * (b - a);
function resize() {
const rect = stage.getBoundingClientRect();
const dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = Math.max(1, Math.round(rect.width * dpr));
canvas.height = Math.max(1, Math.round(rect.height * dpr));
}
window.addEventListener('resize', resize);
function makeBricks() {
bricks = [];
const gap = 8, width = (W - 96 - gap * 9) / 10;
for (let row = 0; row < 5; row++) {
for (let col = 0; col < 10; col++) {
bricks.push({ x: 48 + col * (width + gap), y: 56 + row * 37, w: width, h: 30, row, alive: true });
}
}
}
function updateUI() {
scoreEl.textContent = String(score).padStart(5, '0');
lifeSpans.forEach((el, i) => el.classList.toggle('lost', i >= lives));
livesEl.setAttribute('aria-label', lives + (lives === 1 ? ' life' : ' lives'));
}
function resetBall(demo) {
ball.x = demo ? 450 : paddle.x;
ball.y = demo ? 300 : 390;
ball.vx = demo ? 170 : (Math.random() < .5 ? -145 : 145);
ball.vy = -330;
trail = [];
}
function setupDemo() {
mode = 'demo'; score = 0; lives = 3; time = 0;
paddle.x = 450; targetX = 450;
particles = []; rings = []; popups = [];
makeBricks(); resetBall(true); updateUI();
}
function startGame() {
mode = 'playing'; score = 0; lives = 3;
paddle.x = 450; targetX = 450;
leftDown = false; rightDown = false;
particles = []; rings = []; popups = [];
makeBricks(); resetBall(false); updateUI();
startCard.classList.add('hidden');
statusText.textContent = "YOU'RE IN CONTROL · GOOD LUCK!";
playButton.innerHTML = 'RESTART GAME <span>↻</span>';
}
function finishGame(won) {
mode = 'ended';
overlayLabel.textContent = won ? 'ORDER COMPLETE' : 'GAME OVER';
overlayTitle.textContent = won ? 'You smashed it!' : 'All out of lives!';
overlaySub.textContent = 'Click or press a key to play again';
startCard.classList.remove('hidden');
statusText.textContent = won ? 'ALL BRICKS BROKEN · NICE WORK!' : 'GAME OVER · GIVE IT ANOTHER GO';
playButton.innerHTML = 'PLAY AGAIN <span>↗</span>';
}
function heart(x, y, size, color, rotation = 0, alpha = 1) {
ctx.save();
ctx.translate(x, y); ctx.rotate(rotation); ctx.scale(size, size);
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(0, .85);
ctx.bezierCurveTo(-.18, .7, -1, .2, -1, -.3);
ctx.bezierCurveTo(-1, -.85, -.35, -1, 0, -.48);
ctx.bezierCurveTo(.35, -1, 1, -.85, 1, -.3);
ctx.bezierCurveTo(1, .2, .18, .7, 0, .85);
ctx.closePath(); ctx.fill(); ctx.restore();
}
function rounded(x, y, w, h, r, fill) {
ctx.beginPath(); ctx.roundRect(x, y, w, h, r);
ctx.fillStyle = fill; ctx.fill();
}
function sparkle(x, y, size, color, alpha) {
ctx.save(); ctx.translate(x, y); ctx.globalAlpha = alpha;
ctx.fillStyle = color; ctx.beginPath();
ctx.moveTo(0, -size); ctx.quadraticCurveTo(size * .18, -size * .18, size, 0);
ctx.quadraticCurveTo(size * .18, size * .18, 0, size);
ctx.quadraticCurveTo(-size * .18, size * .18, -size, 0);
ctx.quadraticCurveTo(-size * .18, -size * .18, 0, -size);
ctx.fill(); ctx.restore();
}
function burst(brick) {
const x = brick.x + brick.w / 2, y = brick.y + brick.h / 2;
for (let i = 0; i < 46; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = rand(35, 205);
const life = rand(1.05, 2.15);
particles.push({
x: x + rand(-12, 12), y: y + rand(-5, 5),
vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed - 35,
size: rand(2.8, 6.2), life, maxLife: life,
color: heartColors[Math.floor(Math.random() * heartColors.length)],
rotation: rand(-1, 1), spin: rand(-2.2, 2.2), star: i % 9 === 0
});
}
rings.push({ x, y, age: 0 });
popups.push({ x, y: y - 8, age: 0 });
}
function updateEffects(dt) {
particles = particles.filter(p => p.life > 0);
for (const p of particles) {
p.life -= dt; p.x += p.vx * dt; p.y += p.vy * dt;
p.vy += 95 * dt; p.vx *= Math.pow(.94, dt);
p.rotation += p.spin * dt;
}
rings = rings.filter(r => r.age < .55);
rings.forEach(r => r.age += dt);
popups = popups.filter(p => p.age < 1);
popups.forEach(p => p.age += dt);
}
function predictedLanding() {
if (ball.vy <= 0) return ball.x;
const seconds = Math.max(0, (paddle.y - ball.r - ball.y) / ball.vy);
let x = ball.x + ball.vx * seconds;
const min = ball.r, span = W - 2 * ball.r;
let n = (x - min) % (span * 2);
if (n < 0) n += span * 2;
if (n > span) n = span * 2 - n;
return min + n;
}
function update(dt) {
time += dt;
updateEffects(dt);
if (mode === 'ended') return;
if (mode === 'demo') {
targetX = predictedLanding() + Math.sin(time * 1.7) * 19;
const desired = clamp(targetX, paddle.width / 2 + 7, W - paddle.width / 2 - 7);
paddle.x += clamp(desired - paddle.x, -740 * dt, 740 * dt);
} else {
if (leftDown || rightDown) {
paddle.x += ((rightDown ? 1 : 0) - (leftDown ? 1 : 0)) * 620 * dt;
targetX = paddle.x;
} else {
paddle.x += clamp(targetX - paddle.x, -1100 * dt, 1100 * dt);
}
paddle.x = clamp(paddle.x, paddle.width / 2 + 7, W - paddle.width / 2 - 7);
}
const oldX = ball.x, oldY = ball.y;
ball.x += ball.vx * dt; ball.y += ball.vy * dt;
trail.unshift({ x: ball.x, y: ball.y });
if (trail.length > 9) trail.pop();
if (ball.x - ball.r < 8) { ball.x = 8 + ball.r; ball.vx = Math.abs(ball.vx); }
if (ball.x + ball.r > W - 8) { ball.x = W - 8 - ball.r; ball.vx = -Math.abs(ball.vx); }
if (ball.y - ball.r < 35) { ball.y = 35 + ball.r; ball.vy = Math.abs(ball.vy); }
for (const b of bricks) {
if (!b.alive) continue;
const nearestX = clamp(ball.x, b.x, b.x + b.w);
const nearestY = clamp(ball.y, b.y, b.y + b.h);
const dx = ball.x - nearestX, dy = ball.y - nearestY;
if (dx * dx + dy * dy <= ball.r * ball.r) {
b.alive = false;
if (oldY + ball.r <= b.y + 1) {
ball.y = b.y - ball.r; ball.vy = -Math.abs(ball.vy);
} else if (oldY - ball.r >= b.y + b.h - 1) {
ball.y = b.y + b.h + ball.r; ball.vy = Math.abs(ball.vy);
} else if (oldX < b.x) {
ball.x = b.x - ball.r; ball.vx = -Math.abs(ball.vx);
} else {
ball.x = b.x + b.w + ball.r; ball.vx = Math.abs(ball.vx);
}
score += 10; updateUI(); burst(b);
if (!bricks.some(br => br.alive) && mode === 'playing') finishGame(true);
else if (!bricks.some(br => br.alive) && mode === 'demo') makeBricks();
break;
}
}
if (ball.vy > 0 && oldY + ball.r <= paddle.y + 4 &&
ball.y + ball.r >= paddle.y + 4 &&
ball.x >= paddle.x - paddle.width / 2 - ball.r &&
ball.x <= paddle.x + paddle.width / 2 + ball.r) {
ball.y = paddle.y + 4 - ball.r;
const offset = clamp((ball.x - paddle.x) / (paddle.width / 2), -1, 1);
const speed = Math.min(440, 385 + score * .035);
ball.vx = offset * speed * .83;
if (Math.abs(ball.vx) < 75) ball.vx = ball.vx < 0 ? -75 : 75;
ball.vy = -Math.sqrt(speed * speed - ball.vx * ball.vx);
}
if (ball.y - ball.r > H + 10) {
if (mode === 'demo') {
resetBall(true);
} else {
lives--; updateUI();
if (lives <= 0) finishGame(false);
else resetBall(false);
}
}
}
function drawBackground() {
const bg = ctx.createLinearGradient(0, 0, 0, H);
bg.addColorStop(0, '#542c40'); bg.addColorStop(1, '#2e2133');
ctx.fillStyle = bg; ctx.fillRect(0, 0, W, H);
ctx.strokeStyle = 'rgba(255,230,218,.045)'; ctx.lineWidth = 1;
ctx.beginPath();
for (let x = 0; x < W; x += 45) { ctx.moveTo(x, 0); ctx.lineTo(x, H); }
for (let y = 0; y < H; y += 45) { ctx.moveTo(0, y); ctx.lineTo(W, y); }
ctx.stroke();
ctx.fillStyle = 'rgba(255,228,218,.47)';
ctx.font = 'bold 10px Arial'; ctx.textAlign = 'left';
ctx.fillText('✦ FRESHLY MADE FUN', 48, 30);
ctx.textAlign = 'right'; ctx.fillText('BREAK BRICKS, MAKE HEARTS ✦', 852, 30);
ctx.textAlign = 'left';
heart(48, 359, 9, '#ef9bab', -.18, .13);
heart(856, 325, 8, '#ef9bab', .18, .13);
heart(117, 428, 5, '#fbd5be', .2, .13);
sparkle(77, 290, 8, '#ffd7ba', .25);
sparkle(813, 390, 8, '#ffd7ba', .23);
sparkle(843, 274, 4, '#ffd7ba', .3);
ctx.strokeStyle = 'rgba(255,224,210,.12)';
ctx.setLineDash([4, 8]); ctx.beginPath();
ctx.moveTo(48, 493); ctx.lineTo(852, 493); ctx.stroke(); ctx.setLineDash([]);
}
function drawBricks() {
for (const b of bricks) {
if (!b.alive) continue;
rounded(b.x, b.y + 4, b.w, b.h, 6, 'rgba(25, 10, 24, .3)');
const g = ctx.createLinearGradient(0, b.y, 0, b.y + b.h);
g.addColorStop(0, brickColors[b.row][0]);
g.addColorStop(1, brickColors[b.row][1]);
rounded(b.x, b.y, b.w, b.h, 6, g);
ctx.strokeStyle = 'rgba(255,255,255,.35)'; ctx.lineWidth = 1;
ctx.beginPath(); ctx.roundRect(b.x + .5, b.y + .5, b.w - 1, b.h - 1, 5.5); ctx.stroke();
ctx.strokeStyle = 'rgba(255,255,255,.33)'; ctx.lineWidth = 2; ctx.lineCap = 'round';
ctx.beginPath(); ctx.moveTo(b.x + 8, b.y + 5); ctx.lineTo(b.x + 20, b.y + 5); ctx.stroke();
heart(b.x + b.w / 2, b.y + 16, 5, '#fff8ed', 0, .25);
}
}
function drawBurger() {
const x = paddle.x - paddle.width / 2, y = paddle.y;
ctx.save();
ctx.fillStyle = 'rgba(12,7,16,.27)';
ctx.beginPath(); ctx.ellipse(paddle.x, y + 39, 74, 7, 0, 0, Math.PI * 2); ctx.fill();
const lower = ctx.createLinearGradient(0, y + 26, 0, y + 39);
lower.addColorStop(0, '#f4bd72'); lower.addColorStop(1, '#c77c3d');
rounded(x + 5, y + 25, 122, 14, 7, lower);
ctx.strokeStyle = '#89532f'; ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.roundRect(x + 5, y + 25, 122, 14, 7); ctx.stroke();
rounded(x, y + 21, 132, 11, 5, '#713d32');
rounded(x + 5, y + 18, 122, 8, 4, '#eb685c');
ctx.fillStyle = '#ffd560'; ctx.beginPath();
ctx.moveTo(x + 25, y + 22); ctx.lineTo(x + 77, y + 22);
ctx.lineTo(x + 60, y + 32); ctx.lineTo(x + 51, y + 24); ctx.closePath(); ctx.fill();
ctx.strokeStyle = '#79a763'; ctx.lineWidth = 6; ctx.lineJoin = 'round'; ctx.lineCap = 'round';
ctx.beginPath(); ctx.moveTo(x + 4, y + 21);
for (let i = 1; i <= 16; i++) ctx.lineTo(x + 4 + i * 7.7, y + (i % 2 ? 17 : 21));
ctx.stroke();
const bun = ctx.createLinearGradient(0, y, 0, y + 21);
bun.addColorStop(0, '#ffdb91'); bun.addColorStop(.7, '#f3b765'); bun.addColorStop(1, '#d9944c');
ctx.beginPath(); ctx.moveTo(x + 4, y + 18);
ctx.bezierCurveTo(x + 8, y + 4, x + 26, y - 2, x + 66, y - 2);
ctx.bezierCurveTo(x + 106, y - 2, x + 124, y + 4, x + 128, y + 18);
ctx.quadraticCurveTo(x + 129, y + 21, x + 123, y + 21);
ctx.lineTo(x + 9, y + 21); ctx.quadraticCurveTo(x + 3, y + 21, x + 4, y + 18);
ctx.fillStyle = bun; ctx.fill(); ctx.strokeStyle = '#a66738'; ctx.lineWidth = 1.5; ctx.stroke();
ctx.strokeStyle = '#fff1c7'; ctx.lineWidth = 2.1; ctx.lineCap = 'round';
const seeds = [[25,10],[42,5],[58,11],[76,4],[94,10],[109,7]];
seeds.forEach((s, i) => {
ctx.beginPath(); ctx.moveTo(x + s[0], y + s[1]);
ctx.lineTo(x + s[0] + 3, y + s[1] + (i % 2 ? 1.5 : -1.5)); ctx.stroke();
});
ctx.restore();
}
function drawEffects() {
for (const r of rings) {
ctx.save(); ctx.globalAlpha = Math.max(0, 1 - r.age / .55) * .7;
ctx.strokeStyle = '#ffe0bc'; ctx.lineWidth = 2;
ctx.beginPath(); ctx.arc(r.x, r.y, 8 + r.age * 100, 0, Math.PI * 2); ctx.stroke(); ctx.restore();
}
for (const p of particles) {
const alpha = clamp(p.life / .45, 0, 1);
if (p.star) sparkle(p.x, p.y, p.size * 1.1, p.color, alpha);
else heart(p.x, p.y, p.size, p.color, p.rotation, alpha);
}
for (const p of popups) {
ctx.save(); ctx.globalAlpha = Math.min(1, (1 - p.age) * 2);
ctx.textAlign = 'center'; ctx.font = 'bold 15px Arial';
ctx.fillStyle = '#fff0d5'; ctx.fillText('+10', p.x, p.y - p.age * 35);
ctx.restore();
}
}
function drawBall() {
for (let i = trail.length - 1; i >= 1; i--) {
const p = trail[i];
ctx.beginPath(); ctx.arc(p.x, p.y, ball.r * (1 - i / 12), 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 219, 175, ${.17 * (1 - i / 10)})`; ctx.fill();
}
ctx.save(); ctx.shadowColor = '#ffb79c'; ctx.shadowBlur = 14;
ctx.beginPath(); ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
ctx.fillStyle = '#fff0d1'; ctx.fill(); ctx.restore();
ctx.beginPath(); ctx.arc(ball.x - 2, ball.y - 2.5, 2.2, 0, Math.PI * 2);
ctx.fillStyle = '#fff'; ctx.fill();
}
function render() {
ctx.setTransform(canvas.width / W, 0, 0, canvas.height / H, 0, 0);
ctx.clearRect(0, 0, W, H);
drawBackground(); drawBricks(); drawEffects(); drawBurger(); drawBall();
}
function frame(now) {
if (!last) last = now;
const dt = Math.min((now - last) / 1000, .035);
last = now;
update(dt); render();
requestAnimationFrame(frame);
}
function pointerX(e) {
const rect = stage.getBoundingClientRect();
return clamp((e.clientX - rect.left) / rect.width * W, paddle.width / 2 + 7, W - paddle.width / 2 - 7);
}
stage.addEventListener('pointerdown', e => {
if (mode !== 'playing') startGame();
targetX = pointerX(e);
if (e.pointerType !== 'mouse') stage.setPointerCapture(e.pointerId);
e.preventDefault();
});
stage.addEventListener('pointermove', e => {
if (mode === 'playing') targetX = pointerX(e);
});
window.addEventListener('keydown', e => {
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', ' '].includes(e.key)) e.preventDefault();
if (e.ctrlKey || e.metaKey || e.altKey || e.key === 'Tab') return;
if (mode !== 'playing') startGame();
if (e.key === 'ArrowLeft' || e.key.toLowerCase() === 'a') leftDown = true;
if (e.key === 'ArrowRight' || e.key.toLowerCase() === 'd') rightDown = true;
});
window.addEventListener('keyup', e => {
if (e.key === 'ArrowLeft' || e.key.toLowerCase() === 'a') leftDown = false;
if (e.key === 'ArrowRight' || e.key.toLowerCase() === 'd') rightDown = false;
});
window.addEventListener('blur', () => { leftDown = false; rightDown = false; });
playButton.addEventListener('click', startGame);
document.getElementById('restart').addEventListener('click', startGame);
resize();
setupDemo();
render();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>Reasoning trace (10,473 tokens)
**Designing game layout** I need to establish a visual hierarchy for the game, considering a header with a logo, status, and possibly a side panel. The game's best feature should fill the viewport but still be rich in design. I’m thinking of a layout that's 1200 pixels wide, with a spacious playfield and side information for players. The game area could have a dark plum background, detailed bricks, and clever heart motifs. I'll also ensure that the overall UI is cohesive and fits nicely within the first viewport.**Structuring the game UI** I’m thinking about integrating game information into a separate top bar while the right side shows how to play, stats, and a legend, plus a Start button and a hint. The game canvas would be around 865 by 535 pixels, including a header UI and a bottom status area. I need to ensure responsiveness, especially for smaller screens, so at 375 pixels, the game and info should stack without overflow. For the desktop version, I’m aiming for a main width of 1150 pixels and a header height of 76 pixels.**Designing the game stage** I’m working on the game stage design. The frame size is set, and I’m considering colors like dark burgundy for vibes. I’ll use a dark purple base with a faint grid and some star decorations. The background will have a burgundy gradient. I’m planning a brick grid of 9 columns by 5 rows in candy colors. Each brick will pop into hearts when hit, contributing to the score and enhancing gameplay with automatic AI movement.**Refining game mechanics** I’m evaluating the heart mechanics after the second burst, considering extending their life for 2 seconds with 35 hearts that float down due to gravity. Minimizing visual overlap seems critical. I could introduce a demo mode with an accelerated speed and incorporate some breaks. The ball speed is around 360, with necessary timing for the first hit and subsequent frames. I need to ensure enough bricks remain and that user interactions work smoothly, including paddle movement and game resets. An overlay message should be positioned without obscuring the actual gameplay.**Designing overlay display** I'm considering the overlay display, aiming for the center at around 58% on the Y-axis, with a message card saying "CLICK OR PRESS A KEY TO START" and a note that "Demo is playing for you." I have to make sure it doesn't obstruct gameplay. My idea is to position the overlay at the bottom, right above the paddle. I want a translucent rectangular area with rounded corners and a small title, and a click prompt centered about 65 pixels from the bottom.**Designing game mechanics** I’m working on defining the game stage size and layout. I need robust mechanics for starting and pausing. A demo mode would be good, with manual play once clicked, and options to restart after wins/losses. I’ll ensure score updates, lives lost, and win overlays when there are no bricks left. The controls should respond to mouse, touch, and keyboard, and during the demo, the AI will anticipate the ball’s path. I'm also considering maintaining ball trajectory variability.**Adjusting paddle mechanics** I'm focusing on setting up the paddle's offset to create varied angles when hitting the ball. The AI will anticipate the predicted landing by reflecting based on the distance to the paddle line, with a slight random offset for unpredictability. In demo mode, the paddle will track the target while maintaining velocity. I need to make sure the ball doesn't get stuck vertically, and when it hits the paddle, I'll compute relative positions to ensure there's minimal horizontal movement if it's near the center.**Designing a Brick Layout** I’m thinking of using star shapes and tiny motifs for a visually appealing design. For the brick labels, I imagine each one with a heart icon in low contrast. I’m considering a layout of 10 columns, with specific dimensions for spacing and placement, totaling 50 bricks. Adding small white, slightly transparent heart shapes in each brick's center would create nice visual texture. I want to use a gradient of colors from hot pink to light pink for an attractive look.**Adjusting display settings** I’m thinking about setting up a grid display with a flex column layout. The stage width should be full, and I'll keep the aspect ratio fixed. It’s like 900 by 510, which equals a height of 185 at a width of 327. I realize it’s pretty small, but it should still be playable, especially since the burger canvas is scaled. Let’s see how that works out in practice!**Refining interface design** I’m working on a CSS overlay, needing to consider bottom percentages and ensuring that a screenshot at 375 pixels captures everything well. On mobile, I'll set the overlay font to 9 and widths to 230. The canvas stage will be playable via pointer, and I’m thinking of a compact scoreboard. Side cards might need a 2-column layout on mobile, and at 375 pixels, the first card will feature a burger illustration. I should also address footer placement and some interface style references.**Designing scoreboard graphics** I’m creating a game scoreboard with a logo that features an inline SVG of a hamburger with a heart, titled "BURGER BREAKOUT". The header will be split, with the logo in a rounded coral square alongside the overline saying "THE FEEL-GOOD ARCADE". I’m planning to use a canvas set to a scaled world of 900x510 pixels. For handling paddle movement and ball physics, I'll apply devicePixelRatio for accurate rendering in the canvas. I'll also implement pointer events for user interaction, making sure the paddle reacts accurately!**Setting up game controls** I need to implement the side start play sets mode and update the scoreboard while hiding the start overlay and focusing on the stage. I’ll call `resetGame(false)` to ensure everything is in place. For keyboard interactions, I’ll add an event listener to capture keydown events. If the left or right arrow keys are pressed, I'll prevent the default action. Depending on whether the game is in demo mode or has ended, I'll initiate `startGame()`. This should set up user controls effectively!**Considering canvas design elements** I’m thinking that maybe I should use a heart shape instead of actual Unicode in the canvas. I want to avoid HTML emojis since their appearance can differ across operating systems. Instead, I could consider using SVG or text stars and utilize icons in the form of simple vectors or outlines. I need to ensure there are no errors with listeners and resizing, and make sure to include `aria-label` for accessibility. Plus, I should think about whether users might expect the ability to drag on mobile.**Considering particle effects** I’m thinking the particles should update before the ball, calling `updateParticles(dt)` every frame. I want to set gravity to 85 and damping to 0.995. The heart should rotate, and the explosion rings will update with brick bursts scoring at the center. When the ball hits, I can emit little heart-shaped pieces outward at a 45-degree angle. I’ll use vivid colors since they will stand out on dark backgrounds. I’ll draw the particles from `p.pos`, with heart sizes ranging from 2.3 to 5.7, but no faint outlines.**Optimizing game display** I’m looking at the math for scoring and lives, wanting to optimize using `setLives` with dynamic hearts. Maybe I can avoid `innerHTML` by using static spans with opacity instead. I’ll keep an eye on performance and avoid any errors. I need to position the overlay at the right spots for both desktop and mobile. It seems there could be issues with paddle placement and overlay on mobile as well, so I’ll adjust those accordingly.**Refining CSS layout** I'm working on the CSS for the intro section, setting parameters like width and padding, making sure the title is clearly visible. Mobile adjustments involve a narrower width and smaller font sizes. I have to check if the desktop layout leaves any blank areas at the bottom. For the grid layout, I'm calculating the maximum width based on the viewport, ensuring everything really fits, including the footer and header. Overall, it looks good with all elements in their proper positions!**Calculating sidebar layout** I’m working on the sidebar feature, which is about 280 in width, and I need to add the controls that take up 325. There's also a gap of 14 to consider. When I calculate this all together, it sums up to 619 in total. It looks like everything fits nicely, but I should double-check if there's enough space for the content. I want to ensure that the layout is practical and user-friendly.**Analyzing game mechanics** I'm looking into the game mechanics involving lives and score. When the ball drops, the lives decrease, and particles move while popups update. There’s a "restart" button that needs to reset the game instead of just starting the demo. I'll also focus on updating the score using a specific format to keep it consistent. I want to ensure that when a key is pressed, it gets registered properly, especially if the Space key is involved. The color and text layout are looking good, so I’ll provide that now.