Run details
- Model
- openai/gpt-6.1-terra
- Route
- OpenRouter API, one user message
- Served by
- OpenAI
- Effort
- medium, OpenRouter's default for this model
- Limits
- 60 min · 128,000 output tokens (the model's limit)
- Date
- 26 Oct 2026
- Time
- 85 s
- Size
- 21 kB
- Tokens
- 83 in, 7,638 out (976 of them thinking)
- List price
- $0.038 at today's price ($1 in / $5 out per M)
- Paid
- $0.038
Exact prompt
Make a Breakout game I can play right now with the keyboard or the mouse. Give it several levels, a score and some juice. It must be one complete, self-contained HTML file: inline all CSS and JavaScript, and make no network requests of any kind (no CDNs, web fonts or remote images). Reply with the file in a single ```html code block.
Source
21 kB 700 lines Raw
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Neon Breakout</title>
<style>
:root {
color-scheme: dark;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: #080b18;
color: #eef4ff;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 24px 14px;
background:
radial-gradient(circle at 20% 10%, #232253 0, transparent 32%),
radial-gradient(circle at 85% 90%, #123044 0, transparent 30%),
#080b18;
}
main { width: min(100%, 960px); }
header {
display: flex;
justify-content: space-between;
align-items: end;
gap: 16px;
margin-bottom: 16px;
}
h1 {
margin: 0;
font-size: clamp(1.5rem, 4vw, 2.4rem);
letter-spacing: -.05em;
line-height: 1;
}
h1 span { color: #67e8f9; }
.subtitle { margin: 6px 0 0; color: #98a8c6; font-size: .9rem; }
.stats { display: flex; gap: clamp(12px, 3vw, 30px); }
.stat { text-align: right; }
.stat small {
display: block;
color: #91a1be;
font-size: .65rem;
font-weight: 800;
letter-spacing: .13em;
}
.stat strong {
display: block;
margin-top: 3px;
font-size: clamp(1rem, 2.5vw, 1.35rem);
font-variant-numeric: tabular-nums;
}
.game {
position: relative;
overflow: hidden;
border: 1px solid #34436c;
border-radius: 16px;
background: #090f22;
box-shadow: 0 24px 70px #0008, 0 0 35px #42d9ff10;
}
canvas {
display: block;
width: 100%;
height: auto;
aspect-ratio: 3 / 2;
touch-action: none;
cursor: crosshair;
}
.overlay {
position: absolute;
inset: 0;
display: grid;
place-items: center;
text-align: center;
padding: 20px;
background: #060b1aa8;
pointer-events: none;
}
.overlay[hidden] { display: none; }
.overlay-box {
max-width: 440px;
padding: 24px 30px;
border: 1px solid #7084bc55;
border-radius: 18px;
background: #101a32e8;
box-shadow: 0 12px 50px #0008;
}
.eyebrow {
color: #67e8f9;
font-size: .7rem;
font-weight: 900;
letter-spacing: .2em;
text-transform: uppercase;
}
.overlay h2 {
margin: 8px 0;
font-size: clamp(1.5rem, 5vw, 2.5rem);
letter-spacing: -.04em;
}
.overlay p { margin: 0; color: #b5c5e3; line-height: 1.5; }
.overlay .hint { margin-top: 14px; color: #fff; font-size: .9rem; }
.toolbar {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 9px;
margin-top: 14px;
}
button {
border: 1px solid #47567a;
border-radius: 9px;
padding: 10px 15px;
background: #192540;
color: #edf6ff;
font: inherit;
font-size: .85rem;
font-weight: 750;
cursor: pointer;
}
button:hover { background: #26395c; }
button:focus-visible { outline: 3px solid #67e8f9; outline-offset: 3px; }
#playButton { background: #67e8f9; color: #071523; border-color: #67e8f9; }
#playButton:hover { background: #a5f3fc; }
.controls {
margin: 0 0 0 auto;
color: #9babc7;
font-size: .8rem;
line-height: 1.5;
}
kbd {
padding: 2px 5px;
border: 1px solid #435170;
border-radius: 4px;
background: #17213a;
color: #dce9ff;
font: inherit;
}
@media (max-width: 580px) {
header { align-items: start; flex-direction: column; }
.stats { width: 100%; justify-content: space-between; }
.stat { text-align: left; }
.controls { width: 100%; margin-top: 4px; }
.overlay-box { padding: 14px 20px; }
.overlay h2 { margin: 4px 0; }
.overlay .hint { margin-top: 6px; }
}
</style>
</head>
<body>
<main>
<header>
<div>
<h1>NEON <span>BREAKOUT</span></h1>
<p class="subtitle">Five levels. One more bounce.</p>
</div>
<div class="stats" aria-label="Game statistics">
<div class="stat"><small>SCORE</small><strong id="score">0</strong></div>
<div class="stat"><small>LEVEL</small><strong id="level">1 / 5</strong></div>
<div class="stat"><small>LIVES</small><strong id="lives">3</strong></div>
</div>
</header>
<div class="game">
<canvas id="game" width="900" height="600"
aria-label="Breakout game. Move the paddle with the mouse or arrow keys. Press space to launch."></canvas>
<div class="overlay" id="overlay">
<div class="overlay-box">
<div class="eyebrow" id="overlayEyebrow">Ready to play?</div>
<h2 id="overlayTitle">Break the glow.</h2>
<p id="overlayText">Clear the bricks, keep the ball alive, and make it through all five levels.</p>
<p class="hint" id="overlayHint">Click the board or press Space to start</p>
</div>
</div>
</div>
<div class="toolbar">
<button id="playButton" type="button">Start game</button>
<button id="restartButton" type="button">Restart</button>
<button id="soundButton" type="button" aria-pressed="false">Sound: off</button>
<p class="controls">
Move: mouse / touch or <kbd>←</kbd> <kbd>→</kbd> ·
Launch: click or <kbd>Space</kbd> · Pause: <kbd>Esc</kbd>
</p>
</div>
</main>
<script>
(() => {
"use strict";
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const scoreEl = document.getElementById("score");
const levelEl = document.getElementById("level");
const livesEl = document.getElementById("lives");
const overlay = document.getElementById("overlay");
const eyebrow = document.getElementById("overlayEyebrow");
const title = document.getElementById("overlayTitle");
const text = document.getElementById("overlayText");
const hint = document.getElementById("overlayHint");
const playButton = document.getElementById("playButton");
const restartButton = document.getElementById("restartButton");
const soundButton = document.getElementById("soundButton");
const W = canvas.width, H = canvas.height, TOTAL_LEVELS = 5;
const keys = { left: false, right: false };
const paddle = { x: W / 2 - 58, y: 548, w: 116, h: 14, speed: 660 };
const ball = { x: W / 2, y: 0, vx: 0, vy: 0, r: 8 };
const palette = ["#ff6b9d", "#ffaf6b", "#ffe078", "#72f1c8", "#66d9ff", "#b497ff"];
const stars = Array.from({ length: 55 }, (_, i) => ({
x: (i * 137.5 + 23) % W,
y: (i * 83.7 + 31) % H,
r: i % 7 === 0 ? 1.5 : 0.8
}));
let state, score, level, lives, bricks, particles, trail, serving;
let shake = 0, flash = 0, soundEnabled = false, audioContext = null;
let lastTime = 0, accumulator = 0;
function clamp(n, min, max) { return Math.max(min, Math.min(max, n)); }
function beep(frequency, duration = 0.06, type = "sine", volume = 0.035) {
if (!soundEnabled) return;
try {
audioContext ||= new (window.AudioContext || window.webkitAudioContext)();
if (audioContext.state === "suspended") audioContext.resume();
const oscillator = audioContext.createOscillator();
const gain = audioContext.createGain();
oscillator.type = type;
oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
gain.gain.setValueAtTime(volume, audioContext.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration);
oscillator.connect(gain);
gain.connect(audioContext.destination);
oscillator.start();
oscillator.stop(audioContext.currentTime + duration);
} catch (_) {
// The game remains playable if audio is unavailable.
}
}
function updateHUD() {
scoreEl.textContent = score.toLocaleString();
levelEl.textContent = `${level} / ${TOTAL_LEVELS}`;
livesEl.textContent = lives;
}
function setOverlay(kind) {
overlay.hidden = kind === "playing";
if (kind === "ready") {
eyebrow.textContent = "Ready to play?";
title.textContent = "Break the glow.";
text.textContent = "Clear the bricks, keep the ball alive, and make it through all five levels.";
hint.textContent = "Click the board or press Space to start";
} else if (kind === "paused") {
eyebrow.textContent = "Take a breath";
title.textContent = "Paused";
text.textContent = "Your game is right where you left it.";
hint.textContent = "Press Escape or click Resume";
} else if (kind === "over") {
eyebrow.textContent = "Game over";
title.textContent = "Nice run.";
text.textContent = `You scored ${score.toLocaleString()} points.`;
hint.textContent = "Click the board or press Space to try again";
} else if (kind === "won") {
eyebrow.textContent = "All levels cleared";
title.textContent = "You did it!";
text.textContent = `Final score: ${score.toLocaleString()} points.`;
hint.textContent = "Click the board or press Space to play again";
}
playButton.textContent = kind === "playing" ? "Pause" :
kind === "paused" ? "Resume" :
kind === "ready" ? "Start game" : "Play again";
}
function buildLevel(n) {
const result = [];
const cols = 10, rows = 6, bw = 78, bh = 21, gap = 8;
const startX = (W - (cols * bw + (cols - 1) * gap)) / 2;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
let include = true;
if (n === 2) include = !(row >= 3 && (col === 0 || col === 9));
if (n === 3) include = (row + col) % 5 !== 0;
if (n === 4) include = !(row >= 2 && (col === 2 || col === 7) && row % 2 === 0);
if (n === 5) include = !((row === 3 || row === 4) && (col === 4 || col === 5));
if (!include) continue;
const hp = n >= 2 && row < Math.min(4, n - 1) && (col + row + n) % 3 !== 0 ? 2 : 1;
result.push({
x: startX + col * (bw + gap),
y: 85 + row * (bh + gap),
w: bw, h: bh, hp, maxHp: hp,
color: palette[(row + n - 1) % palette.length]
});
}
}
return result;
}
function attachBall() {
ball.x = paddle.x + paddle.w / 2;
ball.y = paddle.y - ball.r - 2;
ball.vx = ball.vy = 0;
serving = true;
trail = [];
}
function reset() {
state = "ready";
score = 0;
level = 1;
lives = 3;
paddle.x = W / 2 - paddle.w / 2;
bricks = buildLevel(level);
particles = [];
shake = flash = 0;
keys.left = keys.right = false;
attachBall();
updateHUD();
setOverlay(state);
}
function start() {
state = "playing";
setOverlay(state);
}
function launch() {
if (!serving || state !== "playing") return;
const speed = 390 + (level - 1) * 28;
const angle = (Math.random() * 0.5 - 0.25);
ball.vx = Math.sin(angle) * speed;
ball.vy = -Math.cos(angle) * speed;
serving = false;
beep(440, 0.08, "triangle");
}
function pauseToggle() {
if (state === "playing") {
state = "paused";
keys.left = keys.right = false;
setOverlay(state);
} else if (state === "paused") {
state = "playing";
setOverlay(state);
}
}
function action() {
if (state === "ready") {
start();
launch();
} else if (state === "over" || state === "won") {
reset();
start();
launch();
} else if (state === "playing") {
launch();
}
}
function burst(x, y, color, count = 10) {
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 40 + Math.random() * 200;
const life = 0.3 + Math.random() * 0.45;
particles.push({
x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed,
life, maxLife: life, size: 2 + Math.random() * 4, color
});
}
}
function nextLevel() {
score += 500 * level;
if (level === TOTAL_LEVELS) {
state = "won";
burst(W / 2, H / 2, "#67e8f9", 35);
beep(880, 0.3, "triangle");
updateHUD();
setOverlay(state);
return;
}
level++;
bricks = buildLevel(level);
paddle.x = W / 2 - paddle.w / 2;
attachBall();
flash = 0.5;
beep(660, 0.18, "triangle");
updateHUD();
}
function movePointer(event) {
const rect = canvas.getBoundingClientRect();
const x = (event.clientX - rect.left) * W / rect.width;
paddle.x = clamp(x - paddle.w / 2, 8, W - paddle.w - 8);
if (serving) {
ball.x = paddle.x + paddle.w / 2;
ball.y = paddle.y - ball.r - 2;
}
}
canvas.addEventListener("pointermove", movePointer);
canvas.addEventListener("pointerdown", event => {
event.preventDefault();
movePointer(event);
if (canvas.setPointerCapture) canvas.setPointerCapture(event.pointerId);
action();
});
playButton.addEventListener("click", () => {
if (state === "playing" || state === "paused") pauseToggle();
else {
if (state === "over" || state === "won") reset();
start();
}
});
restartButton.addEventListener("click", () => {
reset();
start();
});
soundButton.addEventListener("click", () => {
soundEnabled = !soundEnabled;
soundButton.textContent = `Sound: ${soundEnabled ? "on" : "off"}`;
soundButton.setAttribute("aria-pressed", String(soundEnabled));
if (soundEnabled) beep(660, 0.1, "triangle");
});
window.addEventListener("keydown", event => {
const key = event.key.toLowerCase();
if (["arrowleft", "arrowright", "a", "d"].includes(key)) {
if (key === "arrowleft" || key === "arrowright") event.preventDefault();
if (key === "arrowleft" || key === "a") keys.left = true;
if (key === "arrowright" || key === "d") keys.right = true;
} else if (key === " " && event.target.tagName !== "BUTTON") {
event.preventDefault();
if (!event.repeat) action();
} else if (key === "escape" && !event.repeat) {
pauseToggle();
}
});
window.addEventListener("keyup", event => {
const key = event.key.toLowerCase();
if (key === "arrowleft" || key === "a") keys.left = false;
if (key === "arrowright" || key === "d") keys.right = false;
});
window.addEventListener("blur", () => {
keys.left = keys.right = false;
if (state === "playing") pauseToggle();
});
function update(dt) {
if (state !== "playing") return;
const direction = (keys.right ? 1 : 0) - (keys.left ? 1 : 0);
paddle.x = clamp(paddle.x + direction * paddle.speed * dt, 8, W - paddle.w - 8);
for (const p of particles) {
p.x += p.vx * dt;
p.y += p.vy * dt;
p.vy += 160 * dt;
p.life -= dt;
}
particles = particles.filter(p => p.life > 0);
shake = Math.max(0, shake - dt * 18);
flash = Math.max(0, flash - dt * 1.6);
if (serving) {
ball.x = paddle.x + paddle.w / 2;
ball.y = paddle.y - ball.r - 2;
return;
}
const oldX = ball.x, oldY = ball.y;
ball.x += ball.vx * dt;
ball.y += ball.vy * dt;
if (ball.x - ball.r < 8) {
ball.x = 8 + ball.r;
ball.vx = Math.abs(ball.vx);
beep(250);
} else if (ball.x + ball.r > W - 8) {
ball.x = W - 8 - ball.r;
ball.vx = -Math.abs(ball.vx);
beep(250);
}
if (ball.y - ball.r < 8) {
ball.y = 8 + ball.r;
ball.vy = Math.abs(ball.vy);
beep(280);
}
if (ball.vy > 0 &&
oldY + ball.r <= paddle.y + 4 &&
ball.y + ball.r >= paddle.y &&
ball.x + ball.r >= paddle.x &&
ball.x - ball.r <= paddle.x + paddle.w) {
ball.y = paddle.y - ball.r;
const offset = clamp((ball.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2), -1, 1);
const speed = Math.min(650, Math.hypot(ball.vx, ball.vy) + 3);
const angle = offset * 1.12;
ball.vx = Math.sin(angle) * speed;
ball.vy = -Math.cos(angle) * speed;
burst(ball.x, paddle.y, "#67e8f9", 6);
beep(360 + Math.abs(offset) * 180, 0.06, "triangle");
}
for (let i = bricks.length - 1; i >= 0; i--) {
const b = bricks[i];
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) continue;
if (oldY + ball.r <= b.y) {
ball.y = b.y - ball.r;
ball.vy = -Math.abs(ball.vy);
} else if (oldY - ball.r >= b.y + b.h) {
ball.y = b.y + b.h + ball.r;
ball.vy = Math.abs(ball.vy);
} else if (oldX + ball.r <= b.x) {
ball.x = b.x - ball.r;
ball.vx = -Math.abs(ball.vx);
} else if (oldX - ball.r >= b.x + b.w) {
ball.x = b.x + b.w + ball.r;
ball.vx = Math.abs(ball.vx);
} else {
ball.vy *= -1;
}
b.hp--;
score += 50;
burst(ball.x, ball.y, b.color, b.hp === 0 ? 12 : 5);
shake = b.hp === 0 ? 3 : 1.5;
beep(b.hp === 0 ? 520 + (i % 5) * 60 : 220, 0.07, "triangle");
if (b.hp <= 0) {
score += 50;
bricks.splice(i, 1);
}
updateHUD();
if (bricks.length === 0) nextLevel();
break;
}
if (state !== "playing" || serving) return;
if (ball.y - ball.r > H) {
lives--;
shake = 8;
flash = 0.35;
beep(120, 0.25, "sawtooth");
updateHUD();
if (lives <= 0) {
state = "over";
setOverlay(state);
} else {
attachBall();
}
}
if (!serving && state === "playing") {
trail.push({ x: ball.x, y: ball.y });
if (trail.length > 12) trail.shift();
}
}
function roundedRect(x, y, w, h, r) {
ctx.beginPath();
ctx.roundRect(x, y, w, h, r);
}
function render(time) {
ctx.clearRect(0, 0, W, H);
ctx.save();
if (shake > 0 && state === "playing") {
ctx.translate((Math.random() - 0.5) * shake, (Math.random() - 0.5) * shake);
}
const bg = ctx.createLinearGradient(0, 0, 0, H);
bg.addColorStop(0, "#111b38");
bg.addColorStop(1, "#080d1e");
ctx.fillStyle = bg;
ctx.fillRect(-10, -10, W + 20, H + 20);
ctx.strokeStyle = "#ffffff08";
ctx.lineWidth = 1;
for (let x = 0; x < W; x += 45) {
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke();
}
for (let y = 0; y < H; y += 45) {
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke();
}
for (const s of stars) {
ctx.fillStyle = `rgba(190, 220, 255, ${0.15 + 0.12 * Math.sin(time / 900 + s.x)})`;
ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); ctx.fill();
}
ctx.fillStyle = "#7f9bc9";
ctx.globalAlpha = 0.7;
ctx.font = "bold 12px system-ui, sans-serif";
ctx.letterSpacing = "2px";
ctx.fillText(`LEVEL ${level} / ${TOTAL_LEVELS}`, 26, 40);
ctx.textAlign = "right";
ctx.fillText(`${bricks.length} BRICKS LEFT`, W - 26, 40);
ctx.textAlign = "left";
ctx.globalAlpha = 1;
for (const b of bricks) {
ctx.save();
ctx.shadowColor = b.color;
ctx.shadowBlur = 12;
ctx.fillStyle = b.color;
roundedRect(b.x, b.y, b.w, b.h, 5);
ctx.fill();
ctx.shadowBlur = 0;
ctx.fillStyle = "#ffffff55";
roundedRect(b.x + 3, b.y + 3, b.w - 6, 4, 2);
ctx.fill();
if (b.hp > 1) {
ctx.fillStyle = "#14203b99";
roundedRect(b.x + b.w / 2 - 10, b.y + 9, 20, 6, 3);
ctx.fill();
}
ctx.restore();
}
for (const p of particles) {
ctx.globalAlpha = clamp(p.life / p.maxLife, 0, 1);
ctx.fillStyle = p.color;
ctx.fillRect(p.x, p.y, p.size, p.size);
}
ctx.globalAlpha = 1;
for (let i = 0; i < trail.length; i++) {
ctx.globalAlpha = (i / trail.length) * 0.3;
ctx.fillStyle = "#8cefff";
ctx.beginPath();
ctx.arc(trail[i].x, trail[i].y, ball.r * (i / trail.length), 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1;
ctx.save();
ctx.shadowColor = "#55e7ff";
ctx.shadowBlur = 22;
const pg = ctx.createLinearGradient(paddle.x, 0, paddle.x + paddle.w, 0);
pg.addColorStop(0, "#53a8ff");
pg.addColorStop(0.5, "#b3fbff");
pg.addColorStop(1, "#53a8ff");
ctx.fillStyle = pg;
roundedRect(paddle.x, paddle.y, paddle.w, paddle.h, 7);
ctx.fill();
ctx.shadowColor = "#fff";
ctx.shadowBlur = 18;
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
if (serving && state === "playing") {
ctx.fillStyle = "#b8cbea";
ctx.textAlign = "center";
ctx.font = "bold 13px system-ui, sans-serif";
ctx.fillText("CLICK OR PRESS SPACE TO LAUNCH", W / 2, H - 20);
ctx.textAlign = "left";
}
if (flash > 0) {
ctx.fillStyle = `rgba(120, 220, 255, ${flash * 0.3})`;
ctx.fillRect(0, 0, W, H);
}
ctx.restore();
}
function frame(time) {
if (!lastTime) lastTime = time;
const elapsed = Math.min((time - lastTime) / 1000, 0.05);
lastTime = time;
accumulator += elapsed;
const step = 1 / 120;
while (accumulator >= step) {
update(step);
accumulator -= step;
}
render(time);
requestAnimationFrame(frame);
}
reset();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>

