GAMES IN HTML5

JavaScript Top-Down Shooter — Canvas 2D Bullet System Tutorial

Build a top-down shooter in vanilla JavaScript — rotating player ship, auto-firing bullet stream, enemy waves that approach from all edges, hit detection, and score. Zero libraries, zero assets, pure Canvas 2D. Move with WASD or arrows, and watch the bullet system manage itself automatically for the video demo.

What you will build

A Canvas top-down shooter with a player ship that rotates to face the nearest enemy, auto-fires bullets, spawns enemies from random edge positions, and removes them on hit. The score increments with each kill. The entire game loop — physics, bullets, enemies, collision — is under 90 lines.

LIVE GAME DEMO

Top-Down Shooter — JavaScript Canvas

WASD / Arrows to move — auto-fires at enemies

How the top-down shooter bullet system works

Bullets are objects stored in a flat array: { x, y, vx, vy, life }. Each frame, velocity is added to position, life decrements, and off-screen or expired bullets are spliced out. Collision is distance-based: Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y) < hitRadius. Simpler than AABB for circular ships, and fast enough for hundreds of simultaneous bullets at 60 fps.

Enemy AI is three lines: compute the angle from enemy to player, multiply by a speed scalar, add to position. This "seek" behavior is the foundation of every homing enemy in top-down games. For variety, add a noise offset to the angle: angle += Math.sin(time * 0.05) * 0.4 and enemies weave instead of charging straight.

shooter.jsKEY IDEA
// Spawn bullet in aim direction
function fireBullet() {
  bullets.push({
    x: player.x, y: player.y,
    vx: Math.cos(player.angle) * BULLET_SPEED,
    vy: Math.sin(player.angle) * BULLET_SPEED,
    life: 60
  });
}

// Enemy seek behavior
const angle = Math.atan2(player.y - e.y, player.x - e.x);
e.x += Math.cos(angle) * ENEMY_SPEED;
e.y += Math.sin(angle) * ENEMY_SPEED;

The entire bullet system is array push/splice + two Math functions. No physics engine, no library — just a loop and trigonometry.

Scaling up to a real game

Add a weapon system: store bullet properties (speed, spread, damage) in a config object and switch between them on pickup. Add spread by randomizing the angle slightly: angle += (Math.random() - 0.5) * spreadFactor. Add boss enemies by scaling the HP and hitRadius. Add screen shake by offsetting the ctx.save()/translate() by a decaying random amount when a hit registers.

Optimization for many bullets

For bullet-hell scenarios (200+ bullets), avoid pushing/splicing the array every frame. Instead, maintain a fixed-size pool: pre-allocate 300 bullet objects and mark them as active/inactive with a flag. Reactivate inactive bullets instead of allocating new ones. This eliminates garbage collection pauses at peak fire rates.

ADVERTISEMENT
ADVERTISEMENT

No comments:

Post a Comment

Search the blog

Blog archive

Latest in English