JP Bothma Work Services About CV Contact

← All work

SwarmFort.io

A multiplayer .io game where toys defend forts from a horde, grow swarms of flies, and fire them at each other. Free in the browser, no download.

When
2026
Role
Product lead · built with Claude Code
Stack
TypeScript · Canvas 2D · Web Audio · Node.js · WebSockets · SQLite
SwarmFort.io — cover image

Eight players (people, with bots filling empty seats) each start at a fort on one shared map. Your toy fights the dust-bunny horde automatically, so moving is the main control. Kills drop gems and, sometimes, a fly for your swarm. Carry gems home to bank them, then spend them on levels, turrets, walls and a bigger swarm.

Fire the swarm at a rival, their fort or their mine; they can send their own swarm to meet it, and the bigger side wins the clash. Lose your toy or your fort and you’re out. The last one standing wins. An Infinite mode runs a much bigger world that never ends: people come and go, empty forts are captured, and whoever holds the most wears the crown with a bounty on their head.

In numbers

  • 9 Days to build · 39 commits
  • ~13K Lines of typescript
  • 72 Automated tests
  • 1 Runtime dependency (ws)
  • ~83 KB Gzipped game code
  • 150+ Players per cpu core in load tests

Engineering highlights

  • Deterministic simulation. A fixed 60 Hz step, seeded randomness and no clock reads, so the same seed and inputs always replay the same match. That one rule powers the tests, a bot-only balance simulator, and the preview-video tool, which replays matches to film their best moments.
  • Server-authoritative multiplayer. A Node server with WebSockets runs every room on one 60 Hz loop. Each player gets binary snapshots 20 times a second, trimmed to what’s near their camera: about 1–2 KB each. The client predicts its own movement and reconciles with the server by input sequence numbers.
  • Bots that play properly. An autopilot fills empty seats: bots bank and build, claim mines, block incoming swarms and fire their own. Bot-only match simulations tuned the pacing to 7.5–10 minute games, with the first knockout after minute three.
  • Zero art or audio files. Characters, monsters, forts, the toy-room floor and about 80 UI icons are drawn in code. Every sound is synthesised with Web Audio, including a soundtrack that builds through the match and a buzzing drone for swarms in flight.
  • Ready for game portals. One adapter connects the CrazyGames SDK (ads, rewarded ads, cloud saves, invites, instant multiplayer, mute) and falls back to a plain build anywhere else. A Playwright script plays the game at all ten of the portal’s QA window sizes and fails on overlapping or clipped UI.
  • Privacy-first analytics. Anonymous gameplay events (no accounts, no personal data; Do Not Track and GPC respected) go into SQLite on the game server. A private stats page shows players, sessions, games and day-1 / day-7 retention.

How it fits together

  • Browser client. Canvas 2D renderer, DOM HUD, Web Audio, input prediction, a replica of the world built from snapshots.
  • Shared simulation. The same deterministic world code runs on the server and offline in the browser, so the game still works with no server.
  • Game server. Node and ws: lobbies, rooms, a 60 Hz loop, interest-managed snapshots, analytics, static hosting.

Client to server: movement inputs each frame (binary), commands as small JSON. Server to client: 20 Hz binary snapshots, events and seat info. Hosted on a small VPS behind nginx, with systemd and Let’s Encrypt. GitHub Actions tests every branch and deploys when the production branch moves, with validation and automatic rollback.

From first build to portal-ready

  1. Started as Survivors 99: 99 players each fighting their own horde and sending monsters at each other. A shared-map prototype played better, so it became one map where forts, mines and swarms make other players the real threat.
  2. Playtesting showed nobody could lose in the opening, so protection became shorter, weaker and visible (a shield bubble with a countdown).
  3. Moved the game onto an authoritative multiplayer server, then shipped it on its own domain with automatic deploys.
  4. The first CrazyGames submission was declined on quality. Their QA guidelines became a checklist: a menu that fits every window, a decluttered HUD, instant play, an in-game tutorial, swarms from 0:30 instead of 2:00, and protection for newcomers.
  5. A visual pass replaced emoji with drawn icons, added real game fonts, and turned the flat map into a toy room.

Screens

The SwarmFort.io menu: the logo, four toy characters to choose from, PLAY and INFINITE buttons, over a live bot match.
The menu, over a live bot match.
A SwarmFort.io match on the toy-room map: forts on rugs, a crystal mine, players with fly swarms, the minimap and standings.
A match: forts on rugs, a mine in a sandbox, swarms buzzing around their players.

My role

I led the product: the concept, the game design calls, playtesting and balance feedback, the name and domain, hosting, and the CrazyGames submission. I built it with Claude Code as an AI pair-programmer, directing the work feature by feature and reviewing the results.

Visit SwarmFort.io ↗

© 2026 JP Bothma · Leiden, Netherlands · Linkedin · Github