<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Atari Breakout</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #0a0e27;
font-family: 'Courier New', monospace;
color: #00ff00;
}
#gameContainer {
position: relative;
width: 800px;
height: 600px;
}
canvas {
display: block;
background: #000;
border: 3px solid #00ff00;
box-shadow: 0 0 20px rgba(0, 255, 0, 0.5);
image-rendering: pixelated;
image-rendering: crisp-edges;
}
#hud {
position: absolute;
top: 10px;
left: 10px;
font-size: 14px;
color: #00ff00;
text-shadow: 0 0 10px rgba(0, 255, 0, 0.8);
z-index: 10;
font-weight: bold;
line-height: 1.6;
}
#lives {
position: absolute;
top: 10px;
right: 10px;
font-size: 14px;
color: #ff0000;
text-shadow: 0 0 10px rgba(255, 0, 0, 0.8);
z-index: 10;
font-weight: bold;
}
#powerupStatus {
position: absolute;
bottom: 10px;
left: 10px;
font-size: 12px;
color: #ffff00;
text-shadow: 0 0 10px rgba(255, 255, 0, 0.8);
z-index: 10;
font-weight: bold;
line-height: 1.5;
}
.powerup-indicator {
display: inline-block;
margin-right: 15px;
padding: 3px 8px;
border: 1px solid #ffff00;
border-radius: 3px;
font-size: 11px;
}
.powerup-indicator.active {
background: rgba(255, 255, 0, 0.3);
border-color: #00ff00;
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.98);
border: 3px solid #00ff00;
padding: 40px;
text-align: center;
display: none;
z-index: 100;
font-size: 24px;
color: #00ff00;
text-shadow: 0 0 20px rgba(0, 255, 0, 0.8);
min-width: 350px;
box-shadow: 0 0 30px rgba(0, 255, 0, 0.5);
}
#gameOver h1 {
margin-bottom: 20px;
font-size: 36px;
animation: pulse 0.5s infinite;
}
#gameOver p {
margin: 12px 0;
font-size: 18px;
}
#gameOver button {
margin-top: 25px;
padding: 12px 30px;
font-size: 16px;
background: #00ff00;
color: #000;
border: 2px solid #00ff00;
cursor: pointer;
font-weight: bold;
font-family: 'Courier New', monospace;
text-transform: uppercase;
transition: all 0.2s;
}
#gameOver button:hover {
background: #00cc00;
box-shadow: 0 0 15px rgba(0, 255, 0, 0.8);
}
#pauseMessage {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.95);
border: 3px solid #ffff00;
padding: 30px 50px;
text-align: center;
display: none;
z-index: 50;
font-size: 24px;
color: #ffff00;
text-shadow: 0 0 20px rgba(255, 255, 0, 0.8);
font-weight: bold;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.98);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 200;
border: 3px solid #00ff00;
}
#startScreen h1 {
font-size: 56px;
color: #00ff00;
text-shadow: 0 0 30px rgba(0, 255, 0, 0.8);
margin-bottom: 40px;
animation: pulse 0.8s infinite;
letter-spacing: 4px;
}
#startScreen p {
font-size: 15px;
color: #00ff00;
margin: 8px 0;
text-align: center;
max-width: 550px;
line-height: 1.6;
}
#startScreen .powerup-guide {
margin-top: 30px;
padding: 20px;
border: 2px solid #00ff00;
border-radius: 5px;
background: rgba(0, 255, 0, 0.05);
}
#startScreen .powerup-guide p {
font-size: 13px;
margin: 4px 0;
}
#startScreen button {
margin-top: 35px;
padding: 16px 50px;
font-size: 20px;
background: #00ff00;
color: #000;
border: 3px solid #00ff00;
cursor: pointer;
font-weight: bold;
font-family: 'Courier New', monospace;
text-transform: uppercase;
transition: all 0.2s;
letter-spacing: 2px;
}
#startScreen button:hover {
background: #00cc00;
box-shadow: 0 0 30px rgba(0, 255, 0, 0.6);
transform: scale(1.05);
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" width="800" height="600"></canvas>
<div id="hud">
<div>SCORE: <span id="score">0</span></div>
<div>LEVEL: <span id="level">1</span></div>
</div>
<div id="lives">LIVES: <span id="livesCount">3</span></div>
<div id="powerupStatus">
<div><span class="powerup-indicator" id="projectileIndicator">PROJECTILES: OFF</span></div>
<div><span class="powerup-indicator" id="speedIndicator">SPEED: NORMAL</span></div>
<div><span class="powerup-indicator" id="paddleIndicator">PADDLE: NORMAL</span></div>
</div>
<div id="startScreen">
<h1>BREAKOUT</h1>
<p>Arrow Keys or A/D to Move Paddle</p>
<p>Space Bar for Projectiles (when available)</p>
<p>P to Pause/Resume Game</p>
<div class="powerup-guide">
<p style="font-weight: bold; color: #00ff00;">POWERUPS:</p>
<p>🟢 <span style="color: #00ff00;">Paddle Size</span> - Increases paddle width by 10%</p>
<p>🔵 <span style="color: #0088ff;">Speed Boost</span> - Increases ball speed by 10%</p>
<p>🔴 <span style="color: #ff0000;">Projectile</span> - Fire 2 projectiles per shot (Space)</p>
<p>🟣 <span style="color: #ff00ff;">Multiball</span> - Spawn 2 additional balls</p>
</div>
<p style="font-size: 14px; margin-top: 20px; color: #ffff00;">
BRICKS: 🟩 1HP | 🟨 2HP | 🟥 3HP
</p>
<button onclick="game.startGame()">START GAME</button>
</div>
<div id="gameOver">
<h1 id="gameOverTitle">GAME OVER</h1>
<p>Final Score: <span id="finalScore">0</span></p>
<p>Level Reached: <span id="levelReached">1</span></p>
<p id="bricksDestroyed" style="font-size: 14px; color: #ffff00;"></p>
<button onclick="location.reload()">PLAY AGAIN</button>
</div>
<div id="pauseMessage">
⏸ PAUSED - Press P to Resume ⏸
</div>
</div>
<script>
class AudioSystem {
constructor() {
try {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
} catch (e) {
console.warn('Web Audio API not available');
this.audioContext = null;
}
if (this.audioContext) {
this.masterGain = this.audioContext.createGain();
this.masterGain.connect(this.audioContext.destination);
this.masterGain.gain.value = 0.25;
}
this.bgmOscillators = [];
this.bgmPlaying = false;
}
playBrickHit() {
if (!this.audioContext) return;
const now = this.audioContext.currentTime;
const osc = this.audioContext.createOscillator();
const gain = this.audioContext.createGain();
osc.connect(gain);
gain.connect(this.masterGain);
osc.type = 'square';
osc.frequency.setValueAtTime(900, now);
osc.frequency.exponentialRampToValueAtTime(650, now + 0.08);
gain.gain.setValueAtTime(0.35, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.08);
osc.start(now);
osc.stop(now + 0.08);
}
playPaddleHit() {
if (!this.audioContext) return;
const now = this.audioContext.currentTime;
const osc = this.audioContext.createOscillator();
const gain = this.audioContext.createGain();
osc.connect(gain);
gain.connect(this.masterGain);
osc.type = 'triangle';
osc.frequency.setValueAtTime(500, now);
osc.frequency.exponentialRampToValueAtTime(350, now + 0.12);
gain.gain.setValueAtTime(0.3, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.12);
osc.start(now);
osc.stop(now + 0.12);
}
playWallHit() {
if (!this.audioContext) return;
const now = this.audioContext.currentTime;
const osc = this.audioContext.createOscillator();
const gain = this.audioContext.createGain();
osc.connect(gain);
gain.connect(this.masterGain);
osc.type = 'square';
osc.frequency.setValueAtTime(250, now);
gain.gain.setValueAtTime(0.2, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.06);
osc.start(now);
osc.stop(now + 0.06);
}
playPowerupCollect() {
if (!this.audioContext) return;
const now = this.audioContext.currentTime;
const osc1 = this.audioContext.createOscillator();
const gain1 = this.audioContext.createGain();
osc1.connect(gain1);
gain1.connect(this.masterGain);
osc1.type = 'square';
osc1.frequency.setValueAtTime(1000, now);
gain1.gain.setValueAtTime(0.3, now);
gain1.gain.exponentialRampToValueAtTime(0.01, now + 0.12);
osc1.start(now);
osc1.stop(now + 0.12);
const osc2 = this.audioContext.createOscillator();
const gain2 = this.audioContext.createGain();
osc2.connect(gain2);
gain2.connect(this.masterGain);
osc2.type = 'square';
osc2.frequency.setValueAtTime(1300, now + 0.08);
gain2.gain.setValueAtTime(0.3, now + 0.08);
gain2.gain.exponentialRampToValueAtTime(0.01, now + 0.2);
osc2.start(now + 0.08);
osc2.stop(now + 0.2);
}
startBackgroundMusic() {
if (this.bgmPlaying || !this.audioContext) return;
this.bgmPlaying = true;
this.playMelodyPhrase();
}
stopBackgroundMusic() {
this.bgmPlaying = false;
this.bgmOscillators.forEach(osc => {
try {
osc.stop();
} catch (e) {}
});
this.bgmOscillators = [];
}
playMelodyPhrase() {
if (!this.bgmPlaying || !this.audioContext) return;
const now = this.audioContext.currentTime;
const notes = [
{ freq: 440, dur: 0.12 }, { freq: 550, dur: 0.12 }, { freq: 660, dur: 0.12 }, { freq: 550, dur: 0.12 }, { freq: 880, dur: 0.18 }, { freq: 770, dur: 0.18 }, { freq: 660, dur: 0.24 }, ];
let time = now;
notes.forEach(note => {
this.playNote(note.freq, time, note.dur);
time += note.dur;
});
const totalDuration = notes.reduce((a, b) => a + b.dur, 0);
setTimeout(() => this.playMelodyPhrase(), totalDuration * 1000 + 150);
}
playNote(freq, startTime, duration) {
if (!this.audioContext) return;
const osc = this.audioContext.createOscillator();
const gain = this.audioContext.createGain();
osc.connect(gain);
gain.connect(this.masterGain);
osc.type = Math.random() > 0.4 ? 'square' : 'triangle';
osc.frequency.setValueAtTime(freq, startTime);
gain.gain.setValueAtTime(0.12, startTime);
gain.gain.exponentialRampToValueAtTime(0.01, startTime + duration);
osc.start(startTime);
osc.stop(startTime + duration);
this.bgmOscillators.push(osc);
}
}
class Paddle {
constructor(canvas) {
this.canvas = canvas;
this.width = 80;
this.height = 14;
this.x = (canvas.width - this.width) / 2;
this.y = canvas.height - 25;
this.speed = 6;
this.baseWidth = this.width;
}
update(keys) {
const moveLeft = keys['ArrowLeft'] || keys['a'] || keys['A'];
const moveRight = keys['ArrowRight'] || keys['d'] || keys['D'];
if (moveLeft && moveRight) {
} else if (moveLeft) {
this.x -= this.speed;
} else if (moveRight) {
this.x += this.speed;
}
this.x = Math.max(0, Math.min(this.x, this.canvas.width - this.width));
}
draw(ctx) {
ctx.fillStyle = '#00ff00';
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.strokeStyle = '#00cc00';
ctx.lineWidth = 2;
ctx.strokeRect(this.x, this.y, this.width, this.height);
ctx.fillStyle = '#00ff00';
ctx.fillRect(this.x + this.width / 2 - 1, this.y - 3, 2, 3);
}
increasePaddleSize() {
this.width = Math.min(this.baseWidth * 1.1, 150);
}
resetPaddleSize() {
this.width = this.baseWidth;
}
getHitPosition(ballX) {
return ((ballX - this.x) / this.width) * 2 - 1;
}
}
class Ball {
constructor(canvas, x, y, vx, vy) {
this.canvas = canvas;
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.radius = 4;
this.baseSpeed = Math.sqrt(vx * vx + vy * vy);
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x - this.radius < 0 || this.x + this.radius > this.canvas.width) {
this.vx *= -1;
this.x = Math.max(this.radius, Math.min(this.x, this.canvas.width - this.radius));
game.audio.playWallHit();
}
if (this.y - this.radius < 0) {
this.vy *= -1;
this.y = this.radius;
game.audio.playWallHit();
}
}
draw(ctx) {
ctx.fillStyle = '#ffff00';
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#ffcc00';
ctx.lineWidth = 1;
ctx.stroke();
}
increaseSpeed(factor = 1.1) {
this.vx *= factor;
this.vy *= factor;
this.baseSpeed *= factor;
}
decreaseSpeed(factor = 0.909) {
this.vx *= factor;
this.vy *= factor;
this.baseSpeed *= factor;
}
}
class Brick {
constructor(x, y, width, height, hp) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.maxHp = hp;
this.hp = hp;
}
getColor() {
if (this.hp === 1) return '#00ff00';
if (this.hp === 2) return '#ffff00';
if (this.hp >= 3) return '#ff0000';
return '#ff0000';
}
draw(ctx) {
ctx.fillStyle = this.getColor();
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 1;
ctx.strokeRect(this.x, this.y, this.width, this.height);
ctx.fillStyle = '#000000';
ctx.font = 'bold 11px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(this.hp, this.x + this.width / 2, this.y + this.height / 2);
}
hit() {
this.hp--;
return this.hp <= 0;
}
}
class Powerup {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
this.width = 24;
this.height = 24;
this.vy = 2;
this.vx = Math.random() * 2 - 1;
this.rotation = 0;
}
getColor() {
switch (this.type) {
case 'paddle_size': return '#00ff00';
case 'speed_boost': return '#0088ff';
case 'projectile': return '#ff0000';
case 'multiball': return '#ff00ff';
default: return '#ffffff';
}
}
getSymbol() {
switch (this.type) {
case 'paddle_size': return 'P';
case 'speed_boost': return 'S';
case 'projectile': return 'X';
case 'multiball': return 'M';
default: return '?';
}
}
update() {
this.y += this.vy;
this.x += this.vx;
this.rotation += 0.1;
if (this.x < 0) this.vx *= -1;
if (this.x + this.width > 800) this.vx *= -1;
}
draw(ctx) {
ctx.save();
ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
ctx.rotate(this.rotation);
ctx.fillStyle = this.getColor();
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
ctx.strokeRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.fillStyle = '#000000';
ctx.font = 'bold 13px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(this.getSymbol(), 0, 0);
ctx.restore();
}
}
class Projectile {
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 4;
this.height = 12;
this.vy = -9;
}
update() {
this.y += this.vy;
}
draw(ctx) {
ctx.fillStyle = '#ff4444';
ctx.fillRect(this.x - this.width / 2, this.y, this.width, this.height);
ctx.strokeStyle = '#ff8888';
ctx.lineWidth = 1;
ctx.strokeRect(this.x - this.width / 2, this.y, this.width, this.height);
}
isOffScreen(canvasHeight) {
return this.y < 0;
}
}
class Game {
constructor() {
this.canvas = document.getElementById('gameCanvas');
this.ctx = this.canvas.getContext('2d');
this.audio = new AudioSystem();
this.state = 'menu'; this.score = 0;
this.level = 1;
this.lives = 3;
this.totalBricksDestroyed = 0;
this.paddle = new Paddle(this.canvas);
this.balls = [new Ball(this.canvas, this.canvas.width / 2, this.canvas.height - 50, -3.5, -5)];
this.bricks = [];
this.powerups = [];
this.projectiles = [];
this.keys = {};
this.canShoot = false;
this.canShootEndTime = 0;
this.speedBoostEndTime = 0;
this.paddleSizeEndTime = 0;
this.shootCooldown = 0;
this.setupEventListeners();
this.createBrickLayout();
this.gameLoop();
}
setupEventListeners() {
window.addEventListener('keydown', (e) => {
this.keys[e.key] = true;
if ((e.key === 'p' || e.key === 'P') && (this.state === 'playing' || this.state === 'paused')) {
this.togglePause();
}
if ((e.key === ' ') && this.state === 'playing' && this.canShoot) {
this.fireProjectiles();
e.preventDefault();
}
});
window.addEventListener('keyup', (e) => {
this.keys[e.key] = false;
});
}
createBrickLayout() {
this.bricks = [];
const brickWidth = 60;
const brickHeight = 16;
const paddingX = 20;
const paddingY = 45;
const spacingX = 12;
const spacingY = 12;
const cols = Math.floor((this.canvas.width - paddingX * 2) / (brickWidth + spacingX));
const rows = 3 + Math.floor(this.level / 2);
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const x = paddingX + col * (brickWidth + spacingX);
const y = paddingY + row * (brickHeight + spacingY);
const baseHp = 1 + Math.floor((row / rows) * 2);
const levelBonus = Math.floor(this.level / 3);
const hp = Math.min(baseHp + levelBonus, 3);
const brick = new Brick(x, y, brickWidth, brickHeight, hp);
this.bricks.push(brick);
}
}
}
startGame() {
document.getElementById('startScreen').style.display = 'none';
this.state = 'playing';
this.audio.startBackgroundMusic();
}
togglePause() {
if (this.state === 'playing') {
this.state = 'paused';
} else if (this.state === 'paused') {
this.state = 'playing';
}
document.getElementById('pauseMessage').style.display = this.state === 'paused' ? 'block' : 'none';
}
fireProjectiles() {
if (!this.canShoot || this.shootCooldown > 0) return;
const projectile1 = new Projectile(
this.paddle.x + this.paddle.width / 3,