Models 44

Qwen3 Coder Next

by Qwenreasoning defaultClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
1 min 33 s
Cost
$0.0044
Output tokens
5,426
Spent thinking
0 tokens
Speed
58.3 tok/s
Console errors
0
First token after
498 ms
First visible text after
498 ms
Prompt tokens
513
Code size
20.5 KB
Finish reason
stop
Served by
Parasail
Model id
qwen/qwen3-coder-next
Reasoning sent
model default
Max output
235,929 tokens
Generated
Nov 13, 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 · 743 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Neon Breakout</title>
    <style>
        :root {
            --bg-color: #0f0f1a;
            --text-color: #ffffff;
            --accent-color: #00f3ff;
            --danger-color: #ff0055;
            --success-color: #00ff9d;
        }

        body {
            margin: 0;
            padding: 0;
            background-color: var(--bg-color);
            color: var(--text-color);
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            overflow: hidden;
            user-select: none;
        }

        h1 {
            margin: 10px 0;
            font-weight: 300;
            letter-spacing: 3px;
            text-transform: uppercase;
            text-shadow: 0 0 10px var(--accent-color);
            font-size: 2.5rem;
        }

        #game-container {
            position: relative;
            box-shadow: 0 0 30px rgba(0, 243, 255, 0.2);
            border: 2px solid #333;
            border-radius: 4px;
        }

        canvas {
            display: block;
            background: radial-gradient(circle at center, #1a1a2e 0%, #0f0f1a 100%);
            cursor: none; /* Hide default cursor during gameplay */
        }

        #ui-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            padding: 20px;
            box-sizing: border-box;
        }

        .hud {
            display: flex;
            justify-content: space-between;
            font-size: 1.2rem;
            font-weight: bold;
            text-shadow: 1px 1px 2px black;
        }

        .hud span {
            color: var(--accent-color);
        }

        #overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.85);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            pointer-events: auto;
            z-index: 10;
            transition: opacity 0.3s;
        }

        #overlay.hidden {
            opacity: 0;
            pointer-events: none;
        }

        #overlay h2 {
            font-size: 4rem;
            margin-bottom: 20px;
            text-transform: uppercase;
            letter-spacing: 5px;
        }

        .win-text { color: var(--success-color); text-shadow: 0 0 20px var(--success-color); }
        .lose-text { color: var(--danger-color); text-shadow: 0 0 20px var(--danger-color); }

        button {
            background: transparent;
            color: var(--accent-color);
            border: 2px solid var(--accent-color);
            padding: 15px 40px;
            font-size: 1.5rem;
            cursor: pointer;
            text-transform: uppercase;
            transition: all 0.3s ease;
            font-weight: bold;
            letter-spacing: 2px;
            border-radius: 50px;
            box-shadow: 0 0 10px rgba(0, 243, 255, 0.2);
        }

        button:hover {
            background: var(--accent-color);
            color: var(--bg-color);
            box-shadow: 0 0 30px rgba(0, 243, 255, 0.6);
            transform: scale(1.05);
        }

        .controls-hint {
            margin-top: 30px;
            font-size: 0.9rem;
            color: #888;
            text-align: center;
            line-height: 1.6;
        }

        .powerup-indicator {
            display: flex;
            gap: 10px;
            margin-top: 10px;
        }
        .active-powerup {
            padding: 5px 10px;
            border-radius: 15px;
            font-size: 0.8rem;
            color: #000;
            font-weight: bold;
            animation: pulse 1s infinite;
        }
        .pw-wide { background: var(--accent-color); }
        .pw-multi { background: var(--success-color); }

        @keyframes pulse {
            0% { box-shadow: 0 0 5px currentColor; }
            50% { box-shadow: 0 0 15px currentColor; }
            100% { box-shadow: 0 0 5px currentColor; }
        }
    </style>
</head>
<body>

    <h1>Neon Breakout</h1>

    <div id="game-container">
        <canvas id="gameCanvas" width="800" height="600"></canvas>
        
        <div id="ui-layer">
            <div class="hud">
                <div>SCORE: <span id="scoreDisplay">0</span></div>
                <div>LIVES: <span id="livesDisplay">3</span></div>
            </div>
            <div class="powerup-indicator" id="activeEffects">
                <!-- Active effects appear here -->
            </div>
        </div>

        <div id="overlay">
            <h2 id="overlayTitle">BREAKOUT</h2>
            <button id="startBtn">Start Game</button>
            <div class="controls-hint">
                Mouse to Move / Click to Launch<br>
                Arrow Keys (Left/Right) also supported
            </div>
        </div>
    </div>

<script>
/**
 * GAME CONSTANTS & CONFIGURATION
 */
const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 600;
const PADDLE_WIDTH_BASE = 100;
const PADDLE_HEIGHT = 15;
const BALL_RADIUS = 8;
const BALL_SPEED_BASE = 6;
const BRICK_ROWS = 6;
const BRICK_COLS = 9;
const BRICK_GAP = 8;
const BRICK_HEIGHT = 25;

// Brick Types
const TYPE_NORMAL = 0; // 1 Hit
const TYPE_HEAVY = 1;  // 2 Hits (Cracks)

// PowerUps
const PU_WIDE = 'wide';   // Wider paddle
const PU_MULTI = 'multi'; // Multi-ball

// Colors
const COLORS = {
    paddle: '#00f3ff',
    ball: '#ffffff',
    brickNormal: '#ff0055',
    brickHeavy: '#aa00aa',
    brickHeavyCracked: '#ff9900',
    text: '#ffffff',
    powerups: {
        [PU_WIDE]: '#00f3ff',
        [PU_MULTI]: '#00ff9d'
    }
};

/**
 * GAME STATE
 */
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('scoreDisplay');
const livesEl = document.getElementById('livesDisplay');
const overlay = document.getElementById('overlay');
const overlayTitle = document.getElementById('overlayTitle');
const startBtn = document.getElementById('startBtn');
const activeEffectsEl = document.getElementById('activeEffects');

let gameLoopId;
let animationFrameId;
let score = 0;
let lives = 3;
let gameState = 'MENU'; // MENU, PLAYING, GAMEOVER, VICTORY
let particles = [];
let powerUps = [];

// Input State
let mouseX = CANVAS_WIDTH / 2;
let keys = { ArrowLeft: false, ArrowRight: false };
let isMouseDown = false;

// Entities
let paddle = {
    x: CANVAS_WIDTH / 2 - PADDLE_WIDTH_BASE / 2,
    y: CANVAS_HEIGHT - 40,
    width: PADDLE_WIDTH_BASE,
    height: PADDLE_HEIGHT,
    color: COLORS.paddle,
    widthTimer: null
};

let balls = [];
let bricks = [];

/**
 * CLASSES
 */

class Ball {
    constructor(x, y, dx, dy) {
        this.x = x;
        this.y = y;
        this.dx = dx;
        this.dy = dy;
        this.radius = BALL_RADIUS;
        this.speed = BALL_SPEED_BASE;
        this.active = true;
        this.color = COLORS.ball;
    }

    update() {
        // Wall Collisions
        if (this.x - this.radius < 0) {
            this.x = this.radius;
            this.dx = -this.dx;
        } else if (this.x + this.radius > CANVAS_WIDTH) {
            this.x = CANVAS_WIDTH - this.radius;
            this.dx = -this.dx;
        }

        if (this.y - this.radius < 0) {
            this.y = this.radius;
            this.dy = -this.dy;
        } else if (this.y - this.radius > CANVAS_HEIGHT) {
            this.active = false; // Ball lost
        }

        // Paddle Collision
        if (
            this.y + this.radius >= paddle.y &&
            this.y - this.radius <= paddle.y + paddle.height &&
            this.x >= paddle.x &&
            this.x <= paddle.x + paddle.width
        ) {
            // Physics: Calculate angle based on where it hit the paddle
            let hitPoint = this.x - (paddle.x + paddle.width / 2);
            // Normalize hit point (-1 to 1)
            hitPoint = hitPoint / (paddle.width / 2);
            
            // Clamp to avoid extreme angles
            hitPoint = Math.max(-1, Math.min(1, hitPoint));

            // Calculate angle (Max 60 degrees)
            let angle = hitPoint * (Math.PI / 3); 

            // Calculate new velocity
            // Keep speed constant, change direction
            this.dx = this.speed * Math.sin(angle);
            this.dy = -this.speed * Math.cos(angle);
            
            // Push ball out of paddle to prevent sticking
            this.y = paddle.y - this.radius - 1;
        }

        this.x += this.dx;
        this.y += this.dy;
    }

    draw() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        ctx.fillStyle = this.color;
        ctx.shadowBlur = 10;
        ctx.shadowColor = this.color;
        ctx.fill();
        ctx.shadowBlur = 0;
        ctx.closePath();
    }
}

class Brick {
    constructor(col, row, type) {
        this.width = (CANVAS_WIDTH - (BRICK_COLS + 1) * BRICK_GAP) / BRICK_COLS;
        this.height = BRICK_HEIGHT;
        this.x = BRICK_GAP + col * (this.width + BRICK_GAP);
        this.y = BRICK_GAP + row * (this.height + BRICK_GAP);
        this.type = type;
        this.status = 1; // 1 = Active, 0 = Broken
        this.hits = type === TYPE_HEAVY ? 2 : 1;
    }

    hit() {
        this.hits--;
        if (this.hits <= 0) {
            this.status = 0;
            return true; // Broken
        }
        return false; // Still standing
    }

    draw() {
        if (this.status === 0) return;

        let color = COLORS.brickNormal;
        if (this.type === TYPE_HEAVY) {
            color = this.hits === 2 ? COLORS.brickHeavy : COLORS.brickHeavyCracked;
        }

        ctx.fillStyle = color;
        ctx.shadowBlur = 5;
        ctx.shadowColor = color;
        
        // Draw rounded rect
        const r = 5;
        ctx.beginPath();
        ctx.roundRect(this.x, this.y, this.width, this.height, r);
        ctx.fill();
        
        // Detail for cracked heavy bricks
        if (this.type === TYPE_HEAVY && this.hits === 1) {
            ctx.strokeStyle = '#000';
            ctx.lineWidth = 1;
            ctx.shadowBlur = 0;
            ctx.beginPath();
            ctx.moveTo(this.x + this.width/4, this.y);
            ctx.lineTo(this.x + this.width/2, this.y + this.height/2);
            ctx.lineTo(this.x + 3*this.width/4, this.y);
            ctx.stroke();
            
            ctx.beginPath();
            ctx.moveTo(this.x + this.width/2, this.y + this.height/2);
            ctx.lineTo(this.x + this.width/2, this.y + this.height);
            ctx.stroke();
        } else {
            ctx.shadowBlur = 0;
        }

        // PowerUp Chance Icon
        if (this.type === TYPE_NORMAL && Math.random() < 0.15 && this.status === 1) {
            // Only draw icon if it's a potential powerup brick (decided on creation but we render a generic icon)
            // For better design, we'd set a property on creation. 
            // Let's cheat slightly and draw a small icon if it's the first row (higher chance)
            if (Math.random() > 0.6) {
                this.hasPowerUp = true;
                ctx.fillStyle = '#fff';
                ctx.font = '12px Arial';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText('?', this.x + this.width/2, this.y + this.height/2);
            }
        }
    }
}

class Particle {
    constructor(x, y, color) {
        this.x = x;
        this.y = y;
        this.color = color;
        this.size = Math.random() * 4 + 2;
        this.speedX = Math.random() * 6 - 3;
        this.speedY = Math.random() * 6 - 3;
        this.life = 1.0; // Opacity/Life
        this.decay = Math.random() * 0.03 + 0.01;
    }

    update() {
        this.x += this.speedX;
        this.y += this.speedY;
        this.life -= this.decay;
        this.size *= 0.95;
    }

    draw() {
        ctx.globalAlpha = this.life;
        ctx.fillStyle = this.color;
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
        ctx.fill();
        ctx.globalAlpha = 1.0;
    }
}

class PowerUp {
    constructor(x, y, type) {
        this.x = x;
        this.y = y;
        this.type = type;
        this.width = 20;
        this.height = 20;
        this.dy = 3;
        this.active = true;
        
        this.color = COLORS.powerups[type];
        this.icon = type === PU_WIDE ? 'W' : 'M';
    }

    update() {
        this.y += this.dy;
        if (this.y > CANVAS_HEIGHT) this.active = false;

        // Paddle Collision
        if (
            this.y + this.height >= paddle.y &&
            this.y <= paddle.y + paddle.height &&
            this.x + this.width >= paddle.x &&
            this.x <= paddle.x + paddle.width
        ) {
            this.collect();
        }
    }

    collect() {
        this.active = false;
        if (this.type === PU_WIDE) {
            paddle.width = PADDLE_WIDTH_BASE * 1.5;
            if (paddle.widthTimer) clearTimeout(paddle.widthTimer);
            paddle.widthTimer = setTimeout(() => {
                paddle.width = PADDLE_WIDTH_BASE;
            }, 10000);
        } else if (this.type === PU_MULTI) {
            // Spawn 2 additional balls if only 1 exists
            if (balls.length === 1) {
                let b = balls[0];
                balls.push(new Ball(b.x, b.y, b.dx * 0.8, b.dy * 0.8)); // Slightly different angle
                balls.push(new Ball(b.x, b.y, b.dx * -0.8, b.dy * 0.8));
            }
        }
        
        // Update UI
        updatePowerUpUI();
    }

    draw() {
        ctx.fillStyle = this.color;
        ctx.shadowBlur = 10;
        ctx.shadowColor = this.color;
        ctx.beginPath();
        ctx.arc(this.x + 10, this.y + 10, 10, 0, Math.PI * 2);
        ctx.fill();
        
        ctx.fillStyle = '#000';
        ctx.font = 'bold 12px Arial';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.shadowBlur = 0;
        ctx.fillText(this.icon, this.x + 10, this.y + 10);
    }
}

/**
 * GAME LOGIC
 */

function initGame() {
    score = 0;
    lives = 3;
    scoreEl.innerText = score;
    livesEl.innerText = lives;
    
    // Reset Paddle
    paddle.width = PADDLE_WIDTH_BASE;
    paddle.x = (CANVAS_WIDTH - paddle.width) / 2;
    if (paddle.widthTimer) clearTimeout(paddle.widthTimer);
    
    // Clear Entities
    particles = [];
    powerUps = [];
    balls = [];
    bricks = [];

    createLevel();
    
    // Create first ball
    resetBall();
    
    gameState = 'PLAYING';
    overlay.classList.add('hidden');
    
    if (gameLoopId) cancelAnimationFrame(gameLoopId);
    gameLoop();
}

function createLevel() {
    for (let r = 0; r < BRICK_ROWS; r++) {
        for (let c = 0; c < BRICK_COLS; c++) {
            let type = TYPE_NORMAL;
            // Heavy bricks in bottom 3 rows, random distribution
            if (r >= 3 && Math.random() > 0.7) {
                type = TYPE_HEAVY;
            }
            bricks.push(new Brick(c, r, type));
        }
    }
}

function resetBall() {
    balls = [new Ball(CANVAS_WIDTH / 2, CANVAS_HEIGHT - 60, 0, 0)];
    // Ball sticks to paddle initially
}

function spawnParticles(x, y, color) {
    for (let i = 0; i < 15; i++) {
        particles.push(new Particle(x, y, color));
    }
}

function updatePowerUpUI() {
    activeEffectsEl.innerHTML = '';
    
    // Check Wide Paddle
    if (paddle.width > PADDLE_WIDTH_BASE) {
        const el = document.createElement('div');
        el.className = 'active-powerup pw-wide';
        el.innerText = 'WIDE PADDLE';
        activeEffectsEl.appendChild(el);
    }

    // Check Multi-ball
    if (balls.length > 1) {
        const el = document.createElement('div');
        el.className = 'active-powerup pw-multi';
        el.innerText = `MULTI-BALL (${balls.length} BALLS)`;
        activeEffectsEl.appendChild(el);
    }
}

function gameOver(victory) {
    gameState = victory ? 'VICTORY' : 'GAMEOVER';
    overlayTitle.innerText = victory ? "VICTORY!" : "GAME OVER";
    overlayTitle.className = victory ? "win-text" : "lose-text";
    startBtn.innerText = "Play Again";
    overlay.classList.remove('hidden');
}

function gameLoop() {
    // Clear Canvas
    ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

    // Update Paddle Position
    if (keys.ArrowLeft) paddle.x -= 8;
    if (keys.ArrowRight) paddle.x += 8;

    // Clamp Paddle
    if (paddle.x < 0) paddle.x = 0;
    if (paddle.x + paddle.width > CANVAS_WIDTH) paddle.x = CANVAS_WIDTH - paddle.width;

    // Draw Paddle
    ctx.fillStyle = paddle.color;
    ctx.shadowBlur = 15;
    ctx.shadowColor = paddle.color;
    ctx.beginPath();
    ctx.roundRect(paddle.x, paddle.y, paddle.width, paddle.height, 5);
    ctx.fill();
    ctx.shadowBlur = 0;

    // Handle Balls
    let activeBalls = 0;
    balls.forEach(ball => {
        if (ball.active) {
            ball.update();
            ball.draw();
            activeBalls++;

            // Brick Collision
            for (let b of bricks) {
                if (b.status === 1) {
                    if (
                        ball.x > b.x &&
                        ball.x < b.x + b.width &&
                        ball.y > b.y &&
                        ball.y < b.y + b.height
                    ) {
                        ball.dy = -ball.dy;
                        b.hit();
                        score += 10;
                        scoreEl.innerText = score;
                        spawnParticles(b.x + b.width/2, b.y + b.height/2, b.type === TYPE_HEAVY ? COLORS.brickHeavy : COLORS.brickNormal);
                        
                        // Check for PowerUp
                        if (b.hasPowerUp && Math.random() > 0.5) {
                            const type = Math.random() > 0.5 ? PU_WIDE : PU_MULTI;
                            powerUps.push(new PowerUp(b.x + b.width/2, b.y + b.height/2, type));
                        }

                        break; // Only hit one brick per frame per ball to prevent weirdness
                    }
                }
            }
        }
    });

    if (activeBalls === 0) {
        lives--;
        livesEl.innerText = lives;
        if (lives > 0) {
            resetBall();
        } else {
            gameOver(false);
        }
    }

    // Handle PowerUps
    powerUps.forEach(p => {
        p.update();
        p.draw();
    });
    powerUps = powerUps.filter(p => p.active);

    // Handle Particles
    for (let i = particles.length - 1; i >= 0; i--) {
        particles[i].update();
        particles[i].draw();
        if (particles[i].life <= 0) {
            particles.splice(i, 1);
        }
    }

    // Check Victory
    const remainingBricks = bricks.filter(b => b.status === 1).length;
    if (remainingBricks === 0) {
        gameOver(true);
    }

    if (gameState === 'PLAYING') {
        gameLoopId = requestAnimationFrame(gameLoop);
    }
}

/**
 * EVENT LISTENERS
 */

// Mouse
canvas.addEventListener('mousemove', (e) => {
    const rect = canvas.getBoundingClientRect();
    const root = document.documentElement;
    mouseX = e.clientX - rect.left - root.scrollLeft;
    // Center paddle on mouse
    paddle.x = mouseX - paddle.width / 2;
    
    // Clamp
    if (paddle.x < 0) paddle.x = 0;
    if (paddle.x + paddle.width > CANVAS_WIDTH) paddle.x = CANVAS_WIDTH - paddle.width;
});

canvas.addEventListener('mousedown', () => {
    if (gameState === 'PLAYING' && balls.every(b => b.dx === 0)) {
        // Launch ball if it's stuck
        balls[0].dx = (Math.random() * 4 - 2) + 0.5; // Slight random angle
        balls[0].dy = -BALL_SPEED_BASE;
    }
});

// Keyboard
window.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowLeft') keys.ArrowLeft = true;
    if (e.key === 'ArrowRight') keys.ArrowRight = true;
    
    // Launch ball with Space or Up
    if ((e.key === ' ' || e.key === 'ArrowUp') && gameState === 'PLAYING' && balls.every(b => b.dx === 0)) {
        balls[0].dx = (Math.random() * 4 - 2) + 0.5;
        balls[0].dy = -BALL_SPEED_BASE;
    }
});

window.addEventListener('keyup', (e) => {
    if (e.key === 'ArrowLeft') keys.ArrowLeft = false;
    if (e.key === 'ArrowRight') keys.ArrowRight = false;
});

// Start Button
startBtn.addEventListener('click', () => {
    initGame();
});

// Initial Draw (Background)
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

</script>
</body>
</html>