Skip to content

Instantly share code, notes, and snippets.

@simonw
Created July 29, 2025 18:51
Show Gist options
  • Select an option

  • Save simonw/b61d161a8a969e4558c812a64dadbb45 to your computer and use it in GitHub Desktop.

Select an option

Save simonw/b61d161a8a969e4558c812a64dadbb45 to your computer and use it in GitHub Desktop.

https://chat.qwen.ai/?model=Qwen3-30B-A3B-2507

Write an HTML and JavaScript page implementing space invaders


Space Invaders Game

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>

How to Play:

  1. Click the "START GAME" button to begin
  2. Use the LEFT and RIGHT arrow keys to move your spaceship
  3. Press SPACEBAR to shoot at the invaders
  4. Destroy all invaders before they reach the bottom of the screen
  5. You have 3 lives - each time an invader bullet hits you, you lose a life
  6. When you lose all lives or the invaders reach the bottom, the game ends
  7. Click "PLAY AGAIN" to restart

Features:

  • 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment