<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Atari Breakout</title>
<style>
body {
background: #000000;
color: #00ff00;
font-family: 'Courier New', monospace;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
flex-direction: column;
}
#container {
position: relative;
width: 800px;
height: 600px;
}
canvas {
border: 4px solid #00ff00;
background: #000;
display: block;
margin: 0 auto;
}
#hud {
position: absolute;
top: 10px;
left: 10px;
color: #00ff00;
font-size: 14px;
text-shadow: 1px 1px #000;
pointer-events: none;
}
#touch-controls {
display: none;
margin-top: 10px;
text-align: center;
}
button {
background: #000;
color: #00ff00;
border: 2px solid #00ff00;
padding: 8px 16px;
margin: 5px;
font-family: 'Courier New', monospace;
cursor: pointer;
font-size: 14px;
}
button:active {
background: #003300;
}
#info {
margin-top: 8px;
font-size: 12px;
color: #00aa00;
}
</style>
</head>
<body>
<div id="container">
<canvas id="game" width="800" height="600"></canvas>
<div id="hud">
<span id="score">SCORE: 00000</span> |
<span id="lives">LIVES: 3</span> |
<span id="level">LEVEL: 1</span>
</div>
</div>
<div id="touch-controls">
<button id="left-btn">LEFT</button>
<button id="right-btn">RIGHT</button>
<button id="fire-btn">FIRE</button>
</div>
<div id="info">SPACE: Start/Pause | Arrows/A/D: Move | Mouse: Aim | Touch: Mobile controls</div>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d', { alpha: true });
const WIDTH = 800;
const HEIGHT = 600;
const STATES = { MENU: 0, PLAYING: 1, PAUSED: 2, GAME_OVER: 3, LEVEL_COMPLETE: 4 };
let gameState = STATES.MENU;
let score = 0;
let lives = 3;
let level = 1;
let lastTime = Date.now();
let frameCount = 0;
let fps = 60;
let paddle = {
x: WIDTH / 2 - 60,
y: HEIGHT - 35,
width: 120,
height: 14,
speed: 7.5,
targetWidth: 120
};
let balls = [];
let bricks = [];
let powerups = [];
let projectiles = [];
let particles = [];
let keys = {};
let mouseX = WIDTH / 2;
let isMouseActive = false;
let audioCtx;
let bgMusicOsc = null;
let bgMusicGain = null;
let musicPlaying = false;
let musicVolume = 0.15;
let isMuted = false;
function initAudio() {
try {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
} catch (e) {
audioCtx = null;
}
}
function playSound(type, vol = 0.4) {
if (!audioCtx || isMuted) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
const filter = audioCtx.createBiquadFilter();
osc.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
if (type === 'brick') {
osc.type = 'square';
osc.frequency.value = 880 + Math.random() * 120;
gain.gain.value = vol * 0.6;
filter.type = 'lowpass';
filter.frequency.value = 1200;
setTimeout(() => osc.stop(), 90);
} else if (type === 'paddle') {
osc.type = 'sawtooth';
osc.frequency.value = 320;
gain.gain.value = vol * 0.5;
setTimeout(() => osc.stop(), 140);
} else if (type === 'wall') {
osc.type = 'sine';
osc.frequency.value = 180;
gain.gain.value = vol * 0.3;
setTimeout(() => osc.stop(), 60);
} else if (type === 'powerup') {
osc.type = 'triangle';
osc.frequency.value = 650;
gain.gain.value = vol;
setTimeout(() => osc.stop(), 220);
} else if (type === 'life') {
osc.type = 'sine';
osc.frequency.value = 520;
gain.gain.value = vol * 0.8;
setTimeout(() => osc.stop(), 180);
} else if (type === 'level') {
osc.type = 'square';
osc.frequency.value = 440;
gain.gain.value = vol * 0.7;
setTimeout(() => osc.stop(), 350);
} else if (type === 'projectile') {
osc.type = 'sawtooth';
osc.frequency.value = 1100;
gain.gain.value = vol * 0.4;
setTimeout(() => osc.stop(), 70);
}
osc.start();
}
function startBackgroundMusic() {
if (!audioCtx || isMuted || musicPlaying) return;
musicPlaying = true;
if (bgMusicOsc) bgMusicOsc.stop();
const osc1 = audioCtx.createOscillator();
const osc2 = audioCtx.createOscillator();
const gain = audioCtx.createGain();
const filter = audioCtx.createBiquadFilter();
osc1.type = 'square';
osc2.type = 'triangle';
osc1.frequency.value = 110;
osc2.frequency.value = 220;
gain.gain.value = musicVolume;
filter.type = 'lowpass';
filter.frequency.value = 900;
osc1.connect(filter);
osc2.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
osc1.start();
osc2.start();
bgMusicOsc = osc1;
bgMusicGain = gain;
let noteIndex = 0;
const melody = [110, 130, 165, 196, 220, 196, 165, 130];
const interval = setInterval(() => {
if (!musicPlaying || !audioCtx || isMuted) {
clearInterval(interval);
return;
}
if (bgMusicOsc) {
bgMusicOsc.frequency.value = melody[noteIndex % melody.length];
noteIndex++;
}
}, 280);
}
function stopBackgroundMusic() {
musicPlaying = false;
if (bgMusicOsc) {
bgMusicOsc.stop();
bgMusicOsc = null;
}
}
function toggleMute() {
isMuted = !isMuted;
if (isMuted) {
stopBackgroundMusic();
} else if (gameState === STATES.PLAYING) {
startBackgroundMusic();
}
}
document.addEventListener('keydown', (e) => {
keys[e.key] = true;
if (e.key === ' ' || e.key === 'Spacebar') {
e.preventDefault();
handleSpace();
}
if (e.key.toLowerCase() === 'm') {
toggleMute();
}
});
document.addEventListener('keyup', (e) => { keys[e.key] = false; });
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
mouseX = e.clientX - rect.left;
isMouseActive = true;
});
canvas.addEventListener('click', () => {
if (gameState === STATES.MENU || gameState === STATES.GAME_OVER) {
startGame();
} else if (gameState === STATES.PAUSED) {
gameState = STATES.PLAYING;
if (!isMuted) startBackgroundMusic();
}
});
function setupTouchControls() {
const leftBtn = document.getElementById('left-btn');
const rightBtn = document.getElementById('right-btn');
const fireBtn = document.getElementById('fire-btn');
const touchDiv = document.getElementById('touch-controls');
if (!leftBtn) return;
let leftHeld = false;
let rightHeld = false;
function updatePaddleTouch() {
if (gameState !== STATES.PLAYING) return;
if (leftHeld) paddle.x -= paddle.speed * 1.6;
if (rightHeld) paddle.x += paddle.speed * 1.6;
paddle.x = Math.max(0, Math.min(WIDTH - paddle.width, paddle.x));
}
leftBtn.addEventListener('touchstart', (e) => { e.preventDefault(); leftHeld = true; });
leftBtn.addEventListener('touchend', () => { leftHeld = false; });
leftBtn.addEventListener('mousedown', () => { leftHeld = true; });
leftBtn.addEventListener('mouseup', () => { leftHeld = false; });
rightBtn.addEventListener('touchstart', (e) => { e.preventDefault(); rightHeld = true; });
rightBtn.addEventListener('touchend', () => { rightHeld = false; });
rightBtn.addEventListener('mousedown', () => { rightHeld = true; });
rightBtn.addEventListener('mouseup', () => { rightHeld = false; });
fireBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
if (gameState === STATES.PLAYING) fireProjectile();
});
fireBtn.addEventListener('mousedown', () => {
if (gameState === STATES.PLAYING) fireProjectile();
});
const isMobile = /Mobi|Android|iPhone|iPad|tablet/i.test(navigator.userAgent) ||
('ontouchstart' in window);
if (isMobile) {
touchDiv.style.display = 'block';
}
canvas.addEventListener('touchstart', handleTouchMove);
canvas.addEventListener('touchmove', handleTouchMove);
function handleTouchMove(e) {
if (gameState !== STATES.PLAYING) return;
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const touchX = e.touches[0].clientX - rect.left;
paddle.x = touchX - paddle.width / 2;
paddle.x = Math.max(0, Math.min(WIDTH - paddle.width, paddle.x));
isMouseActive = true;
}
setInterval(() => {
if (leftHeld || rightHeld) updatePaddleTouch();
}, 16);
}
function handleSpace() {
if (gameState === STATES.MENU || gameState === STATES.GAME_OVER) {
startGame();
} else if (gameState === STATES.PLAYING) {
gameState = STATES.PAUSED;
stopBackgroundMusic();
} else if (gameState === STATES.PAUSED) {
gameState = STATES.PLAYING;
if (!isMuted) startBackgroundMusic();
} else if (gameState === STATES.LEVEL_COMPLETE) {
nextLevel();
}
}
function fireProjectile() {
if (gameState !== STATES.PLAYING) return;
const projX = paddle.x + paddle.width / 2;
projectiles.push({
x: projX,
y: paddle.y - 5,
dy: -13,
width: 4,
height: 14
});
playSound('projectile', 0.35);
createParticles(projX, paddle.y - 3, 3, '#aaaaaa', 'smoke');
}
function resizeCanvas() {
const container = document.getElementById('container');
canvas.width = WIDTH;
canvas.height = HEIGHT;
}
function getBrickColor(hp) {
switch (hp) {
case 1: return '#ffff00'; case 2: return '#00ff00'; case 3: return '#00aaff'; case 4: return '#ff8800'; default: return '#ff3333'; }
}
function createBricks() {
bricks = [];
const cols = 11;
const rows = 5;
const brickW = 62;
const brickH = 18;
const pad = 4;
const startY = 55;
const totalWidth = cols * brickW + (cols - 1) * pad;
const startX = (WIDTH - totalWidth) / 2;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
let hp = 1;
const baseHp = Math.floor(1 + (level - 1) * 0.6 + (rows - r - 1) * 0.35);
hp = Math.max(1, Math.min(5, baseHp));
if (level >= 3 && Math.random() < 0.12) hp = Math.min(5, hp + 1);
const bx = startX + c * (brickW + pad);
const by = startY + r * (brickH + pad);
bricks.push({
x: bx,
y: by,
width: brickW,
height: brickH,
hp: hp,
maxHp: hp,
color: getBrickColor(hp)
});
}
}
}
function createLevelPattern() {
bricks = [];
const cols = 11;
const rows = 5;
const brickW = 62;
const brickH = 18;
const pad = 4;
const startY = 55;
const totalW = cols * brickW + (cols - 1) * pad;
const startX = (WIDTH - totalW) / 2;
let pattern = level % 11;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
let shouldPlace = true;
let hp = Math.max(1, Math.min(5, Math.floor(1 + (level-1)*0.55 + (rows-r-1)*0.3)));
if (pattern === 0) { shouldPlace = true;
} else if (pattern === 1) { shouldPlace = (c >= r && c < cols - r);
} else if (pattern === 2) { const dist = Math.abs(c - cols/2) + Math.abs(r - rows/2);
shouldPlace = dist < 3.5;
} else if (pattern === 3) { shouldPlace = ((r + c) % 2 === 0);
} else if (pattern === 4) { shouldPlace = (r % 2 === 0);
} else if (pattern === 5) { shouldPlace = Math.abs(Math.sin((c + r) * 0.9)) > 0.3;
} else if (pattern === 6) { const d = Math.sqrt((c-5.5)**2 + (r-2)**2);
shouldPlace = d < 3.2 || d > 4.5;
} else if (pattern === 7) { shouldPlace = (c === 5 || r === 2);
} else if (pattern === 8) { const d = Math.abs(Math.sqrt((c-5.5)**2 + (r-2)**2) - 2.2);
shouldPlace = d < 1.1;
} else if (pattern === 9) { shouldPlace = Math.abs(c - 5.5) <= (r * 0.9 + 1);
} else { shouldPlace = Math.random() > 0.15;
}
if (shouldPlace) {
const bx = startX + c * (brickW + pad);
const by = startY + r * (brickH + pad);
bricks.push({
x: bx, y: by, width: brickW, height: brickH,
hp: hp, maxHp: hp, color: getBrickColor(hp)
});
}
}
}
if (bricks.length < 8) {
for (let i = 0; i < 18; i++) {
const c = Math.floor(Math.random()*cols);
const r = Math.floor(Math.random()*rows);
bricks.push({
x: startX + c*(brickW+pad), y: startY + r*(brickH+pad),
width: brickW, height: brickH, hp: 2, maxHp: 2, color: '#00ff00'
});
}
}
}
function createParticles(x, y, count, color, type = 'fire') {
for (let i = 0; i < count; i++) {
let vx = (Math.random() - 0.5) * (type === 'jet' ? 3 : 7);
let vy = (Math.random() - 0.5) * (type === 'jet' ? -5 : 6);
if (type === 'jet') vy = -Math.random() * 4 - 1;
if (type === 'smoke') {
vx *= 0.6; vy = -Math.random() * 2 - 0.5;
}
particles.push({
x: x + (Math.random() - 0.5) * 8,
y: y + (Math.random() - 0.5) * 6,
vx: vx,
vy: vy,
life: (type === 'jet' || type === 'smoke') ? 18 + Math.random() * 14 : 22 + Math.random() * 18,
color: color,
size: (type === 'smoke') ? 2.5 : (3 + Math.random() * 3.5),
type: type,
gravity: type === 'fire' ? 0.08 : 0.02
});
}
}
function createFloatingText(x, y, text, color = '#00ff00') {
particles.push({
x: x, y: y,
vx: 0, vy: -1.8,
life: 55,
color: color,
size: 14,
type: 'text',
text: text,
gravity: -0.01
});
}
function initLevel() {
paddle.x = WIDTH / 2 - paddle.width / 2;
paddle.targetWidth = 120;
paddle.width = 120;
balls = [{
x: WIDTH / 2,
y: HEIGHT - 60,
radius: 7.5,
dx: (level > 1 ? 4.5 : 4) * (Math.random() > 0.5 ? 1 : -1),
dy: -4.2,
speed: 4.5
}];
createLevelPattern();
powerups = [];
projectiles = [];
particles = [];
if (level === 1) {
createParticles(WIDTH/2, HEIGHT/2 - 100, 12, '#00ff00', 'jet');
}
}
function startGame() {
score = 0;
lives = 3;
level = 1;
gameState = STATES.PLAYING;
initLevel();
updateHUD();
if (!isMuted) startBackgroundMusic();
}
function nextLevel() {
level++;
gameState = STATES.PLAYING;
initLevel();
if (level % 2 === 0) {
lives++;
createFloatingText(WIDTH/2, HEIGHT/2 - 80, '+1UP', '#ffff00');
playSound('life');
}
score += 600;
updateHUD();
if (!isMuted) startBackgroundMusic();
}
function update() {
if (gameState !== STATES.PLAYING) return;
let paddleMoved = false;
if (keys['ArrowLeft'] || keys['a'] || keys['A']) {
paddle.x -= paddle.speed;
paddleMoved = true;
}
if (keys['ArrowRight'] || keys['d'] || keys['D']) {
paddle.x += paddle.speed;
paddleMoved = true;
}
if (isMouseActive && !paddleMoved) {
paddle.x = mouseX - paddle.width / 2;
}
if (Math.abs(paddle.width - paddle.targetWidth) > 0.5) {
paddle.width += (paddle.targetWidth - paddle.width) * 0.18;
}
paddle.x = Math.max(0, Math.min(WIDTH - paddle.width, paddle.x));
for (let b = balls.length - 1; b >= 0; b--) {
const ball = balls[b];
ball.x += ball.dx;
ball.y += ball.dy;
if (ball.x - ball.radius < 0) {
ball.x = ball.radius;
ball.dx = -ball.dx * 0.995;
playSound('wall');
}
if (ball.x + ball.radius > WIDTH) {
ball.x = WIDTH - ball.radius;
ball.dx = -ball.dx * 0.995;
playSound('wall');
}
if (ball.y - ball.radius < 0) {
ball.y = ball.radius;
ball.dy = -ball.dy;
playSound('wall');
}
if (ball.y + ball.radius > HEIGHT) {
balls.splice(b, 1);
if (balls.length === 0) {
lives--;
updateHUD();
if (lives <= 0) {
gameState = STATES.GAME_OVER;
stopBackgroundMusic();
return;
} else {
balls.push({
x: WIDTH/2,
y: HEIGHT - 60,
radius: 7.5,
dx: 4.2 * (Math.random() > 0.5 ? 1 : -1),
dy: -4.3,
speed: 4.5
});
paddle.x = WIDTH/2 - paddle.width/2;
}
}
continue;
}
if (ball.y + ball.radius > paddle.y &&
ball.y - ball.radius < paddle.y + paddle.height &&
ball.x > paddle.x && ball.x < paddle.x + paddle.width) {
const hitPos = (ball.x - paddle.x) / paddle.width; const angle = (hitPos - 0.5) * (Math.PI * 0.72);
const speed = Math.min(8.2, Math.max(3.2, Math.sqrt(ball.dx*ball.dx + ball.dy*ball.dy)));
ball.dx = Math.sin(angle) * speed;
ball.dy = -Math.abs(Math.cos(angle) * speed);
if (speed < 7.5) {
ball.dx *= 1.03;
ball.dy *= 1.03;
}
playSound('paddle');
createParticles(ball.x, paddle.y, 4, '#00aaff', 'jet');
}
for (let i = bricks.length - 1; i >= 0; i--) {
const brick = bricks[i];
if (ball.x + ball.radius > brick.x && ball.x - ball.radius < brick.x + brick.width &&
ball.y + ball.radius > brick.y && ball.y - ball.radius < brick.y + brick.height) {
brick.hp--;
score += 10 * brick.maxHp;
playSound('brick');
updateHUD();
createParticles(brick.x + brick.width/2, brick.y + brick.height/2, 7, brick.color, 'fire');
const prevX = ball.x - ball.dx;
const prevY = ball.y - ball.dy;
if (prevX < brick.x || prevX > brick.x + brick.width) {
ball.dx = -ball.dx;
} else {
ball.dy = -ball.dy;
}
if (brick.hp <= 0) {
bricks.splice(i, 1);
if (Math.random() < (0.18 - level * 0.006)) {
const pTypes = ['paddle', 'speed', 'projectile', 'lava', 'bomb', 'growth', 'mushroom', 'multiball'];
const pType = pTypes[Math.floor(Math.random() * pTypes.length)];
powerups.push({
x: brick.x + brick.width / 2,
y: brick.y,
type: pType,
vy: 2.8,
size: 14
});
}
}
break;
}
}
}
for (let i = powerups.length - 1; i >= 0; i--) {
const p = powerups[i];
p.y += p.vy;
if (p.y > HEIGHT + 20) {
powerups.splice(i, 1);
continue;
}
if (p.y + 8 > paddle.y && p.y < paddle.y + paddle.height &&
p.x > paddle.x && p.x < paddle.x + paddle.width) {
applyPowerup(p.type);
playSound('powerup');
powerups.splice(i, 1);
score += 250;
updateHUD();
}
}
for (let i = projectiles.length - 1; i >= 0; i--) {
const proj = projectiles[i];
proj.y += proj.dy;
if (Math.random() < 0.6) {
createParticles(proj.x, proj.y + 6, 1, '#888888', 'smoke');
}
if (proj.y < 0) {
projectiles.splice(i, 1);
continue;
}
let hit = false;
for (let j = bricks.length - 1; j >= 0; j--) {
const b = bricks[j];
if (proj.x > b.x && proj.x < b.x + b.width &&
proj.y > b.y && proj.y < b.y + b.height) {
b.hp -= 1;
playSound('brick');
createParticles(proj.x, proj.y, 4, '#ffffff', 'fire');
if (b.hp <= 0) {
bricks.splice(j, 1);
score += 10 * b.maxHp;
if (Math.random() < 0.12) {
powerups.push({x: b.x + b.width/2, y: b.y, type: 'paddle', vy: 2.5});
}
}
updateHUD();
projectiles.splice(i, 1);
hit = true;
break;
}
}
if (hit) continue;
}
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.vy += p.gravity || 0.06;
p.life -= 1;
if (p.type === 'smoke') {
p.vx *= 0.98;
p.size *= 0.97;
}
if (p.life <= 0) {
particles.splice(i, 1);
}
}
if (bricks.length === 0 && gameState === STATES.PLAYING) {
gameState = STATES.LEVEL_COMPLETE;
stopBackgroundMusic();
score += 500;
createParticles(WIDTH/2, HEIGHT/2, 35, '#00ff00', 'jet');
playSound('level');
updateHUD();
}
for (const ball of balls) {
const spd = Math.sqrt(ball.dx * ball.dx + ball.dy * ball.dy);
if (spd > 8.2) {
ball.dx *= 8.2 / spd;
ball.dy *= 8.2 / spd;
}
if (spd < 3.0) {
ball.dx *= 3.1 / spd;
ball.dy *= 3.1 / spd;
}
}
}
function applyPowerup(type) {
switch (type) {
case 'paddle':
paddle.targetWidth = Math.min(210, paddle.targetWidth + 45);
createFloatingText(paddle.x + 30, paddle.y - 20, 'WIDE', '#ffff00');
break;
case 'speed':
for (const ball of balls) {
ball.dx *= 0.45;
ball.dy *= 0.45;
}
createFloatingText(paddle.x + 20, paddle.y - 20, 'SLOW', '#ff88ff');
break;
case 'projectile':
for (let k = 0; k < 8; k++) {
projectiles.push({
x: paddle.x + 15 + k * (paddle.width / 9),
y: paddle.y - 6,
dy: -14.5
});
}
createFloatingText(paddle.x + 10, paddle.y - 18, 'MISSILE', '#ffffff');
break;
case 'lava':
for (const ball of balls) {
ball.lava = 35 * 60; }
createFloatingText(paddle.x + 15, paddle.y - 20, 'LAVA', '#ff6600');
break;
case 'bomb':
for (const ball of balls) {
ball.bomb = 8;
}
createFloatingText(paddle.x + 15, paddle.y - 20, 'BOMB', '#222222');
break;
case 'growth':
for (const ball of balls) {
ball.radius = 11;
ball.growth = 35 * 60;
}
createFloatingText(paddle.x + 10, paddle.y - 18, 'GROW', '#ffffff');
break;
case 'mushroom':
lives++;
createFloatingText(paddle.x + paddle.width/2 - 20, paddle.y - 25, '+1UP', '#ffff00');
playSound('life');
break;
case 'multiball':
const mainBall = balls[0];
if (mainBall) {
for (let i = 0; i < 9; i++) {
const angle = (i - 4) * 0.28;
balls.push({
x: paddle.x + paddle.width / 2,
y: paddle.y - 15,
radius: 6.5,
dx: Math.sin(angle) * 3.8,
dy: -Math.abs(Math.cos(angle) * 3.8),
speed: 3.8
});
}
}
createFloatingText(paddle.x + 15, paddle.y - 22, 'MULTI', '#aa00ff');
break;
}
}
function draw() {
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.strokeStyle = '#001100';
ctx.lineWidth = 1;
for (let x = 0; x < WIDTH; x += 40) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, HEIGHT);
ctx.stroke();
}
for (const b of bricks) {
ctx.fillStyle = b.color;
ctx.fillRect(b.x, b.y, b.width, b.height);
ctx.strokeStyle = '#ffffff';
ctx.strokeRect(b.x, b.y, b.width, b.height);
if (b.hp > 1) {
ctx.fillStyle = '#000000';
ctx.font = 'bold 11px Courier New';
ctx.fillText(b.hp, b.x + b.width/2 - 4, b.y + b.height/2 + 4);
}
}
ctx.fillStyle = '#00aaff';
ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
ctx.fillStyle = '#0066aa';
ctx.fillRect(paddle.x, paddle.y, paddle.width, 5); ctx.strokeStyle = '#aaffff';
ctx.strokeRect(paddle.x, paddle.y, paddle.width, paddle.height);
if (projectiles.length > 0) {
ctx.fillStyle = '#ffaa00';
ctx.fillRect(paddle.x + 8, paddle.y - 3, 6, 5);
ctx.fillRect(paddle.x + paddle.width - 14, paddle.y - 3, 6, 5);
}
for (const ball of balls) {
let ballColor = '#ff3333';
if (ball.lava && ball.lava > 0) ballColor = '#ffaa00';
if (ball.bomb && ball.bomb > 0) ballColor = '#222222';
ctx.fillStyle = ballColor;
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = (ball.lava || ball.bomb) ? '#ffaa00' : '#ffffff';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.lineWidth = 1;
if (ball.lava && ball.lava > 0) {
ctx.fillStyle = '#ff6600';
ctx.fillRect(ball.x - 2, ball.y + 6, 4, 8);
}
}
for (const p of powerups) {
let col = '#ffff00';
if (p.type === 'speed') col = '#ff00ff';
if (p.type === 'projectile') col = '#ffffff';
if (p.type === 'lava') col = '#ff6600';
if (p.type === 'bomb') col = '#333333';
if (p.type === 'growth') col = '#aaffaa';
if (p.type === 'mushroom') col = '#ff3333';
if (p.type === 'multiball') col = '#aa00ff';
ctx.fillStyle = col;
ctx.fillRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size);
ctx.strokeStyle = '#fff';
ctx.strokeRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size);
ctx.fillStyle = '#000';
ctx.font = '9px Courier New';
ctx.fillText(p.type[0].toUpperCase(), p.x - 4, p.y + 3);
}
ctx.fillStyle = '#ffffff';
for (const proj of projectiles) {
ctx.fillRect(proj.x - 2, proj.y, 4, 11);
}
for (const p of particles) {
ctx.globalAlpha = Math.max(0.1, p.life / 55);
ctx.fillStyle = p.color;
if (p.type === 'text') {
ctx.font = 'bold 15px Courier New';
ctx.fillText(p.text, p.x, p.y);
} else {
ctx.fillRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size);
}
}
ctx.globalAlpha = 1.0;
ctx.fillStyle = '#00ff00';
ctx.font = 'bold 14px Courier New';
if (gameState === STATES.MENU) {
ctx.fillStyle = 'rgba(0,0,0,0.75)';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = '#00ff00';
ctx.font = 'bold 42px Courier New';
ctx.fillText('ATARI BREAKOUT', WIDTH/2 - 195, HEIGHT/2 - 60);
ctx.font = '18px Courier New';
ctx.fillText('PRESS SPACE OR CLICK TO START', WIDTH/2 - 165, HEIGHT/2 + 10);
ctx.font = '13px Courier New';
ctx.fillText('M = Mute Music', WIDTH/2 - 70, HEIGHT/2 + 55);
} else if (gameState === STATES.PAUSED) {
ctx.fillStyle = 'rgba(0,0,0,0.65)';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = '#ffff00';
ctx.font = 'bold 38px Courier New';
ctx.fillText('PAUSED', WIDTH/2 - 65, HEIGHT/2);
} else if (gameState === STATES.GAME_OVER) {
ctx.fillStyle = 'rgba(0,0,0,0.75)';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = '#ff3333';
ctx.font = 'bold 42px Courier New';
ctx.fillText('GAME OVER', WIDTH/2 - 115, HEIGHT/2 - 20);
ctx.fillStyle = '#00ff00';
ctx.font = '18px Courier New';
ctx.fillText('FINAL SCORE: ' + score, WIDTH/2 - 75, HEIGHT/2 + 25);
ctx.fillText('PRESS SPACE TO RESTART', WIDTH/2 - 125, HEIGHT/2 + 60);
} else if (gameState === STATES.LEVEL_COMPLETE) {
ctx.fillStyle = 'rgba(0,0,0,0.65)';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = '#00ff00';
ctx.font = 'bold 36px Courier New';
ctx.fillText('LEVEL ' + (level-1) + ' COMPLETE', WIDTH/2 - 145, HEIGHT/2);
ctx.font = '18px Courier New';
ctx.fillText('PRESS SPACE FOR NEXT LEVEL', WIDTH/2 - 145, HEIGHT/2 + 45);
}
if (gameState === STATES.PLAYING) {
ctx.fillStyle = '#003300';
ctx.font = '10px Courier New';
ctx.fillText('FPS:' + Math.floor(fps), WIDTH - 55, 18);
}
}
function gameLoop() {
const now = Date.now();
frameCount++;
if (now - lastTime > 1000) {
fps = frameCount;
frameCount = 0;
lastTime = now;
}
update();
draw();
updateHUD();
for (const ball of balls) {
if (ball.lava !== undefined && ball.lava > 0) ball.lava--;
if (ball.growth !== undefined && ball.growth > 0) {
ball.growth--;
if (ball.growth <= 0) ball.radius = 7.5;
}
if (ball.bomb !== undefined && ball.bomb > 0) ball.bomb--;
}
requestAnimationFrame(gameLoop);
}
function updateHUD() {
document.getElementById('score').innerText = 'SCORE: ' + score.toString().padStart(5, '0');
document.getElementById('lives').innerText = 'LIVES: ' + lives;
document.getElementById('level').innerText = 'LEVEL: ' + level;
}
function init() {
initAudio();
resizeCanvas();
setupTouchControls();
createBricks();
gameLoop();
updateHUD();
setTimeout(() => {
if (gameState === STATES.MENU && audioCtx) {
}
}, 600);
}
init();
</script>
</body>
</html>