Building Swarm Entropy:
From Rock-Paper-Scissors to Neon Auto-Battler
What started as a simple coding experiment quickly spiraled into absolute neon chaos.
I set out to build a simple rock-paper-scissors simulation in a single HTML file: Leaf beats Water, Water beats Fire, Fire beats Leaf. Throw 60 units onto an HTML5 Canvas, make them chase each other, and see who wins.
But as the engine took shape, the scope exploded. I introduced flocking algorithms, rigid-body physics, class-based combat (Snipers, Healers, Shields), environmental hazards like wormholes, and game-breaking powers.
The result is Swarm Entropy: a zero-dependency, vanilla JavaScript auto-battler. Here is a look at how the game plays, and a deep dive into the custom engine running under the hood.
The Game: Chaos on the Grid
In Swarm Entropy, you don't directly control your units. You act as a commander: you pick your army, draft a game-breaking power, and release them onto the grid.
The battles are broken into two phases:
Rallying: Units scramble to find their allies, forming tight defensive clusters.
Battle: The gloves come off. Predators hunt their prey, while the prey frantically scatter.
To prevent infinite loops of units just chasing each other in circles, the arena is rigged with hazards. Mud pits slow units to a crawl, Wormholes teleport them across the map, and every few seconds, a massive Lightning Strike instantly vaporizes a random unit.
If a battle goes on long enough that only two armies remain, the game enters Sudden Death. The Rock-Paper-Scissors rules break down, and the "Prey" turns around to fight back, dealing 50% damage to their natural predators. It makes for some incredible underdog victories!
Under the Hood: Building the Engine
Writing a game engine from scratch in a single file means you have to get creative with performance and physics. Here are the three pillars that make Swarm Entropy tick.
1. Swarm Intelligence (Boids Algorithm)
The units don't use complex pathfinding like A*. Instead, they rely on steering behaviors inspired by Craig Reynolds' "Boids" algorithm. Every frame, each unit calculates its velocity based on three simple desires:
Cohesion: Move toward the center of mass of nearby allies.
Separation: Don't bump into your friends.
Flee/Steer: Run away from predators, charge toward prey.
By blending these forces together, the units organically form formations, kite enemies, and execute flanking maneuvers without any hard-coded logic.
// A simplified look at the steering logic
function steer(targetX, targetY, weight) {
const dx = targetX - u.x;
const dy = targetY - u.y;
const dist = Math.hypot(dx, dy);
if (dist < 0.5) return; // Prevent division by zero
// Add weighted velocity towards the target
fx += (dx / dist) * weight;
fy += (dy / dist) * weight;
}
// Snipers want to stay exactly at optimal firing range
const diff = currentDistance - optimalDistance;
if (diff > 0) {
steer(enemy.x, enemy.y, 2.5); // Move closer
} else {
flee(enemy.x, enemy.y, 3.2); // Too close! Back up!
}
2. The "Black Hole" Bug & Mass-Based Physics
Early in development, I noticed a game-breaking bug: The Fire Army was losing 90% of its matches, regardless of what powers were used.
It turned out to be a physics flaw. When resolving collisions, my knockback math was blindly pushing Unit A away from Unit B. But because of the way the arrays were structured, Fire units were accidentally getting pushed into their enemies instead of bouncing off them. They were getting sucked into a continuous-damage black hole!
To fix this, I rewrote the collision resolution to calculate kinetic knockback based on overlapping radii and physical mass. This not only fixed the bug but allowed me to introduce Juggernauts—massive units with 15x the mass that send standard units flying across the screen like bowling pins.
// Calculating overlap between two colliding circles
const overlap = (radA + radB) - dist;
if (overlap > 0) {
const nx = dx / dist; // Normal vector X
const ny = dy / dist; // Normal vector Y
// Calculate relative mass
const totalMass = massA + massB;
const push = KNOCKBACK_FORCE * (overlap / (radA + radB));
// Apply proportional momentum to both units
a.vx -= nx * push * (massB / totalMass);
a.vy -= ny * push * (massB / totalMass);
b.vx += nx * push * (massA / totalMass);
b.vy += ny * push * (massA / totalMass);
}
3. Zero-Cost Permanent Splatter (The Off-Screen Canvas)
I wanted the battlefield to feel the impact of the war. When a unit dies, it leaves a permanent, glowing neon splatter on the ground. However, telling the HTML Canvas to draw 500+ individual splatters 60 times a second would instantly tank the browser's framerate.
The solution? An off-screen canvas.
I created a secondary canvas that never gets added to the DOM. When a unit dies, I paint the splatter onto that invisible canvas exactly once. Then, in the main render loop, I just take a single "photograph" of that off-screen canvas and draw it as the background.
// 1. Setup an invisible canvas in memory
let splatterCanvas = document.createElement('canvas');
let splatterCtx = splatterCanvas.getContext('2d');
// 2. Draw to it ONLY when someone dies
function addSplatter(x, y, color) {
splatterCtx.beginPath();
splatterCtx.arc(x, y, radius, 0, Math.PI * 2);
splatterCtx.fillStyle = color;
splatterCtx.fill();
}
// 3. In the main 60FPS loop, just draw the cached image once!
function render() {
ctx.fillRect(0, 0, width, height); // Draw dark background
ctx.drawImage(splatterCanvas, 0, 0); // Draw the permanent splatter layer
// ... draw living units on top
}
Conclusion
Building Swarm Entropy was an incredible exercise in managing complexity within a single file. Watching the Boids algorithms interact with the class mechanics (like Shields intercepting predators while Healers cower in the back) creates genuinely emergent storytelling.
It proves you don't always need a massive game engine like Unity or Godot to build something chaotic, beautiful, and deeply fun. Sometimes, all you need is a <canvas> tag, a lot of math and a Pro Subscription to Google Gemini - that helped immensely with creation of this game ;)
[ LINK TO THE SWARM ENTROPY - https://marcdahl.dk/swarm.html ]
Kommentarer
Send en kommentar