Models 44

GPT-5 Nano

by OpenAIReasoningClean
Open in new window

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

How it went

Total time
1 min 14 s
Cost
$0.0046
Output tokens
11,551
Spent thinking
6,080 tokens
Speed
160.4 tok/s
Console errors
0
First token after
2.0 s
First visible text after
42.4 s
Prompt tokens
508
Code size
17.5 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-5-nano
Reasoning sent
{"effort":"medium"}
Max output
128,000 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 · 645 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Vanilla Breakout - Self-playing Demo</title>
<style>
  :root {
    --bg0: #0b1020;
    --bg1: #111531;
    --bg2: #1a1f3a;
    --paddle: #4af;
    --paddleWiden: #7af;
    --ball: #fff;
    --brick1: #ff8c00;
    --brick2: #e74c3c;
    --brick3: #f1c40f;
    --grid: rgba(255,255,255,0.08);
  }

  * { box-sizing: border-box; }

  html, body {
    height: 100%;
    margin: 0;
    background: linear-gradient(#0b1020, #0a0e1a 60%, #0b1020);
    color: #eaeefc;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial;
    overflow: hidden;
    user-select: none;
  }

  /* Canvas scales to fit any viewport while preserving aspect */
  .stage {
    width: min(92vw, 1200px);
    aspect-ratio: 5 / 3; /* internal game: 900x540 ~ 5:3 */
    margin: 0 auto;
    display: block;
    position: relative;
  }

  canvas {
    width: 100%;
    height: 100%;
    display: block;
    background: radial-gradient(circle at 40% 0%, rgba(255,255,255,0.08), rgba(0,0,0,0) 40%),
                linear-gradient(#162044, #0b1020 60%, #05233a 100%);
    border-radius: 14px;
    box-shadow: 0 12px 40px rgba(0,0,0,0.5);
  }

  /* HUDs */
  .hud {
    position: absolute;
    top: 8px;
    left: 8px;
    padding: 6px 12px;
    border-radius: 8px;
    background: rgba(0,0,0,0.35);
    color: #eaf7ff;
    font-weight: 600;
    letter-spacing: .3px;
    font-size: 14px;
  }

  /* Overlay for start / end screens */
  .overlay {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0,0,0,0.55);
    backdrop-filter: blur(1px);
  }

  .overlay.active { display: flex; }

  .panel {
    background: linear-gradient(#1a2240, #0b1020);
    border: 1px solid rgba(255,255,255,0.15);
    padding: 22px 26px;
    border-radius: 12px;
    text-align: center;
    max-width: 520px;
    width: calc(100% - 40px);
    box-shadow: 0 20px 60px rgba(0,0,0,0.5);
  }

  .panel h1 {
    margin: 0 0 8px;
    font-size: 28px;
    letter-spacing: .3px;
  }

  .panel p {
    margin: 6px 0 18px;
    color: #cbd8ff;
    font-size: 15px;
  }

  .btn {
    padding: 10px 16px;
    font-size: 14px;
    border-radius: 8px;
    border: none;
    cursor: pointer;
    color: #062a3a;
    background: linear-gradient(#a9ff4d, #58e000);
    font-weight: 700;
  }

  .btn.secondary {
    background: linear-gradient(#7a7a7a, #4a4a4a);
    color: #fff;
  }

  .hidden { display: none; }

  /* Responsive tweaks for narrow viewports */
  @media (max-width: 720px) {
    .hud { font-size: 12px; padding: 5px 10px; }
    .panel h1 { font-size: 22px; }
  }
</style>
</head>
<body>
  <div class="stage" aria-label="Breakout game stage">
    <canvas id="gameCanvas" width="900" height="540"></canvas>
    <div id="hud" class="hud" aria-label="score and lives">Score: 0 • Lives: 3</div>
    <div id="overlay" class="overlay" aria-label="start screen">
      <div class="panel">
        <h1>Breakout: Self-Playing Demo</h1>
        <p>Click or press any key to start. The paddle is controlled by AI, but you can also move with the mouse or arrow keys if you wish.</p>
        <button id="startBtn" class="btn" aria-label="Start game">Start</button>
      </div>
    </div>
  </div>

<script>
(() => {
  // Canvas and rendering context
  const canvas = document.getElementById('gameCanvas');
  const ctx = canvas.getContext('2d', { alpha: false });

  // Game constants (internal resolution matches canvas size: 900x540)
  const GAME_W = 900;
  const GAME_H = 540;

  // HUD
  const hud = document.getElementById('hud');
  const overlay = document.getElementById('overlay');
  const startBtn = document.getElementById('startBtn');

  // Game state
  let bricks = [];
  let balls = [];
  let powerUps = [];
  let particles = [];
  let score = 0;
  let lives = 3;
  let started = false;
  let gameOver = false;
  let gameWon = false;

  // Input / AI
  let manualActive = false; // whether user input is active
  let mouseX = GAME_W / 2;
  let keyLeft = false, keyRight = false;

  // Device input: paddle
  const PADDLE = {
    x: GAME_W / 2 - 60,
    y: GAME_H - 34,
    w: 120,
    h: 12,
    baseW: 120,
    speed: 9,
    widenUntil: 0 // timestamp when widen ends
  };

  // Power-up state
  const POWER = {
    WIDE: 'wide',
    MULTI: 'multiball'
  };
  let activePower = null;
  let powerEndTime = 0;
  const WIDE_MULTIPLIER = 1.6;
  const WIDE_DURATION = 10000; // ms

  // Ball
  function createBall(x, y, vx, vy) {
    return {
      x, y,
      r: 6,
      vx, vy,
      speed: Math.hypot(vx, vy),
      color: '#fff'
    };
  }

  // Init stage: bricks, paddle, one ball
  function initLevel() {
    bricks = [];
    powerUps = [];
    balls = [];
    particles = [];
    score = 0;
    // Bricks grid
    const cols = 12;
    const rows = 6; // at least 6 rows
    const gap = 8;
    const brickW = (GAME_W - (cols + 1) * gap) / cols;
    const brickH = 22;

    for (let r = 0; r < rows; r++) {
      for (let c = 0; c < cols; c++) {
        const x = gap + c * (brickW + gap);
        const y = 40 + r * (brickH + gap);
        // Health: most bricks 1, some 2
        const isTwoHp = Math.random() < 0.25; // ~25% bricks tougher
        const hp = isTwoHp ? 2 : 1;
        // Random power-up bricks
        const hasPower = Math.random() < 0.15; // ~15% bricks carry a power-up
        let powerType = null;
        if (hasPower) {
          powerType = Math.random() < 0.5 ? POWER.WIDE : POWER.MULTI;
        }

        bricks.push({
          x, y, w: brickW, h: brickH, hp, maxHp: hp,
          alive: true,
          color: hp === 2 ? '#e74c3c' : '#ff8c00',
          cracked: false,
          hasPower, powerType
        });
      }
    }

    // Paddle
    PADDLE.x = GAME_W / 2 - PADDLE.w / 2;
    PADDLE.y = GAME_H - 34;
    PADDLE.w = PADDLE.baseW;
    PADDLE.widenUntil = 0;

    // One ball ready to launch (behind the scenes, AI will handle launch)
    balls = [];
    const initBall = createBall(PADDLE.x + PADDLE.w / 2, PADDLE.y - 8, 0, -4);
    balls.push(initBall);

    // Initial overlay reset
    score = 0;
    lives = 3;
    gameOver = false;
    gameWon = false;
    started = false;
    activePower = null;
    powerEndTime = 0;
  }

  // Collision helpers
  function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }

  function circleRectCollision(circle, rect) {
    const closestX = clamp(circle.x, rect.x, rect.x + rect.w);
    const closestY = clamp(circle.y, rect.y, rect.y + rect.h);
    const dx = circle.x - closestX;
    const dy = circle.y - closestY;
    return (dx * dx + dy * dy) <= (circle.r * circle.r);
  }

  // Apply power-up effect
  function applyPower(type) {
    if (type === POWER.WIDE) {
      // widen paddle for duration
      PADDLE.w = Math.min(GAME_W * 0.75, PADDLE.baseW * WIDE_MULTIPLIER);
      PADDLE.widenUntil = performance.now() + WIDE_DURATION;
    } else if (type === POWER.MULTI) {
      // spawn two extra balls (total 3)
      const baseBall = balls[0];
      if (baseBall) {
        // spawn two with slight angles
        const spawnAngles = [-0.35, 0.35];
        for (let a of spawnAngles) {
          const speed = Math.hypot(baseBall.vx, baseBall.vy) || 4;
          const vx = Math.cos(a) * speed;
          const vy = -Math.abs(Math.sin(a) * speed) - 0.5; // upward-ish
          balls.push(createBall(baseBall.x, baseBall.y, vx, vy));
        }
      }
    }
  }

  // Spawn particles at brick break
  function spawnParticles(x, y, color) {
    const count = 14;
    for (let i = 0; i < count; i++) {
      const angle = (Math.PI * 2) * (i / count);
      const speed = 1.5 + Math.random() * 2.5;
      particles.push({
        x, y,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed,
        life: 40 + Math.random() * 20,
        color
      });
    }
  }

  // Create a new brick break soundless spark (omitted; visuals only)

  // Input handling
  function onMouseMove(e) {
    const rect = canvas.getBoundingClientRect();
    mouseX = (e.clientX - rect.left) * (GAME_W / rect.width);
    // mark manual control
    manualActive = true;
  }

  function onKeyDown(e) {
    if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') manualActive = true;
  }

  // Start game
  function startGame() {
    overlay.classList.remove('active');
    started = true;
  }

  // Update HUD
  function updateHUD() {
    hud.textContent = `Score: ${score} • Lives: ${lives}`;
  }

  // Main loop
  let lastTs = 0;
  function loop(ts) {
    const dt = Math.min(32, ts - lastTs);
    lastTs = ts;

    // Clear and draw background
    ctx.fillStyle = '#0b1020';
    ctx.fillRect(0, 0, GAME_W, GAME_H);

    // Update power-downs
    const now = performance.now();
    if (PADDLE.widenUntil > 0 && now > PADDLE.widenUntil) {
      PADDLE.widenUntil = 0;
      PADDLE.w = PADDLE.baseW;
    }
    if (powerEndTime > 0 && now > powerEndTime && activePower) {
      activePower = null;
      // could reset any other effects here if needed
    }

    // AI / Manual paddle control
    const paddleCenter = PADDLE.x + PADDLE.w / 2;
    // If user input active, use manual. Otherwise use simple AI chasing the closest ball.
    let targetX;
    if (manualActive && (mouseX !== undefined)) {
      targetX = mouseX - PADDLE.w / 2;
    } else {
      // AI: follow the ball closest to the paddle (highest y)
      let target = null;
      let bestY = -1;
      for (const b of balls) {
        if (!target || b.y > bestY) {
          target = b;
          bestY = b.y;
        }
      }
      if (target) {
        targetX = target.x - PADDLE.w / 2;
      } else {
        targetX = paddleCenter - PADDLE.w / 2;
      }
    }
    // Move paddle toward targetX with speed limit
    const dx = targetX - PADDLE.x;
    const maxMove = PADDLE.speed;
    if (Math.abs(dx) > 1) {
      PADDLE.x += Math.sign(dx) * Math.min(Math.abs(dx), maxMove);
      // clamp
      if (PADDLE.x < 0) PADDLE.x = 0;
      if (PADDLE.x + PADDLE.w > GAME_W) PADDLE.x = GAME_W - PADDLE.w;
    }

    // Update and draw bricks
    for (const br of bricks) {
      // draw
      if (br.alive) {
        // brick color depends on hp
        let color = br.color;
        if (br.hp === 2) color = '#e74c3c';
        if (br.cracked) color = '#d35400';
        ctx.fillStyle = color;
        ctx.fillRect(br.x, br.y, br.w, br.h);

        // power-up icon overlay
        if (br.hasPower) {
          ctx.fillStyle = '#fff';
          ctx.globalAlpha = 0.95;
          ctx.beginPath();
          ctx.arc(br.x + br.w - 8, br.y + 8, 5, 0, Math.PI * 2);
          ctx.fill();
          ctx.globalAlpha = 1;
        }

        // grid lines
        ctx.strokeStyle = 'rgba(0,0,0,0.15)';
        ctx.strokeRect(br.x, br.y, br.w, br.h);
      }
    }

    // Update and draw power-ups
    for (let i = powerUps.length - 1; i >= 0; i--) {
      const p = powerUps[i];
      p.y += p.vy;
      // draw icon as small square with color
      ctx.fillStyle = p.color;
      ctx.fillRect(p.x - 6, p.y - 6, 12, 12);
      // collision with paddle
      if (circleRectCollision({ x: p.x, y: p.y, r: 6, vx: 0, vy: 0 }, { x: PADDLE.x, y: PADDLE.y, w: PADDLE.w, h: PADDLE.h })) {
        // apply effect
        applyPower(p.type);
        powerUps.splice(i, 1);
      } else if (p.y > GAME_H + 20) {
        powerUps.splice(i, 1);
      }
    }

    // Update and draw balls
    for (let i = balls.length - 1; i >= 0; i--) {
      const b = balls[i];
      // move
      b.x += b.vx;
      b.y += b.vy;

      // walls
      if (b.x - b.r < 0) { b.x = b.r; b.vx = -b.vx; }
      if (b.x + b.r > GAME_W) { b.x = GAME_W - b.r; b.vx = -b.vx; }
      if (b.y - b.r < 0) { b.y = b.r; b.vy = -b.vy; }

      // paddle collision
      if (b.y + b.r >= PADDLE.y && b.y - b.r <= PADDLE.y + PADDLE.h &&
          b.x >= PADDLE.x && b.x <= PADDLE.x + PADDLE.w) {
        // Compute hit position relative to paddle center
        const relX = (b.x - (PADDLE.x + PADDLE.w / 2)) / (PADDLE.w / 2); // -1..1
        const maxAngle = Math.PI * 5 / 12; // 75 degrees
        const angle = relX * maxAngle;
        const speed = Math.hypot(b.vx, b.vy);
        b.vx = Math.sin(angle) * speed;
        b.vy = -Math.abs(Math.cos(angle) * speed);
        b.y = PADDLE.y - b.r - 1; // nudge out
      }

      // bottom boundary: lose ball
      if (b.y - b.r > GAME_H) {
        balls.splice(i, 1);
      } else {
        // draw ball
        ctx.fillStyle = b.color;
        ctx.beginPath();
        ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
        ctx.fill();
      }
    }

    // Brick collisions (ball vs bricks)
    // Only check collisions if there are balls
    outerLoop:
    for (let bi = 0; bi < bricks.length; bi++) {
      const br = bricks[bi];
      if (!br.alive) continue;
      // test against each ball (take the first that collides)
      for (let biBall = 0; biBall < balls.length; biBall++) {
        const ball = balls[biBall];
        if (circleRectCollision(ball, br)) {
          // determine side by closest point technique
          const closestX = clamp(ball.x, br.x, br.x + br.w);
          const closestY = clamp(ball.y, br.y, br.y + br.h);
          const dx = ball.x - closestX;
          const dy = ball.y - closestY;
          if (Math.abs(dx) > Math.abs(dy)) {
            ball.vx = -ball.vx;
          } else {
            ball.vy = -ball.vy;
          }

          br.hp--;
          if (br.hp <= 0) {
            br.alive = false;
            // hard brick destroyed: crack effect
            spawnParticles(br.x + br.w / 2, br.y + br.h / 2, br.color);
            // spawn power-up if brick had one
            if (br.hasPower) {
              // drop power-up at brick center
              powerUps.push({
                x: br.x + br.w / 2,
                y: br.y + br.h / 2,
                vy: 1.6,
                type: br.powerType,
                color: br.powerType === POWER.WIDE ? '#7bd' : '#f7f'
              });
            }
            // add score
            score += br.maxHp * 40;
          } else {
            // first hit cracks
            br.cracked = true;
            // slight score for hit
            score += 5;
          }
          // prevent multiple collisions in one frame
          break outerLoop;
        }
      }
    }

    // Particles draw
    for (let i = particles.length - 1; i >= 0; i--) {
      const p = particles[i];
      p.x += p.vx;
      p.y += p.vy;
      p.vy += 0.04; // gravity
      p.life -= 1;
      ctx.fillStyle = p.color;
      ctx.globalAlpha = Math.max(0, p.life / 60);
      ctx.fillRect(p.x, p.y, 2, 2);
      ctx.globalAlpha = 1;
      if (p.life <= 0) particles.splice(i, 1);
    }

    // Draw paddle (with potential widen)
    ctx.fillStyle = (PADDLE.w > PADDLE.baseW) ? '#9ef' : '#4af';
    ctx.fillRect(PADDLE.x, PADDLE.y, PADDLE.w, PADDLE.h);

    // Score / lives drawn in HUD element only

    // End conditions
    if (!started && balls.length > 0) {
      // keep AI running; nothing to do
    }

    // Win check
    const bricksLeft = bricks.filter(b => b.alive).length;
    if (bricksLeft === 0) {
      gameWon = true;
      overlay.classList.add('active');
      started = false;
      document.querySelector('#overlay .panel h1').textContent = 'You Win!';
      document.querySelector('#overlay .panel p').textContent = 'All bricks cleared. Play again?';
      document.getElementById('startBtn').textContent = 'Restart';
      overlay.classList.add('active');
      // Freeze game
      return;
    }

    // Lose check: no lives left
    if (lives <= 0) {
      gameOver = true;
      overlay.classList.add('active');
      started = false;
      document.querySelector('#overlay .panel h1').textContent = 'Game Over';
      document.querySelector('#overlay .panel p').textContent = 'Try again?';
      document.getElementById('startBtn').textContent = 'Restart';
      overlay.classList.add('active');
      return;
    }

    updateHUD();
    requestAnimationFrame(loop);
  }

  // Start / restart flow
  function resetAndStart() {
    initLevel();
    updateHUD();
    // initial launch direction (AI will handle)
    // Give the first ball a gentle upward nudge if not yet moving
    if (balls.length > 0) {
      const b = balls[0];
      if (b.vy === 0 && b.vx === 0) {
        b.vy = -4;
        b.vx = 0;
      }
    }
    overlay.classList.remove('active');
    overlay.classList.add('hidden');
    started = true;
    manualActive = false;
    requestAnimationFrame(loop);
  }

  // Click to start
  startBtn.addEventListener('click', () => {
    resetAndStart();
  });
  canvas.addEventListener('mousemove', onMouseMove);
  window.addEventListener('keydown', onKeyDown);

  // Init + start screen
  function init() {
    // Prepare level, but do not start the loop yet
    initLevel();
    updateHUD();
    overlay.classList.add('active');
    overlay.classList.remove('hidden');
    // Ensure initial state
    started = false;
  }

  // Resize handling (keep crisp)
  function fitCanvas() {
    // The canvas is drawn at 900x540 but CSS scales; nothing heavy needed
  }

  // Kick off
  init();
  fitCanvas();

  // Expose a fallback: start on pressing any key or clicking overlay
  window.addEventListener('mousedown', () => {
    if (!started) resetAndStart();
  });
  window.addEventListener('keypress', () => {
    if (!started) resetAndStart();
  });

  // Prevent scroll on space or arrows
  window.addEventListener('keydown', (e) => {
    if ([' ', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
      e.preventDefault();
    }
  });

  // Start with a short auto-run: keep the overlay visible until user starts
  overlay.classList.add('active');
  // The actual loop starts after start interaction

})();
</script>
</body>
</html>
Reasoning trace (6,080 tokens)
**Planning Breakout game features**

I need to create a self-contained Breakout game in an HTML file. The plan includes paddle control by mouse and keyboard (left/right arrows) to move the paddle. The ball's bounce angle will depend on where it hits the paddle. I'll design at least six rows of bricks, mixing normal bricks with tougher ones that require two hits to break and visibly crack after the first hit. Power-up icons will drop from some bricks, offering power-ups for the player.**Implementing power-ups in the game**

I’m working on the power-up features for the Breakout game. Some bricks will have a property that randomly assigns them a power-up type, either "size" or "multiball." When these bricks are destroyed, a power-up will drop that falls due to gravity. If the paddle catches a power-up in time, it will enlarge the paddle width by 1.5 times for 10 seconds. For multiball, I'll spawn an additional two balls, bringing the total to three. That's clear now!**Detailing game mechanics**

For power-up countdowns, I need to revert the paddle width back to normal after 10 seconds and remove any extra balls after their duration is up. 

I will ensure that the ball never passes through the bricks or paddle by implementing precise collision detection. This will involve updating ball positions with velocity each frame and detecting collisions with walls, the paddle, and bricks. 

I'll also create particles that appear when a brick breaks, represented as small colored dots, while displaying the player’s remaining lives and score.**Setting up game screens and demo mode**

I need to create game over and victory screens, including a restart option, and implement UI overlay screens. 

For the self-playing demo mode, I’ll start with a "Click or press a key to start" message. The game will run automatically, using a simple AI to control the paddle, making it follow the ball's x position within a small speed. After the user interacts with the canvas, the game will begin, with a full-screen overlay providing instructions on how to start the demo. It has to be properly implemented behind the message.**Planning game implementation and mechanics**

I'll start by implementing the AI to control the paddle in demo mode, where user control will be supported but overridden by AI movements. The game will display a "Click or press a key to start" message, and I'll ensure the code runs without errors on the latest Chrome while avoiding advanced features.

The game canvas will be responsive, adapting to various screen sizes while maintaining the correct aspect ratio. With at least six rows and twelve columns of bricks, tougher bricks will occasionally drop power-ups. 

I will ensure that collisions are managed to prevent any ball from passing through bricks or the paddle, and particles will be created when bricks break. Victory and game over screens will allow for restarting the game. All of this will be contained in a single HTML file, complete with structured objects for bricks, power-ups, the ball, the paddle, and particles.**Implementing collision and control mechanics**

I’ll start with calculating collision distances for the ball and bricks, using a simpler method to ensure the ball separates correctly from the rectangle without sticking. I'll handle ball collision with each brick, allowing only one collision per frame to avoid tunneling. 

For paddle collisions, the angle will depend on where the ball hits the paddle. I’ll also track power-ups like 'wide' and 'multiball,'. Multiball will create extra balls near the current ball’s position. 

The AI will control the paddle, searching for the nearest ball, while the overlay will instruct users to start the game, ensuring keyboard input works only after that. Keyboard controls will be limited, and I’ll ensure the system adheres to the requirement of auto-play by ignoring manual inputs during gameplay.**Setting up paddle controls**

I’m planning to manage input effectively for paddle control in the game. Initially, keyboard events will be ignored until the game starts, and we’ll set up a boolean to track whether the game has started or not. 

To satisfy requirements for both mouse and keyboard controls, I’ll allow users to control the paddle once the game has begun. If the AI is active, it won’t override user input; instead, I’ll track manual control whenever the user interacts.

I’ll implement both mouse movement and keyboard arrow keys for paddle movement, ensuring smooth integration with the AI while prioritizing user actions.**Implementing power-ups and game mechanics**

I plan to add power-ups by randomly giving some bricks a 'power' property, aiming for about 8 bricks to have these unique features. I’ll visually differentiate the bricks by color and mark them with a small icon to indicate potential power-ups.

In HTML and CSS, I’ll set up a responsive canvas layout. For the game logic in JavaScript, I’ll establish constants for game dimensions, create paddle and ball objects, and manage movement and collision.

The ball should auto-launch after the overlay, ensuring it doesn’t get stuck. I’ll also implement robust collision detection, keep track of lives, and manage game states, including a victory condition when all bricks are cleared.**Writing the game code**

To add power-ups, I’ll use random logic to set some bricks with a power property and visually indicate cracks after a hit. I’ll draw a small icon on each power-enabled brick to signal its special status.

It’s crucial for the ball not to pass through the paddle or bricks, so I’ll handle collision detection carefully. To avoid tunneling, I’ll implement continuous collision checks by predicting movement paths. I’ll also ensure user experience is smooth in HTML and CSS, keeping everything inline for cross-browser compatibility. Now, I’ll get started on coding the HTML skeleton!