Skifire
An online 2D arena shooter: Tribes-style skiing, Soldat-style jetpacks and Worms-style terrain you blow apart.
The game
Two to four players fight in a free-for-all, or against bots. Hold ski to slide frictionless down the slopes and carry that speed into the air, burn the jetpack to climb, and swing on a grappling hook. The disc launcher, grenades and chaingun carve the earth, and a disc that hits an airborne target scores a MIDAIR bonus.
- Modes: deathmatch, or Rabbit, where whoever holds the one flag scores.
- Maps: four script-painted maps, each with its own colour theme, plus seeded generated maps.
- Matches: five minutes, or first to 10, 20 or 30. A room code in the URL is the whole lobby: share the link and the match starts when everyone has joined.
How it's built
- Rollback netcode, peer to peer. Bevy with ggrs over WebRTC (matchbox): a 60 Hz fixed simulation, two frames of input delay, up to eight frames of prediction. There is no game server, only a signalling server and a relay for players behind strict NATs.
- Deterministic from day one. All simulation state lives in one rollback resource stepped in a fixed order. Every test run also plays an offline sync-test session that re-simulates frames and fails on any desync.
- Terrain that rolls back. The world is an 8192×1536 pixel mask. An explosion is a carve event in an undo log that keeps the pixels it removed, so a rollback undoes carves newer than the restored frame instead of snapshotting the map.
- Movement without a physics engine. Players move kinematically against the mask, with surface normals averaged from nearby solid pixels and sub-stepping so nothing tunnels through a wall at ski speed.
- A seat for agents. A headless agent API runs matches at about 250× real time over JSON lines, with a Gymnasium-style Python wrapper, so an RL loop or an LLM with tool calls can take seats beside the bots.
- Sound from recipes. Every sound effect is synthesised from a fixed-seed recipe layered over CC0 recordings, and a check proves the rebuilt clips are byte for byte the ones shipped.