https://huggingface.co/lmstudio-community/Qwen3-30B-A3B-Instruct-2507-MLX-8bit in LM Studio.
Write an HTML and JavaScript page implementing space invaders
<!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;
font-family: 'Courier New', monospace;
overflow: hidden;
}
#game-container {
position: relative;
width: 600px;
height: 700px;
border: 2px solid #333;
overflow: hidden;
}
#game-canvas {
background-color: #000;
display: block;
}
.score-display {
position: absolute;
top: 10px;
left: 10px;
color: #0f0;
font-size: 20px;
font-weight: bold;
text-shadow: 0 0 5px #0f0;
}
.lives-display {
position: absolute;
top: 10px;
right: 10px;
color: #f00;
font-size: 20px;
font-weight: bold;
text-shadow: 0 0 5px #f00;
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f00;
font-size: 48px;
font-weight: bold;
text-shadow: 0 0 10px #f00;
display: none;
}
.restart-btn {
position: absolute;
top: 60%;
left: 50%;
transform: translate(-50%, -50%);
padding: 10px 20px;
background-color: #0f0;
color: #000;
border: none;
border-radius: 5px;
font-size: 20px;
cursor: pointer;
display: none;
}
.restart-btn:hover {
background-color: #0c0;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="game-canvas" width="600" height="700"></canvas>
<div id="score" class="score-display">Score: 0</div>
<div id="lives" class="lives-display">Lives: 3</div>
<div id="game-over" class="game-over">GAME OVER</div>
<button id="restart-btn" class="restart-btn">Restart Game</button>
</div>
<script>
// Get canvas and context
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
// Game elements
let player;
let bullets = [];
let enemies = [];
let enemyBullets = [];
let particles = [];
// Game state
const gameState = {
score: 0,
lives: 3,
gameOver: false,
level: 1,
enemySpeed: 0.8
};
// Player class
class Player {
constructor() {
this.width = 40;
this.height = 30;
this.x = canvas.width / 2 - this.width / 2;
this.y = canvas.height - this.height - 20;
this.speed = 5;
this.color = '#0f0';
this.shootCooldown = 0;
}
draw() {
// Draw player ship
ctx.fillStyle = this.color;
// Ship body
ctx.fillRect(this.x, this.y, this.width, this.height);
// Ship cockpit
ctx.fillStyle = '#0ff';
ctx.fillRect(this.x + this.width / 2 - 5, this.y + 5, 10, 8);
// Ship wings
ctx.fillStyle = '#0f0';
ctx.fillRect(this.x - 5, this.y + 10, 5, 10);
ctx.fillRect(this.x + this.width, this.y + 10, 5, 10);
}
move(direction) {
if (direction === 'left' && this.x > 0) {
this.x -= this.speed;
} else if (direction === 'right' && this.x < canvas.width - this.width) {
this.x += this.speed;
}
}
shoot() {
if (this.shootCooldown <= 0) {
bullets.push({
x: this.x + this.width / 2 - 2,
y: this.y,
width: 4,
height: 10,
color: '#fff',
speed: -7
});
this.shootCooldown = 15;
}
}
update() {
if (this.shootCooldown > 0) {
this.shootCooldown--;
}
}
getBounds() {
return {
left: this.x,
right: this.x + this.width,
top: this.y,
bottom: this.y + this.height
};
}
}
// Enemy class
class Enemy {
constructor(x, y, type = 1) {
this.width = 30;
this.height = 25;
this.x = x;
this.y = y;
this.type = type; // 1: basic, 2: fast, 3: armored
this.speed = gameState.enemySpeed * (type === 1 ? 1 : type === 2 ? 1.5 : 0.8);
this.direction = 1;
this.shootCooldown = Math.random() * 200 + 50; // Random shooting
this.color = type === 1 ? '#f00' : type === 2 ? '#ff0' : '#8c1a5d';
}
draw() {
// Draw enemy ship
ctx.fillStyle = this.color;
// Ship body
ctx.fillRect(this.x, this.y, this.width, this.height);
// Enemy eyes
ctx.fillStyle = '#fff';
ctx.fillRect(this.x + 6, this.y + 5, 4, 4);
ctx.fillRect(this.x + this.width - 10, this.y + 5, 4, 4);
// Enemy antennae
ctx.fillStyle = '#f00';
if (this.type === 1) {
// Basic enemy
ctx.fillRect(this.x + this.width / 2 - 1, this.y - 5, 2, 5);
} else if (this.type === 2) {
// Fast enemy
ctx.fillRect(this.x + this.width / 4 - 1, this.y - 5, 2, 5);
ctx.fillRect(this.x + (3 * this.width) / 4 - 1, this.y - 5, 2, 5);
} else if (this.type === 3) {
// Armored enemy
ctx.fillRect(this.x + this.width / 2 - 1, this.y - 8, 2, 8);
ctx.fillStyle = '#0f0';
ctx.fillRect(this.x + this.width / 2 - 1, this.y - 6, 2, 3);
}
}
update() {
// Move enemy
this.x += this.speed * this.direction;
// Change direction on edge collision
if (this.x <= 0 || this.x + this.width >= canvas.width) {
this.direction *= -1;
}
// Check for shooting
if (this.shootCooldown <= 0) {
enemyBullets.push({
x: this.x + this.width / 2 - 1,
y: this.y + this.height,
width: 2,
height: 8,
color: '#f00',
speed: 3
});
this.shootCooldown = Math.random() * 200 + 150;
} else {
this.shootCooldown--;
}
}
getBounds() {
return {
left: this.x,
right: this.x + this.width,
top: this.y,
bottom: this.y + this.height
};
}
}
// Bullet class
class Bullet {
constructor(x, y, width, height, color, speed) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.speed = speed;
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
update() {
this.y += this.speed;
}
getBounds() {
return {
left: this.x,
right: this.x + this.width,
top: this.y,
bottom: this.y + this.height
};
}
}
// Particle class for explosions
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = Math.random() * 5 + 2;
this.speedX = Math.random() * 6 - 3;
this.speedY = Math.random() * 6 - 3;
this.color = `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 1)`;
this.life = 30;
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
update() {
this.x += this.speedX;
this.y += this.speedY;
this.size -= 0.1;
this.life--;
}
}
// Initialize game
function initGame() {
player = new Player();
bullets = [];
enemies = [];
enemyBullets = [];
particles = [];
gameState.score = 0;
gameState.lives = 3;
gameState.gameOver = false;
gameState.level = 1;
gameState.enemySpeed = 0.8;
// Create enemies
createEnemies();
// Update UI
updateUI();
}
function createEnemies() {
const rows = Math.min(4, 5 + gameState.level);
const cols = Math.min(8, 10 + gameState.level * 2);
for (let row = 0; row < rows; row++) {
const y = 50 + row * (30 + 10);
for (let col = 0; col < cols; col++) {
const x = 50 + col * (30 + 15);
// Determine enemy type based on row
let type = 1;
if (row === 0) {
// Top row - fast enemies
type = 2;
} else if (row >= rows - 1) {
// Bottom row - armored enemies
type = 3;
}
enemies.push(new Enemy(x, y, type));
}
}
}
// Update UI elements
function updateUI() {
document.getElementById('score').textContent = `Score: ${gameState.score}`;
document.getElementById('lives').textContent = `Lives: ${gameState.lives}`;
// Update game over screen
const gameOverScreen = document.getElementById('game-over');
const restartBtn = document.getElementById('restart-btn');
if (gameState.gameOver) {
gameOverScreen.style.display = 'block';
restartBtn.style.display = 'block';
} else {
gameOverScreen.style.display = 'none';
restartBtn.style.display = 'none';
}
}
// Check collisions
function checkCollisions() {
// Player bullets vs enemies
for (let i = bullets.length - 1; i >= 0; i--) {
const bullet = bullets[i];
for (let j = enemies.length - 1; j >= 0; j--) {
const enemy = enemies[j];
if (collides(bullet.getBounds(), enemy.getBounds())) {
// Remove bullet and enemy
bullets.splice(i, 1);
// Add explosion particles
for (let k = 0; k < 10; k++) {
particles.push(new Particle(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2));
}
// Increase score based on enemy type
if (enemy.type === 1) {
gameState.score += 10;
} else if (enemy.type === 2) {
gameState.score += 20;
} else {
gameState.score += 30;
}
// Remove enemy
enemies.splice(j, 1);
break;
}
}
}
// Enemy bullets vs player
for (let i = enemyBullets.length - 1; i >= 0; i--) {
const bullet = enemyBullets[i];
if (collides(bullet.getBounds(), player.getBounds())) {
// Remove bullet
enemyBullets.splice(i, 1);
// Take damage
gameState.lives--;
updateUI();
// Check game over
if (gameState.lives <= 0) {
endGame();
}
// Add explosion particles
for (let k = 0; k < 10; k++) {
particles.push(new Particle(player.x + player.width / 2, player.y + player.height / 2));
}
break;
}
}
// Enemy ships hit player
for (let i = enemies.length - 1; i >= 0; i--) {
const enemy = enemies[i];
if (collides(enemy.getBounds(), player.getBounds())) {
// Player hit by enemy
gameState.lives--;
updateUI();
if (gameState.lives <= 0) {
endGame();
}
// Add explosion particles
for (let k = 0; k < 15; k++) {
particles.push(new Particle(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2));
}
// Remove enemy
enemies.splice(i, 1);
break;
}
}
// Check if all enemies are defeated
if (enemies.length === 0) {
gameState.level++;
gameState.enemySpeed += 0.2;
// Create new wave
createEnemies();
}
}
function collides(rect1, rect2) {
return (
rect1.left < rect2.right &&
rect1.right > rect2.left &&
rect1.top < rect2.bottom &&
rect1.bottom > rect2.top
);
}
// Game over function
function endGame() {
gameState.gameOver = true;
updateUI();
// Show game over screen
const gameOverScreen = document.getElementById('game-over');
gameOverScreen.style.display = 'block';
}
// Game loop
function gameLoop() {
if (gameState.gameOver) return;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update player
player.update();
// Update bullets
for (let i = bullets.length - 1; i >= 0; i--) {
const bullet = bullets[i];
bullet.update();
if (bullet.y < 0 || bullet.y > canvas.height) {
bullets.splice(i, 1);
}
}
// Update enemy bullets
for (let i = enemyBullets.length - 1; i >= 0; i--) {
const bullet = enemyBullets[i];
bullet.update();
if (bullet.y < 0 || bullet.y > canvas.height) {
enemyBullets.splice(i, 1);
}
}
// Update enemies
for (let i = enemies.length - 1; i >= 0; i--) {
const enemy = enemies[i];
enemy.update();
}
// Update particles
for (let i = particles.length - 1; i >= 0; i--) {
const particle = particles[i];
particle.update();
if (particle.life <= 0 || particle.size <= 0) {
particles.splice(i, 1);
}
}
// Check collisions
checkCollisions();
// Draw all elements
player.draw();
bullets.forEach(bullet => bullet.draw());
enemies.forEach(enemy => enemy.draw());
enemyBullets.forEach(bullet => bullet.draw());
particles.forEach(particle => particle.draw());
// Request next frame
requestAnimationFrame(gameLoop);
}
// Event listeners
document.addEventListener('keydown', (e) => {
if (gameState.gameOver) return;
switch(e.key) {
case 'ArrowLeft':
player.move('left');
break;
case 'ArrowRight':
player.move('right');
break;
case ' ':
player.shoot();
e.preventDefault();
break;
}
});
// Restart game button
document.getElementById('restart-btn').addEventListener('click', () => {
initGame();
});
// Start the game
initGame();
gameLoop();
</script>
</body>
</html>This HTML and JavaScript implementation of Space Invaders includes the following features:
- A player-controlled spaceship that moves left/right and shoots
- Enemy ships in multiple rows with different types (basic, fast, armored)
- Random enemy shooting
- Collision detection for bullets and ships
- Score tracking with different point values based on enemy type
- Lives system (3 lives)
- Level progression with increasing difficulty
- Particle explosions when enemies are destroyed or player is hit
- Game over screen with restart button
- Responsive UI elements
Controls:
- Arrow keys: Move left and right
- Spacebar: Shoot bullets
- Restart button: Play again after game over
The game features a retro arcade feel with green player ship, red enemy ships, and animated explosions. The difficulty increases as you progress through levels with faster enemies and more complex formations.