Asteroids feels like it needs a physics engine — momentum, rotation, splitting rocks, screen wrap. It doesn't. The whole thing is velocity vectors, one angle, and a screen that wraps, in about 120 lines of vanilla JavaScript on a canvas.
🎮 Play it: https://dev48v.infy.uk/game/day10-asteroids.html
1. Rotation is just one number
The ship doesn't move left/right — it rotates. Arrow keys nudge a single angle:
if (left) ship.angle -= 0.07;
if (right) ship.angle += 0.07;
Everything else — which way you thrust, where bullets fly — is derived from that one angle with sin/cos.
2. Thrust adds momentum (you coast)
This is what makes it feel like space. Thrust adds to your velocity, not your position. Let go and you keep drifting:
if (up) {
ship.vx += Math.cos(ship.angle) * 0.13;
ship.vy += Math.sin(ship.angle) * 0.13;
}
ship.x += ship.vx; ship.y += ship.vy;
ship.vx *= 0.99; ship.vy *= 0.99; // gentle "space friction"
3. Trig converts an angle into movement
cos(angle) is the x-component of where you're pointing, sin(angle) is the y. The same pair aims your bullets. Two functions turn "which way am I facing" into "how do I move."
4. The screen wraps (toroidal space)
Fly off one edge, reappear on the other — four lines, applied to the ship, every bullet, and every rock:
const wrap = o => {
if (o.x < 0) o.x += W; if (o.x > W) o.x -= W;
if (o.y < 0) o.y += H; if (o.y > H) o.y -= H;
};
5. Splitting rocks = recursion in disguise
Shoot a big rock and it becomes two smaller, faster ones; shoot those and they split again until they're tiny enough to vanish:
if (dist(bullet, rock) < rock.r) {
if (rock.r > 14) for (let k = 0; k < 2; k++) spawnSmaller(rock);
rocks.splice(i, 1); score += 20;
}
That cascade is what turns 4 asteroids into a tense, screen-filling swarm.
The takeaway
Arcade games feel complex but are usually a handful of simple rules running 60 times a second. Model the world as plain data, apply the rules each frame, draw. Play it here — the "Understand" tab walks through each step. ☄️

























