https://chat.qwen.ai/?model=Qwen3-30B-A3B-2507
Write an HTML and JavaScript page implementing space invaders
Here's a complete HTML and JavaScript implementation of the classic Space Invaders game:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Space Invaders</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
color: white;
font-family: 'Courier New', monospace;
overflow: hidden;
}
#gameContainer {
position: relative;
width: 600px;
height: 700px;
border: 2px solid #333;
overflow: hidden;
}
#gameCanvas {
background-color: #000;
display: block;
}
#scoreDisplay {
position: absolute;
top: 10px;
left: 10px;
font-size: 18px;
color: #0f0;
}
#livesDisplay {
position: absolute;
top: 10px;
right: 10px;
font-size: 18px;
color: #f00;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
#startScreen h1 {
font-size: 48px;
color: #0f0;
margin-bottom: 20px;
text-shadow: 0 0 10px #0f0;
}
#startButton {
padding: 15px 30px;
font-size: 24px;
background-color: #0f0;
color: #000;
border: none;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 0 10px #0f0;
transition: all 0.3s;
}
#startButton:hover {
background-color: #0c0;
transform: scale(1.05);
}
#gameOverScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
#gameOverScreen h2 {
font-size: 48px;
color: #f00;
margin-bottom: 20px;
text-shadow: 0 0 10px #f00;
}
#restartButton {
padding: 15px 30px;
font-size: 24px;
background-color: #0f0;
color: #000;
border: none;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 0 10px #0f0;
transition: all 0.3s;
}
#restartButton:hover {
background-color: #0c0;
transform: scale(1.05);
}
#instructions {
margin-top: 20px;
font-size: 16px;
color: #ccc;
text-align: center;
max-width: 400px;
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" width="600" height="700"></canvas>
<div id="scoreDisplay">Score: 0</div>
<div id="livesDisplay">Lives: 3</div>
<div id="startScreen">
<h1>SPACE INVADERS</h1>
<button id="startButton">START GAME</button>
<div id="instructions">
Use LEFT and RIGHT arrow keys to move<br>
Press SPACEBAR to shoot<br>
Destroy all invaders before they reach the bottom!
</div>
</div>
<div id="gameOverScreen">
<h2>GAME OVER</h2>
<p id="finalScore">Score: 0</p>
<button id="restartButton">PLAY AGAIN</button>
</div>
</div>
<script>
// Get canvas and context
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game variables
let player;
let bullets = [];
let invaders = [];
let explosionParticles = [];
let score = 0;
let lives = 3;
let gameActive = false;
let gameOver = false;
let invaderDirection = 1; // 1 for right, -1 for left
let invaderSpeed = 1;
let invaderDropDistance = 20;
let lastInvaderShot = 0;
let invaderShootInterval = 2000; // milliseconds
// Player properties
const playerWidth = 50;
const playerHeight = 30;
// Bullet properties
const bulletWidth = 4;
const bulletHeight = 15;
const bulletSpeed = 7;
// Invader properties
const invaderWidth = 40;
const invaderHeight = 30;
const invaderRows = 5;
const invadersPerRow = 10;
const invaderPadding = 20;
// Initialize player
function initPlayer() {
player = {
x: canvas.width / 2 - playerWidth / 2,
y: canvas.height - playerHeight - 10,
width: playerWidth,
height: playerHeight,
speed: 5
};
}
// Initialize invaders
function initInvaders() {
invaders = [];
for (let row = 0; row < invaderRows; row++) {
for (let col = 0; col < invadersPerRow; col++) {
invaders.push({
x: col * (invaderWidth + invaderPadding) + invaderPadding,
y: row * (invaderHeight + invaderPadding) + 50,
width: invaderWidth,
height: invaderHeight,
alive: true,
type: row % 3 // Different types based on row
});
}
}
}
// Create explosion particles
function createExplosion(x, y) {
for (let i = 0; i < 10; i++) {
explosionParticles.push({
x: x,
y: y,
vx: Math.random() * 6 - 3,
vy: Math.random() * 6 - 3,
life: 30,
maxLife: 30,
size: Math.random() * 5 + 2
});
}
}
// Draw player
function drawPlayer() {
ctx.fillStyle = '#0f0';
ctx.fillRect(player.x, player.y, player.width, player.height);
// Draw cannon
ctx.fillStyle = '#fff';
ctx.fillRect(player.x + player.width / 2 - 2, player.y - 5, 4, 5);
}
// Draw bullets
function drawBullets() {
ctx.fillStyle = '#ff0';
bullets.forEach(bullet => {
ctx.fillRect(bullet.x, bullet.y, bulletWidth, bulletHeight);
});
}
// Draw invaders
function drawInvaders() {
invaders.forEach(invader => {
if (!invader.alive) return;
// Different colors based on type
let color;
switch (invader.type) {
case 0:
color = '#f00';
break;
case 1:
color = '#f90';
break;
case 2:
color = '#ff0';
break;
default:
color = '#f00';
}
ctx.fillStyle = color;
ctx.fillRect(invader.x, invader.y, invader.width, invader.height);
// Add eyes to invaders
ctx.fillStyle = '#000';
ctx.fillRect(invader.x + 10, invader.y + 8, 5, 5);
ctx.fillRect(invader.x + 25, invader.y + 8, 5, 5);
// Add mouth
ctx.fillStyle = '#000';
ctx.fillRect(invader.x + 15, invader.y + 20, 10, 3);
});
}
// Draw explosion particles
function drawExplosionParticles() {
explosionParticles.forEach(particle => {
ctx.fillStyle = `rgba(255, 255, 0, ${particle.life / particle.maxLife})`;
ctx.fillRect(particle.x, particle.y, particle.size, particle.size);
});
}
// Update bullets
function updateBullets() {
bullets.forEach((bullet, index) => {
bullet.y -= bulletSpeed;
// Remove bullets that go off screen
if (bullet.y < 0) {
bullets.splice(index, 1);
}
});
}
// Update explosion particles
function updateExplosionParticles() {
explosionParticles.forEach((particle, index) => {
particle.x += particle.vx;
particle.y += particle.vy;
particle.life--;
if (particle.life <= 0) {
explosionParticles.splice(index, 1);
}
});
}
// Update invaders
function updateInvaders() {
const now = Date.now();
// Check if it's time for an invader to shoot
if (now - lastInvaderShot > invaderShootInterval && invaders.some(i => i.alive)) {
const aliveInvaders = invaders.filter(i => i.alive);
if (aliveInvaders.length > 0) {
const shooter = aliveInvaders[Math.floor(Math.random() * aliveInvaders.length)];
bullets.push({
x: shooter.x + shooter.width / 2 - bulletWidth / 2,
y: shooter.y + shooter.height,
width: bulletWidth,
height: bulletHeight
});
lastInvaderShot = now;
}
}
// Check if any invaders have reached the bottom
for (const invader of invaders) {
if (invader.alive && invader.y + invader.height >= player.y) {
gameOver = true;
return;
}
}
// Check if invaders have hit the sides
let hitSide = false;
for (const invader of invaders) {
if (invader.alive) {
if (invader.x <= 0 || invader.x + invader.width >= canvas.width) {
hitSide = true;
break;
}
}
}
// Move invaders
if (hitSide) {
invaderDirection *= -1;
// Drop down
for (const invader of invaders) {
if (invader.alive) {
invader.y += invaderDropDistance;
}
}
} else {
// Move horizontally
for (const invader of invaders) {
if (invader.alive) {
invader.x += invaderSpeed * invaderDirection;
}
}
}
// Increase difficulty over time
if (invaders.every(i => !i.alive)) {
// All invaders destroyed, increase level
invaderSpeed += 0.2;
initInvaders();
}
}
// Check collisions
function checkCollisions() {
// Check player-bullet collisions
bullets.forEach((bullet, bulletIndex) => {
// Check collision with invaders
for (const invader of invaders) {
if (invader.alive &&
bullet.x < invader.x + invader.width &&
bullet.x + bulletWidth > invader.x &&
bullet.y < invader.y + invader.height &&
bullet.y + bulletHeight > invader.y) {
// Collision detected
invader.alive = false;
createExplosion(invader.x + invader.width / 2, invader.y + invader.height / 2);
bullets.splice(bulletIndex, 1);
score += 10 * (invader.type + 1); // More points for higher rows
updateScoreDisplay();
return;
}
}
// Check collision with player
if (bullet.x < player.x + player.width &&
bullet.x + bulletWidth > player.x &&
bullet.y < player.y + player.height &&
bullet.y + bulletHeight > player.y) {
// Player hit
lives--;
updateLivesDisplay();
bullets.splice(bulletIndex, 1);
if (lives <= 0) {
gameOver = true;
}
return;
}
});
}
// Update score display
function updateScoreDisplay() {
document.getElementById('scoreDisplay').textContent = `Score: ${score}`;
}
// Update lives display
function updateLivesDisplay() {
document.getElementById('livesDisplay').textContent = `Lives: ${lives}`;
}
// Draw everything
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw stars in the background
ctx.fillStyle = '#fff';
for (let i = 0; i < 100; i++) {
const x = (i * 17) % canvas.width;
const y = (i * 23) % canvas.height;
ctx.fillRect(x, y, 1, 1);
}
// Draw game elements
drawPlayer();
drawBullets();
drawInvaders();
drawExplosionParticles();
// Update game state
if (gameActive && !gameOver) {
updateBullets();
updateInvaders();
updateExplosionParticles();
checkCollisions();
}
}
// Game loop
function gameLoop() {
draw();
requestAnimationFrame(gameLoop);
}
// Start game
function startGame() {
// Reset game state
score = 0;
lives = 3;
invaderSpeed = 1;
invaderDirection = 1;
gameOver = false;
gameActive = true;
// Update displays
updateScoreDisplay();
updateLivesDisplay();
// Initialize game objects
initPlayer();
initInvaders();
bullets = [];
explosionParticles = [];
// Hide start screen
document.getElementById('startScreen').style.display = 'none';
document.getElementById('gameOverScreen').style.display = 'none';
}
// Restart game
function restartGame() {
startGame();
}
// Event listeners
document.getElementById('startButton').addEventListener('click', startGame);
document.getElementById('restartButton').addEventListener('click', restartGame);
// Keyboard controls
const keys = {};
window.addEventListener('keydown', (e) => {
keys[e.key] = true;
// Spacebar to shoot
if (e.key === ' ' && gameActive && !gameOver) {
bullets.push({
x: player.x + player.width / 2 - bulletWidth / 2,
y: player.y,
width: bulletWidth,
height: bulletHeight
});
e.preventDefault();
}
});
window.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
// Handle player movement
function handlePlayerMovement() {
if (!gameActive || gameOver) return;
// Move player left/right
if (keys['ArrowLeft'] && player.x > 0) {
player.x -= player.speed;
}
if (keys['ArrowRight'] && player.x + player.width < canvas.width) {
player.x += player.speed;
}
}
// Main game loop with movement handling
function mainLoop() {
handlePlayerMovement();
draw();
requestAnimationFrame(mainLoop);
}
// Initialize the game
initPlayer();
initInvaders();
// Start the game loop
mainLoop();
// Show start screen initially
document.getElementById('startScreen').style.display = 'flex';
</script>
</body>
</html>
- Click the "START GAME" button to begin
- Use the LEFT and RIGHT arrow keys to move your spaceship
- Press SPACEBAR to shoot at the invaders
- Destroy all invaders before they reach the bottom of the screen
- You have 3 lives - each time an invader bullet hits you, you lose a life
- When you lose all lives or the invaders reach the bottom, the game ends
- Click "PLAY AGAIN" to restart
- Classic Space Invaders gameplay
- Score system with different point values based on invader type
- Lives system
- Increasing difficulty as you progress
- Explosion animations when enemies are destroyed
- Starry background
- Responsive design with start and game over screens
- Multiple invader types with different colors
Enjoy playing this retro-inspired Space Invaders game!