Dots3-Note Preview (free)
by Dots StudioReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 51.2 s
- Cost
- $0
- Output tokens
- 5,339
- Spent thinking
- 0 tokens
- Speed
- 105.9 tok/s
- Console errors
- 0
- First token after
- 718 ms
- First visible text after
- 718 ms
- Prompt tokens
- 510
- Code size
- 16.5 KB
- Finish reason
- stop
- Served by
- AtlasCloud
- Model id
dots-studio/dots-3-note-preview:free- Reasoning sent
{"effort":"none"}- Max output
- 32,000 tokens
- Generated
- Nov 9, 2026
What it was asked
The prompt
Build a polished Breakout (brick breaker) game in a single HTML file using vanilla JavaScript and a <canvas>. Requirements: - A paddle controlled with the mouse and with the left/right arrow keys. - The bounce angle off the paddle depends on where the ball hits it (center = straight up, edges = sharp angle). - One level with at least 6 rows of bricks, mixing normal bricks and tougher bricks that need 2 hits and visibly crack after the first one. - Some bricks visibly carry a power-up icon; when broken they drop it, and catching it with the paddle gives either a wider paddle (10 seconds) or multi-ball (3 balls). - The ball must never pass through bricks or the paddle. - Particles when a brick breaks. - 3 lives, score displayed. Game over and victory screens with a restart option.
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 · 629 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Breakout: Power-Up Edition</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
height: 100%;
background: #0f0f1a;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #fff;
overflow: hidden;
user-select: none;
}
#wrap {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
canvas {
background: linear-gradient(160deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
box-shadow: 0 0 40px rgba(0, 150, 255, 0.3);
display: block;
max-width: 100%;
max-height: 100%;
}
.overlay {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(4px);
z-index: 10;
transition: opacity 0.4s ease;
}
.overlay.hidden { opacity: 0; pointer-events: none; }
.overlay h1 {
font-size: clamp(2rem, 6vw, 3.5rem);
margin-bottom: 1rem;
background: linear-gradient(90deg, #00c6ff, #0072ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 20px rgba(0, 150, 255, 0.4);
}
.overlay p { font-size: 1.2rem; margin-bottom: 2rem; opacity: 0.85; }
.btn {
padding: 12px 32px;
font-size: 1.1rem;
font-weight: 600;
color: #fff;
background: linear-gradient(90deg, #00c6ff, #0072ff);
border: none;
border-radius: 50px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 4px 20px rgba(0, 114, 255, 0.5);
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 6px 25px rgba(0, 114, 255, 0.7); }
.btn:active { transform: translateY(0); }
.hud {
position: absolute;
top: 20px;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
padding: 0 30px;
font-size: 1.1rem;
font-weight: 600;
text-shadow: 0 2px 4px rgba(0,0,0,0.7);
pointer-events: none;
z-index: 5;
}
.lives { color: #ff4d6d; }
.score { color: #00e676; }
.powerup-info {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
text-align: center;
font-size: 0.95rem;
opacity: 0.8;
pointer-events: none;
z-index: 5;
}
</style>
</head>
<body>
<div id="wrap">
<canvas id="game" width="900" height="600"></canvas>
<div class="hud">
<div class="lives">Lives: <span id="livesCount">3</span></div>
<div class="score">Score: <span id="scoreCount">0</span></div>
</div>
<div class="powerup-info" id="powerupInfo"></div>
<div class="overlay" id="startScreen">
<h1>Breakout: Power-Up Edition</h1>
<p>Move paddle with mouse or ← → keys<br>
Catch falling power-ups: <b>Wide Paddle</b> (🟢) or <b>Multi-Ball</b> (🔵)</p>
<button class="btn" id="startBtn">Start Game</button>
</div>
<div class="overlay hidden" id="gameOverScreen">
<h1>Game Over</h1>
<p>Final Score: <span id="finalScore">0</span></p>
<button class="btn" id="restartBtn">Play Again</button>
</div>
<div class="overlay hidden" id="victoryScreen">
<h1>Victory!</h1>
<p>You cleared all bricks!</p>
<button class="btn" id="victoryBtn">Play Again</button>
</div>
</div>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
// UI Elements
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const victoryScreen = document.getElementById('victoryScreen');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const victoryBtn = document.getElementById('victoryBtn');
const livesCount = document.getElementById('livesCount');
const scoreCount = document.getElementById('scoreCount');
const finalScore = document.getElementById('finalScore');
const powerupInfo = document.getElementById('powerupInfo');
// Game State
let state = {
paddle: { x: W/2 - 60, y: H - 40, w: 120, h: 15, speed: 10, baseW: 120, wideTimer: 0 },
balls: [],
bricks: [],
particles: [],
powerups: [],
score: 0,
lives: 3,
running: false,
keys: {},
mouse: { x: W/2 }
};
// Brick Config
const brickRows = 6;
const brickCols = 10;
const brickW = 70;
const brickH = 25;
const brickPad = 8;
const brickOffsetTop = 60;
const brickOffsetLeft = (W - (brickCols * (brickW + brickPad) - brickPad)) / 2;
// Power-up Types
const POWERUP_TYPES = {
WIDE: { color: '#00e676', icon: '🟢', name: 'Wide Paddle', duration: 10000 },
MULTIBALL: { color: '#2196f3', icon: '🔵', name: 'Multi-Ball', duration: 0 }
};
// Utility
function rand(min, max) { return Math.random() * (max - min) + min; }
function createBricks() {
state.bricks = [];
const colors = ['#ff5252', '#ff9800', '#ffeb3b', '#4caf50', '#2196f3', '#9c27b0'];
for (let r = 0; r < brickRows; r++) {
for (let c = 0; c < brickCols; c++) {
const isTough = Math.random() < 0.2; // 20% tough bricks
const hasPowerup = Math.random() < 0.15; // 15% chance
const powerupType = Math.random() < 0.5 ? 'WIDE' : 'MULTIBALL';
state.bricks.push({
x: brickOffsetLeft + c * (brickW + brickPad),
y: brickOffsetTop + r * (brickH + brickPad),
w: brickW,
h: brickH,
color: colors[r % colors.length],
hits: isTough ? 2 : 1,
maxHits: isTough ? 2 : 1,
cracked: false,
hasPowerup,
powerupType
});
}
}
}
function createBall(x, y) {
const angle = rand(-Math.PI/4, -Math.PI/4 * 0.5); // upward angle
const speed = 6;
state.balls.push({
x, y, r: 8,
dx: Math.cos(angle) * speed * (Math.random() < 0.5 ? -1 : 1),
dy: Math.sin(angle) * speed,
active: true
});
}
function resetBall() {
state.balls = [];
createBall(state.paddle.x + state.paddle.w/2, state.paddle.y - 15);
}
function spawnPowerup(x, y, type) {
state.powerups.push({
x, y, r: 12,
type,
dy: 3,
...POWERUP_TYPES[type]
});
}
function createParticles(x, y, color, count = 12) {
for (let i = 0; i < count; i++) {
state.particles.push({
x, y,
dx: rand(-4, 4),
dy: rand(-6, 2),
life: 1,
decay: rand(0.02, 0.05),
size: rand(2, 5),
color
});
}
}
function updatePaddle() {
// Mouse control
state.paddle.x = Math.max(0, Math.min(W - state.paddle.w, state.mouse.x - state.paddle.w/2));
// Keyboard control
if (state.keys['ArrowLeft'] || state.keys['a']) state.paddle.x -= state.paddle.speed;
if (state.keys['ArrowRight'] || state.keys['d']) state.paddle.x += state.paddle.speed;
state.paddle.x = Math.max(0, Math.min(W - state.paddle.w, state.paddle.x));
// Wide paddle timer
if (state.paddle.wideTimer > 0) {
state.paddle.wideTimer -= 16;
if (state.paddle.wideTimer <= 0) {
state.paddle.w = state.paddle.baseW;
state.paddle.wideTimer = 0;
powerupInfo.textContent = '';
}
}
}
function updateBalls() {
for (let i = state.balls.length - 1; i >= 0; i--) {
const ball = state.balls[i];
if (!ball.active) {
state.balls.splice(i, 1);
continue;
}
// Move ball
ball.x += ball.dx;
ball.y += ball.dy;
// Wall collisions
if (ball.x - ball.r < 0) { ball.x = ball.r; ball.dx = -ball.dx; }
if (ball.x + ball.r > W) { ball.x = W - ball.r; ball.dx = -ball.dx; }
if (ball.y - ball.r < 0) { ball.y = ball.r; ball.dy = -ball.dy; }
// Bottom boundary (lose life)
if (ball.y - ball.r > H) {
ball.active = false;
state.balls.splice(i, 1);
if (state.balls.length === 0) {
loseLife();
}
continue;
}
// Paddle collision
const p = state.paddle;
if (ball.y + ball.r > p.y && ball.y - ball.r < p.y + p.h &&
ball.x + ball.r > p.x && ball.x - ball.r < p.x + p.w) {
// Calculate hit position on paddle (-1 to 1)
const hitPos = (ball.x - (p.x + p.w/2)) / (p.w/2);
const angle = hitPos * (Math.PI/3); // Max 60 degrees
ball.y = p.y - ball.r;
ball.dx = Math.sin(angle) * 7;
ball.dy = -Math.cos(angle) * 7;
}
// Brick collisions
for (let j = state.bricks.length - 1; j >= 0; j--) {
const brick = state.bricks[j];
if (ball.x + ball.r > brick.x && ball.x - ball.r < brick.x + brick.w &&
ball.y + ball.r > brick.y && ball.y - ball.r < brick.y + brick.h) {
// Determine collision side
const overlapX = Math.min(ball.x + ball.r - brick.x, brick.x + brick.w - ball.x + ball.r);
const overlapY = Math.min(ball.y + ball.r - brick.y, brick.y + brick.h - ball.y + ball.r);
if (overlapX < overlapY) {
ball.dx = -ball.dx;
} else {
ball.dy = -ball.dy;
}
// Damage brick
brick.hits--;
if (brick.hits === 1 && brick.maxHits === 2) {
brick.cracked = true;
}
if (brick.hits <= 0) {
// Brick destroyed
createParticles(brick.x + brick.w/2, brick.y + brick.h/2, brick.color, 15);
state.score += brick.maxHits === 2 ? 20 : 10;
scoreCount.textContent = state.score;
// Spawn powerup
if (brick.hasPowerup) {
spawnPowerup(brick.x + brick.w/2, brick.y + brick.h/2, brick.powerupType);
}
state.bricks.splice(j, 1);
// Check victory
if (state.bricks.length === 0) {
victory();
}
}
break; // Only one collision per frame
}
}
}
}
function updatePowerups() {
for (let i = state.powerups.length - 1; i >= 0; i--) {
const p = state.powerups[i];
p.y += p.dy;
// Paddle catch
const paddle = state.paddle;
if (p.y + p.r > paddle.y && p.y - p.r < paddle.y + paddle.h &&
p.x + p.r > paddle.x && p.x - p.r < paddle.x + paddle.w) {
if (p.type === 'WIDE') {
state.paddle.w = state.paddle.baseW * 1.8;
state.paddle.wideTimer = POWERUP_TYPES.WIDE.duration;
powerupInfo.textContent = `Wide Paddle: ${(POWERUP_TYPES.WIDE.duration/1000).toFixed(0)}s`;
} else if (p.type === 'MULTIBALL') {
// Add 2 extra balls
const currentBalls = state.balls.length;
for (let b = 0; b < 2; b++) {
const ball = state.balls[b % currentBalls];
if (ball) {
const newBall = {
x: ball.x, y: ball.y, r: ball.r,
dx: rand(-5, 5), dy: -Math.sqrt(25 - (state.balls[0].dx||0)**2),
active: true
};
state.balls.push(newBall);
}
}
powerupInfo.textContent = 'Multi-Ball Activated!';
setTimeout(() => powerupInfo.textContent = '', 2000);
}
state.powerups.splice(i, 1);
continue;
}
// Remove if off screen
if (p.y - p.r > H) {
state.powerups.splice(i, 1);
}
}
}
function updateParticles() {
for (let i = state.particles.length - 1; i >= 0; i--) {
const p = state.particles[i];
p.x += p.dx;
p.y += p.dy;
p.dy += 0.2; // gravity
p.life -= p.decay;
if (p.life <= 0) {
state.particles.splice(i, 1);
}
}
}
function loseLife() {
state.lives--;
livesCount.textContent = state.lives;
if (state.lives <= 0) {
gameOver();
} else {
resetBall();
}
}
function drawPaddle() {
const p = state.paddle;
const gradient = ctx.createLinearGradient(p.x, p.y, p.x, p.y + p.h);
gradient.addColorStop(0, '#00c6ff');
gradient.addColorStop(1, '#0072ff');
ctx.fillStyle = gradient;
roundRect(ctx, p.x, p.y, p.w, p.h, 8);
ctx.fill();
// Highlight
ctx.fillStyle = 'rgba(255,255,255,0.3)';
roundRect(ctx, p.x + 2, p.y + 1, p.w - 4, 4, 3);
ctx.fill();
}
function drawBalls() {
for (const ball of state.balls) {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
ctx.fillStyle = '#fff';
ctx.fill();
// Shine
ctx.beginPath();
ctx.arc(ball.x - ball.r/3, ball.y - ball.r/3, ball.r/3, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,255,255,0.7)';
ctx.fill();
}
}
function drawBricks() {
for (const brick of state.bricks) {
// Base color
ctx.fillStyle = brick.color;
roundRect(ctx, brick.x, brick.y, brick.w, brick.h, 4);
ctx.fill();
// Crack effect for tough bricks
if (brick.cracked) {
ctx.strokeStyle = 'rgba(0,0,0,0.5)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(brick.x + 5, brick.y + 2);
ctx.lineTo(brick.x + brick.w/2, brick.y + brick.h/2);
ctx.lineTo(brick.x + brick.w - 5, brick.y + 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(brick.x + brick.w/2, brick.y + brick.h/2);
ctx.lineTo(brick.x + brick.w/2, brick.y + brick.h - 2);
ctx.stroke();
}
// Highlight
ctx.fillStyle = 'rgba(255,255,255,0.2)';
roundRect(ctx, brick.x + 1, brick.y + 1, brick.w - 2, 4, 2);
ctx.fill();
// Power-up icon
if (brick.hasPowerup) {
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(brick.powerupType === 'WIDE' ? '🟢' : '🔵',
brick.x + brick.w/2, brick.y + brick.h/2);
}
}
}
function drawPowerups() {
for (const p of state.powerups) {
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.fill();
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(p.icon, p.x, p.y);
// Glow
ctx.beginPath();
ctx.arc(p.x, p.y, p.r + 4, 0, Math.PI * 2);
ctx.strokeStyle = p.color;
ctx.lineWidth = 2;
ctx.globalAlpha = 0.6;
ctx.stroke();
ctx.globalAlpha = 1;
}
}
function drawParticles() {
for (const p of state.particles) {
ctx.globalAlpha = p.life;
ctx.fillStyle = p.color;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1;
}
function roundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function render() {
ctx.clearRect(0, 0, W, H);
drawBricks();
drawPowerups();
drawPaddle();
drawBalls();
drawParticles();
}
function gameLoop() {
if (!state.running) return;
updatePaddle();
updateBalls();
updatePowerups();
updateParticles();
render();
requestAnimationFrame(gameLoop);
}
function startGame() {
state = {
paddle: { x: W/2 - 60, y: H - 40, w: 120, h: 15, speed: 10, baseW: 120, wideTimer: 0 },
balls: [],
bricks: [],
particles: [],
powerups: [],
score: 0,
lives: 3,
running: true,
keys: {},
mouse: { x: W/2 }
};
scoreCount.textContent = '0';
livesCount.textContent = '3';
powerupInfo.textContent = '';
createBricks();
resetBall();
startScreen.classList.add('hidden');
gameOverScreen.classList.add('hidden');
victoryScreen.classList.add('hidden');
gameLoop();
}
function gameOver() {
state.running = false;
finalScore.textContent = state.score;
gameOverScreen.classList.remove('hidden');
}
function victory() {
state.running = false;
victoryScreen.classList.remove('hidden');
}
// Event Listeners
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
victoryBtn.addEventListener('click', startGame);
// Keyboard
window.addEventListener('keydown', (e) => {
state.keys[e.key] = true;
if (state.running && (e.key === ' ' || e.key === 'Enter')) {
// Pause/resume could go here
}
});
window.addEventListener('keyup', (e) => {
state.keys[e.key] = false;
});
// Mouse
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
state.mouse.x = (e.clientX - rect.left) * (W / rect.width);
});
// Touch support
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
state.mouse.x = (touch.clientX - rect.left) * (W / rect.width);
}, { passive: false });
// Prevent arrow key scrolling
window.addEventListener('keydown', (e) => {
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', ' '].includes(e.key)) {
e.preventDefault();
}
}, { passive: false });
</script>
</body>
</html>