Why Vanilla JS for Games?
Frameworks are great for web apps. But for browser games, vanilla JavaScript + Canvas 2D gives you:
- 60fps performance without framework overhead
- Zero dependencies — ship a single HTML file
- Full control over the render loop
- Instant deployment — drag and drop to any hosting
What We’re Building
A Vampire Survivors-inspired auto-shooter where:
- Player moves with WASD/Arrow keys
- Weapons fire automatically
- Enemies spawn in waves
- XP gems drop from defeated enemies
- Level-up grants weapon upgrades
Step 1: The Game Loop
Every game starts with a loop. Here’s the foundation:
class Game {
constructor(canvas) {
this.ctx = canvas.getContext('2d');
this.lastTime = 0;
this.entities = [];
}
loop(timestamp) {
const dt = (timestamp - this.lastTime) / 1000;
this.lastTime = timestamp;
this.update(dt);
this.render();
requestAnimationFrame((t) => this.loop(t));
}
}
Step 2: Player Movement
Responsive movement with keyboard input:
const keys = {};
document.addEventListener('keydown', (e) => keys[e.key] = true);
document.addEventListener('keyup', (e) => keys[e.key] = false);
update(dt) {
const speed = 200; // pixels per second
if (keys['w'] || keys['ArrowUp']) this.y -= speed * dt;
if (keys['s'] || keys['ArrowDown']) this.y += speed * dt;
if (keys['a'] || keys['ArrowLeft']) this.x -= speed * dt;
if (keys['d'] || keys['ArrowRight']) this.x += speed * dt;
}
Step 3: Auto-Firing Weapons
The key mechanic — weapons fire on a timer toward the nearest enemy:
class Weapon {
constructor(type, cooldown) {
this.type = type;
this.cooldown = cooldown;
this.timer = 0;
}
update(dt, owner, enemies) {
this.timer -= dt;
if (this.timer <= 0 && enemies.length > 0) {
this.fire(owner, this.findNearest(owner, enemies));
this.timer = this.cooldown;
}
}
}
Step 4: Enemy Waves
Enemies spawn from screen edges with increasing density:
spawnWave() {
const count = Math.floor(5 + this.wave * 2);
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const dist = 600;
this.entities.push(new Enemy(
this.player.x + Math.cos(angle) * dist,
this.player.y + Math.sin(angle) * dist
));
}
this.wave++;
}
Step 5: XP and Level-Up System
When enemies die, they drop XP gems. Collect enough and choose an upgrade:
levelUp() {
this.paused = true;
const options = this.getRandomUpgrades(3);
this.showUpgradeUI(options);
}
The Neon Glow Effect
The secret sauce for making Canvas games look professional — the glow filter:
ctx.shadowBlur = 15;
ctx.shadowColor = '#00ff88';
ctx.fillStyle = '#00ff88';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
This single technique transforms basic shapes into cyberpunk visuals.
Deploy in 30 Seconds
# That's it. Just open index.html or deploy to Vercel
vercel deploy --prod
The Full Source
The complete game is under 500 lines of JavaScript. No build step, no dependencies, no configuration.
Want to try it? Play Neon Survivors →
What’s Next
- Add a boss battle system
- Implement persistent upgrades
- Create new weapon types
- Port to mobile with touch controls
Browser games are the fastest way to ship a playable product. Start with Canvas, keep it simple, and iterate.