Mistral Small 4
by MistralReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 24.7 s
- Cost
- $0.0036
- Output tokens
- 5,248
- Spent thinking
- 0 tokens
- Speed
- 216.6 tok/s
- Console errors
- 0
- First token after
- 479 ms
- First visible text after
- 479 ms
- Prompt tokens
- 532
- Code size
- 23.5 KB
- Finish reason
- stop
- Served by
- Mistral
- Model id
mistralai/mistral-small-2603- Reasoning sent
{"effort":"none"}- Max output
- 209,715 tokens
- Generated
- Nov 12, 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 · 689 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Breakout</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #111;
color: #fff;
font-family: Arial, sans-serif;
overflow: hidden;
user-select: none;
}
#game-container {
position: relative;
width: 100%;
max-width: 800px;
height: 600px;
background: #000;
}
#game-canvas {
background: #000;
display: block;
}
#start-screen, #game-over, #victory {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.8);
z-index: 10;
display: none;
}
#start-screen h1, #game-over h1, #victory h1 {
font-size: 3em;
margin: 0 0 20px;
text-shadow: 0 0 10px #fff;
}
#start-screen p, #game-over p, #victory p {
font-size: 1.2em;
margin: 0 0 30px;
text-align: center;
}
#restart-btn {
padding: 12px 24px;
font-size: 1.2em;
background: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s;
}
#restart-btn:hover {
background: #45a049;
}
#score-display {
position: absolute;
top: 20px;
left: 20px;
font-size: 1.2em;
z-index: 5;
}
#lives-display {
position: absolute;
top: 20px;
right: 20px;
font-size: 1.2em;
z-index: 5;
}
#powerup-indicator {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 1em;
z-index: 5;
background: rgba(0, 0, 0, 0.7);
padding: 5px 10px;
border-radius: 5px;
display: none;
}
</style>
</head>
<body>
<div id="game-container">
<div id="score-display">Score: 0</div>
<div id="lives-display">Lives: 3</div>
<div id="powerup-indicator"></div>
<canvas id="game-canvas" width="800" height="600"></canvas>
<div id="start-screen">
<h1>Breakout</h1>
<p>Click or press any key to start</p>
</div>
<div id="game-over">
<h1>Game Over</h1>
<p>Your score: <span id="final-score">0</span></p>
<button id="restart-btn">Play Again</button>
</div>
<div id="victory">
<h1>Victory!</h1>
<p>Your score: <span id="victory-score">0</span></p>
<button id="restart-btn">Play Again</button>
</div>
</div>
<script>
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('start-screen');
const gameOverScreen = document.getElementById('game-over');
const victoryScreen = document.getElementById('victory');
const finalScoreDisplay = document.getElementById('final-score');
const victoryScoreDisplay = document.getElementById('victory-score');
const scoreDisplay = document.getElementById('score-display');
const livesDisplay = document.getElementById('lives-display');
const powerupIndicator = document.getElementById('powerup-indicator');
const restartBtn = document.getElementById('restart-btn');
// Game state
let gameActive = false;
let gameOver = false;
let victory = false;
let score = 0;
let lives = 3;
let powerupActive = false;
let powerupTimer = 0;
let powerupType = '';
// Paddle
const paddleWidth = 100;
const paddleHeight = 15;
let paddleX = (canvas.width - paddleWidth) / 2;
let paddleY = canvas.height - paddleHeight - 20;
let paddleSpeed = 8;
let mouseX = 0;
let keyLeftPressed = false;
let keyRightPressed = false;
// Ball
let balls = [{ x: canvas.width / 2, y: canvas.height / 2, radius: 8, dx: 4, dy: -4 }];
const ballRadius = 8;
// Bricks
const brickRowCount = 6;
const brickColumnCount = 10;
const brickWidth = 75;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 60;
const brickOffsetLeft = 30;
const bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
const brickType = Math.floor(Math.random() * 10);
bricks[c][r] = {
x: 0,
y: 0,
status: brickType < 7 ? 1 : 2, // 1 = normal, 2 = tough
broken: false,
powerup: Math.random() < 0.2 ? (Math.random() < 0.5 ? 'paddle' : 'multiball') : null
};
}
}
// Power-ups
let powerups = [];
// Particles
let particles = [];
// Event listeners
canvas.addEventListener('mousemove', (e) => {
mouseX = e.clientX - canvas.getBoundingClientRect().left;
if (gameActive) {
paddleX = mouseX - paddleWidth / 2;
paddleX = Math.max(0, Math.min(canvas.width - paddleWidth, paddleX));
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') {
keyLeftPressed = true;
} else if (e.key === 'ArrowRight') {
keyRightPressed = true;
} else if (e.key === ' ' && !gameActive && !gameOver && !victory) {
startGame();
}
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowLeft') {
keyLeftPressed = false;
} else if (e.key === 'ArrowRight') {
keyRightPressed = false;
}
});
restartBtn.addEventListener('click', startGame);
// Start screen
function showStartScreen() {
startScreen.style.display = 'flex';
gameOverScreen.style.display = 'none';
victoryScreen.style.display = 'none';
gameActive = false;
gameOver = false;
victory = false;
}
// Game over screen
function showGameOverScreen() {
gameOver = true;
gameActive = false;
finalScoreDisplay.textContent = score;
gameOverScreen.style.display = 'flex';
startScreen.style.display = 'none';
victoryScreen.style.display = 'none';
}
// Victory screen
function showVictoryScreen() {
victory = true;
gameActive = false;
victoryScoreDisplay.textContent = score;
victoryScreen.style.display = 'flex';
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
}
// Start game
function startGame() {
// Reset game state
balls = [{ x: canvas.width / 2, y: canvas.height / 2, radius: 8, dx: 4, dy: -4 }];
paddleX = (canvas.width - paddleWidth) / 2;
score = 0;
lives = 3;
powerupActive = false;
powerupTimer = 0;
powerupType = '';
powerups = [];
particles = [];
// Reset bricks
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const brickType = Math.floor(Math.random() * 10);
bricks[c][r] = {
x: 0,
y: 0,
status: brickType < 7 ? 1 : 2,
broken: false,
powerup: Math.random() < 0.2 ? (Math.random() < 0.5 ? 'paddle' : 'multiball') : null
};
}
}
// Update displays
scoreDisplay.textContent = `Score: ${score}`;
livesDisplay.textContent = `Lives: ${lives}`;
powerupIndicator.style.display = 'none';
// Hide screens
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
victoryScreen.style.display = 'none';
gameActive = true;
gameOver = false;
victory = false;
}
// Check collision with bricks
function collision(ball) {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const brick = bricks[c][r];
if (brick.status > 0 && !brick.broken) {
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
if (
ball.x + ball.radius > brickX &&
ball.x - ball.radius < brickX + brickWidth &&
ball.y + ball.radius > brickY &&
ball.y - ball.radius < brickY + brickHeight
) {
// Calculate bounce angle based on where ball hits brick
const hitX = (ball.x - (brickX + brickWidth / 2)) / (brickWidth / 2);
ball.dx = hitX * 5;
// Reverse direction
ball.dy = -Math.abs(ball.dy);
// Reduce brick status
brick.status--;
// Create particles
for (let i = 0; i < 15; i++) {
particles.push({
x: ball.x,
y: ball.y,
dx: (Math.random() - 0.5) * 4,
dy: (Math.random() - 0.5) * 4,
size: Math.random() * 3 + 1,
color: brick.status === 0 ? '#ff5555' : '#ffaa55',
life: 30
});
}
// Check if brick is destroyed
if (brick.status <= 0) {
brick.broken = true;
// Add to score
score += brick.status === 0 ? 10 : 20;
scoreDisplay.textContent = `Score: ${score}`;
// Drop powerup if available
if (brick.powerup) {
powerups.push({
x: brickX + brickWidth / 2,
y: brickY + brickHeight / 2,
type: brick.powerup,
width: 20,
height: 20,
speed: 2
});
}
// Check for victory
if (checkVictory()) {
showVictoryScreen();
}
}
return true;
}
}
}
}
return false;
}
// Check collision with paddle
function collisionWithPaddle(ball) {
if (
ball.x + ball.radius > paddleX &&
ball.x - ball.radius < paddleX + paddleWidth &&
ball.y + ball.radius > paddleY &&
ball.y - ball.radius < paddleY + paddleHeight
) {
// Calculate bounce angle based on where ball hits paddle
const hitX = (ball.x - (paddleX + paddleWidth / 2)) / (paddleWidth / 2);
ball.dx = hitX * 5;
ball.dy = -Math.abs(ball.dy);
// Create particles
for (let i = 0; i < 10; i++) {
particles.push({
x: ball.x,
y: ball.y,
dx: (Math.random() - 0.5) * 3,
dy: (Math.random() - 0.5) * 3,
size: Math.random() * 2 + 1,
color: '#aaaaff',
life: 20
});
}
return true;
}
return false;
}
// Check collision with walls
function checkWallCollision(ball) {
// Left wall
if (ball.x - ball.radius < 0) {
ball.x = ball.radius;
ball.dx = Math.abs(ball.dx);
return true;
}
// Right wall
if (ball.x + ball.radius > canvas.width) {
ball.x = canvas.width - ball.radius;
ball.dx = -Math.abs(ball.dx);
return true;
}
// Top wall
if (ball.y - ball.radius < 0) {
ball.y = ball.radius;
ball.dy = Math.abs(ball.dy);
return true;
}
// Bottom wall (lose a life)
if (ball.y + ball.radius > canvas.height) {
return true;
}
return false;
}
// Check if all bricks are broken
function checkVictory() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status > 0 && !bricks[c][r].broken) {
return false;
}
}
}
return true;
}
// Update powerup timer
function updatePowerupTimer(deltaTime) {
if (powerupActive) {
powerupTimer -= deltaTime;
if (powerupTimer <= 0) {
powerupActive = false;
powerupType = '';
powerupIndicator.style.display = 'none';
// Reset paddle width
if (powerupType === 'paddle') {
paddleWidth = 100;
}
}
}
}
// Update game state
function update(deltaTime) {
if (!gameActive) return;
// Update powerup timer
updatePowerupTimer(deltaTime);
// Move paddle with keyboard
if (keyLeftPressed && paddleX > 0) {
paddleX -= paddleSpeed;
}
if (keyRightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += paddleSpeed;
}
// Move balls
for (let i = balls.length - 1; i >= 0; i--) {
const ball = balls[i];
ball.x += ball.dx;
ball.y += ball.dy;
// Check wall collisions
if (checkWallCollision(ball)) {
if (ball.y + ball.radius > canvas.height) {
// Ball fell off bottom
balls.splice(i, 1);
if (balls.length === 0) {
lives--;
livesDisplay.textContent = `Lives: ${lives}`;
if (lives <= 0) {
showGameOverScreen();
} else {
// Reset ball
balls.push({
x: canvas.width / 2,
y: canvas.height / 2,
radius: 8,
dx: 4,
dy: -4
});
}
}
}
}
// Check brick collisions
if (collision(ball)) {
continue;
}
// Check paddle collision
if (collisionWithPaddle(ball)) {
continue;
}
}
// Move powerups
for (let i = powerups.length - 1; i >= 0; i--) {
const powerup = powerups[i];
powerup.y += powerup.speed;
// Check if powerup is caught
if (
powerup.y + powerup.height > paddleY &&
powerup.y < paddleY + paddleHeight &&
powerup.x + powerup.width > paddleX &&
powerup.x < paddleX + paddleWidth
) {
// Activate powerup
powerupActive = true;
powerupType = powerup.type;
powerupTimer = 10000; // 10 seconds
// Update indicator
powerupIndicator.textContent = powerupType === 'paddle' ? 'Paddle Width: 10s' : 'Multi-ball: 10s';
powerupIndicator.style.display = 'block';
// Apply powerup effect
if (powerupType === 'paddle') {
paddleWidth = 150;
} else if (powerupType === 'multiball') {
// Add two more balls
balls.push(
{ x: canvas.width / 2, y: canvas.height / 2, radius: 8, dx: 3, dy: -3 },
{ x: canvas.width / 2, y: canvas.height / 2, radius: 8, dx: -3, dy: -3 }
);
}
powerups.splice(i, 1);
} else if (powerup.y > canvas.height) {
// Powerup fell off bottom
powerups.splice(i, 1);
}
}
// Update particles
for (let i = particles.length - 1; i >= 0; i--) {
const particle = particles[i];
particle.x += particle.dx;
particle.y += particle.dy;
particle.life--;
if (particle.life <= 0) {
particles.splice(i, 1);
}
}
}
// Draw game
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw bricks
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const brick = bricks[c][r];
if (brick.status > 0 && !brick.broken) {
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
// Store position for collision detection
brick.x = brickX;
brick.y = brickY;
// Draw brick
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = brick.status === 1 ? '#55ff55' : '#ff5555';
ctx.fill();
ctx.closePath();
// Draw crack if tough brick has been hit once
if (brick.status === 1) {
ctx.beginPath();
ctx.moveTo(brickX + 5, brickY + 5);
ctx.lineTo(brickX + brickWidth - 5, brickY + brickHeight - 5);
ctx.moveTo(brickX + brickWidth - 5, brickY + 5);
ctx.lineTo(brickX + 5, brickY + brickHeight - 5);
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.stroke();
}
// Draw powerup icon if available
if (brick.powerup) {
ctx.beginPath();
if (brick.powerup === 'paddle') {
ctx.rect(brickX + brickWidth / 2 - 5, brickY + brickHeight / 2 - 5, 10, 10);
} else {
ctx.arc(brickX + brickWidth / 2, brickY + brickHeight / 2, 5, 0, Math.PI * 2);
}
ctx.fillStyle = brick.powerup === 'paddle' ? '#5555ff' : '#ffff55';
ctx.fill();
ctx.closePath();
}
}
}
}
// Draw paddle
ctx.beginPath();
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = powerupActive && powerupType === 'paddle' ? '#5555ff' : '#aaaaff';
ctx.fill();
ctx.closePath();
// Draw balls
balls.forEach(ball => {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.closePath();
});
// Draw powerups
powerups.forEach(powerup => {
ctx.beginPath();
if (powerup.type === 'paddle') {
ctx.rect(powerup.x - powerup.width / 2, powerup.y - powerup.height / 2, powerup.width, powerup.height);
ctx.fillStyle = '#5555ff';
} else {
ctx.arc(powerup.x, powerup.y, powerup.width / 2, 0, Math.PI * 2);
ctx.fillStyle = '#ffff55';
}
ctx.fill();
ctx.closePath();
});
// Draw particles
particles.forEach(particle => {
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
ctx.fillStyle = particle.color;
ctx.fill();
ctx.closePath();
});
}
// Game loop
let lastTime = 0;
function gameLoop(timestamp) {
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
update(deltaTime / 1000);
draw();
if (!gameOver && !victory) {
requestAnimationFrame(gameLoop);
}
}
// Initialize
showStartScreen();
// Start game loop when clicking
canvas.addEventListener('click', () => {
if (!gameActive && !gameOver && !victory) {
startGame();
requestAnimationFrame(gameLoop);
}
});
</script>
</body>
</html>