<
<
<
<Space Invaders
<
body {
margin: 0;
padding: 0;
background-color: #000;
color: #fff;
font-family: 'Courier New', Courier, monospace;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
user-select: none;
}
#game-container {
position: relative;
box-shadow: 0 0 20px rgba(0, 255, 0, 0.2);
border: 2px solid #333;
}
canvas {
display: block;
background-color: #000;
}
#ui {
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 10px 20px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
font-size: 24px;
pointer-events: none;
text-shadow: 2px 2px #000;
z-index: 5;
}
#overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
z-index: 10;
}
h1 {
font-size: 48px;
color: #0f0;
margin-bottom: 20px;
}
p {
font-size: 18px;
margin-bottom: 30px;
line-height: 1.5;
}
button {
padding: 15px 30px;
font-size: 20px;
font-family: 'Courier New', Courier, monospace;
background: #0f0;
color: #000;
border: none;
cursor: pointer;
font-weight: bold;
transition: background 0.2s;
}
button:hover {
background: #0a0;
}
.hidden {
display: none !important;
}
<
<
SCORE: <0
LIVES: <3
<
<
<SPACE INVADERS
<Use LEFT/RIGHT arrows to move<Press SPACE to shoot
<START GAME
<
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const livesEl = document.getElementById('lives');
const overlay = document.getElementById('overlay');
const overlayTitle = document.getElementById('overlay-title');
const overlayDesc = document.getElementById('overlay-desc');
const startBtn = document.getElementById('start-btn');
// Game Constants
canvas.width = 800;
canvas.height = 600;
const PLAYER_WIDTH = 40;
const PLAYER_HEIGHT = 20;
const ALIEN_ROWS = 5;
const ALIEN_COLS = 11;
const ALIEN_SIZE = 30;
const ALIEN_PADDING = 15;
const BULLET_SPEED = 7;
const ALIEN_BULLET_SPEED = 4;
const BUNKER_BLOCK_SIZE = 5;
// Game State
let state = 'START';
let score = 0;
let lives = 3;
let keys = {};
let player = { x: canvas.width / 2 - PLAYER_WIDTH / 2, y: canvas.height - 40 };
let aliens = [];
let alienDirection = 1;
let alienMoveTimer = 0;
let alienMoveInterval = 60;
let bullets = [];
let bunkers = [];
let lastShotTime = 0;
window.addEventListener('keydown', e => keys[e.code] = true);
window.addEventListener('keyup', e => keys[e.code] = false);
function initGame() {
score = 0;
lives = 3;
player.x = canvas.width / 2 - PLAYER_WIDTH / 2;
bullets = [];
aliens = [];
bunkers = [];
alienDirection = 1;
alienMoveInterval = 60;
for (let row = 0; row << AL ALIEN_ROWS; row++) {
for (let col = 0; col << AL ALIEN_COLS; col++) {
aliens.push({
x: col * (ALIEN_SIZE + ALIEN_PADDING) + 50,
y: row * (ALIEN_SIZE + ALIEN_PADDING) + 80,
width: ALIEN_SIZE,
height: ALIEN_SIZE,
type: row
});
}
}
const bunkerCount = 4;
const bunkerWidth = 80;
const bunkerHeight = 60;
const spacing = (canvas.width - (bunkerCount * bunkerWidth)) / (bunkerCount + 1);
for (let i = 0; i << bunker bunkerCount; i++) {
const bx = spacing + i * (bunkerWidth + spacing);
const by = canvas.height - 120;
for (let row = 0; row << bunker bunkerHeight / BUNKER_BLOCK_SIZE; row++) {
for (let col = 0; col << bunker bunkerWidth / BUNKER_BLOCK_SIZE; col++) {
if (!(row === bunkerHeight / BUNKER_BLOCK_SIZE - 1 && col > 10 && col << 20)) {
bunkers.push({
x: bx + col * BUNKER_BLOCK_SIZE,
y: by + row * BUNKER_BLOCK_SIZE,
w: BUNKER_BLOCK_SIZE,
h: BUNKER_BLOCK_SIZE
});
}
}
}
}
scoreEl.innerText = score;
livesEl.innerText = lives;
}
function drawPlayer() {
ctx.fillStyle = '#0f0';
ctx.fillRect(player.x, player.y + 5, PLAYER_WIDTH, PLAYER_HEIGHT - 5);
ctx.fillRect(player.x + PLAYER_WIDTH / 2 - 3, player.y, 6, 5);
}
function drawAliens() {
aliens.forEach(alien => {
ctx.fillStyle = alien.type === 0 ? '#f0f' : (alien.type >= 3 ? '#0ff' : '#ff0');
if (alien.type === 0) {
ctx.fillRect(alien.x + 5, alien.y, 20, 20);
ctx.fillRect(alien.x, alien.y + 10, 5, 10);
ctx.fillRect(alien.x + 25, alien.y + 10, 5, 10);
ctx.fillRect(alien.x + 5, alien.y + 20, 5, 5);
ctx.fillRect(alien.x + 20, alien.y + 20, 5, 5);
} else if (alien.type >= 3) {
ctx.fillRect(alien.x + 5, alien.y + 5, 20, 15);
ctx.fillRect(alien.x, alien.y, 5, 5);
ctx.fillRect(alien.x + 25, alien.y, 5, 5);
ctx.fillRect(alien.x, alien.y + 20, 5, 5);
ctx.fillRect(alien.x + 25, alien.y + 20, 5, 5);
} else {
ctx.fillRect(alien.x + 5, alien.y, 20, 20);
ctx.fillRect(alien.x, alien.y + 5, 5, 10);
ctx.fillRect(alien.x + 25, alien.y + 5, 5, 10);
}
});
}
function drawBunkers() {
ctx.fillStyle = '#0f0';
bunkers.forEach(b => {
ctx.fillRect(b.x, b.y, b.w, b.h);
});
}
function drawBullets() {
bullets.forEach(b => {
ctx.fillStyle = b.owner === 'player' ? '#fff' : '#f00';
ctx.fillRect(b.x, b.y, 3, 10);
});
}
function update() {
if (state !== 'PLAYING') return;
if (keys['ArrowLeft'] && player.x > 0) player.x -= 5;
if (keys['ArrowRight'] && player.x << canvas canvas.width - PLAYER_WIDTH) player.x += 5;
const now = Date.now();
if (keys['Space'] && now - lastShotTime > 500) {
bullets.push({
x: player.x + PLAYER_WIDTH / 2 - 1.5,
y: player.y,
dy: -BULLET_SPEED,
owner: 'player'
});
lastShotTime = now;
}
alienMoveTimer++;
if (alienMoveTimer >= alienMoveInterval) {
let hitEdge = false;
aliens.forEach(a => {
a.x += 10 * alienDirection;
if (a.x + ALIEN_SIZE > canvas.width - 10 || a.x << 10) {
hitEdge = true;
}
});
if (hitEdge) {
alienDirection *= -1;
aliens.forEach(a => {
a.y += 20;
});
alienMoveInterval = Math.max(10, 60 - ((ALIEN_ROWS * ALIEN_COLS - aliens.length) * 2));
}
alienMoveTimer = 0;
}
if (Math.random() << 0.02 && aliens.length > 0) {
const shooter = aliens[Math.floor(Math.random() * aliens.length)];
bullets.push({
x: shooter.x + ALIEN_SIZE / 2,
y: shooter.y + ALIEN_SIZE,
dy: ALIEN_BULLET_SPEED,
owner: 'alien'
});
}
for (let i = bullets.length - 1; i >= 0; i--) {
const b = bullets[i];
b.y += b.dy;
if (b.y << 0 || b.y > canvas.height) {
bullets.splice(i, 1);
continue;
}
if (b.owner === 'player') {
for (let j = aliens.length - 1; j >= 0; j--) {
const a = aliens[j];
if (b.x > a.x && b.x << a a.x + a.width && b.y > a.y && b.y << a a.y + a.height) {
aliens.splice(j, 1);
bullets.splice(i, 1);
score += 10;
scoreEl.innerText = score;
break;
}
}
} else if (b.owner === 'alien') {
if (b.x > player.x && b.x << player player.x + PLAYER_WIDTH && b.y > player.y && b.y << player player.y + PLAYER_HEIGHT) {
bullets.splice(i, 1);
lives--;
livesEl.innerText = lives;
if (lives <= 0) gameOver(false);
continue;
}
}
if (bullets[i]) {
const bCurr = bullets[i];
for (let k = bunkers.length - 1; k >= 0; k--) {
const bun = bunkers[k];
if (bCurr.x > bun.x && bCurr.x << bun bun.x + bun.w && bCurr.y > bun.y && bCurr.y << bun bun.y + bun.h) {
bunkers.splice(k, 1);
bullets.splice(i, 1);
break;
}
}
}
}
if (aliens.length === 0) gameOver(true);
aliens.forEach(a => {
if (a.y + a.height >= player.y) gameOver(false);
});
}
function gameOver(win) {
state = win ? 'WIN' : 'GAMEOVER';
overlay.classList.remove('hidden');
overlayTitle.innerText = win ? 'YOU WON!' : 'GAME OVER';
overlayTitle.style.color = win ? '#0f0' : '#f00';
overlayDesc.innerHTML = `Final Score: ${score}`;
startBtn.innerText = 'TRY AGAIN';
}
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
update();
drawBunkers();
drawPlayer();
drawAliens();
drawBullets();
requestAnimationFrame(loop);
}
startBtn.addEventListener('click', () => {
overlay.classList.add('hidden');
initGame();
state = 'PLAYING';
});
loop();