Chipfire
A chain-reaction grid game you can play against a friend on another device, with no server at all

- backend servers for online play
- 0
- players per game on one device
- 2–4
- simulated games played before every deploy
- 3,000
- AI difficulty levels
- 3
On this page
The problem
Chain Reaction is a well-known grid game with dozens of browser clones. Most of them get two things wrong. The first is the rule itself. When a cell reaches capacity, it should subtract that capacity and keep the rest. Most versions empty it to zero. The game still looks fine, but it is a different game, and nobody notices.
The second is multiplayer. Every online version needs a lobby, a matchmaker and a game server, plus the hosting bill behind them. That cost is why most small games never get an online mode.
Chipfire is for two kinds of people. Players get a fast game they can learn in two minutes and play against a friend on another device, with no account. Technical readers get a working example of multiplayer with no backend at all.
What I built
The same moves always give the same game
I made the rules engine fully predictable. It uses whole numbers only, no clock, and randomness only from a seed stored in the game itself. Any device, in any browser, produces the same result from the same moves. So a game is simply its list of moves. Replay, undo, shareable game codes and online sync all come from that one property. Nothing else is stored or sent as the source of truth.
Two browsers check each other every turn
In online play, each browser keeps its own copy of the game. Only the moves and a short fingerprint of the position travel between them. Both sides apply the same moves and compare fingerprints every turn. If they ever differ, the players have drifted into two different games. The app stops at once, tells both players, and offers to rebuild from the move list. It never quietly picks a winner.
The players do the matchmaking
Two browsers behind home routers can't find each other on their own. Normally a server introduces them. I turned that introduction into a short code you can paste. You send one code over WhatsApp, your friend sends one back, and the game connects. There is no infrastructure to pay for, rate-limit or shut down.
Chains resolve in a fixed order
A cascade is processed one cell at a time from a queue, in the same scan order on every device. Two browsers that handled cells in a different order would drift apart, and that bug would never show on one machine. A cell's capacity is its number of neighbours, read from one function. That keeps hex or wrap-around boards a small addition later.
Failed connections are explained
About 10 to 20% of connections, on strict office networks and some home routers, can't link two devices directly without a paid relay server. A relay would break the no-backend idea. So Chipfire detects the failure quickly, explains it in plain Indonesian, and offers local play on the spot.
Result
Chipfire is live, in Indonesian and English. You can play hotseat with 2 to 4 people, play an AI opponent at three difficulty levels, or play a friend online with pasted codes. It also has a replay viewer, shareable game codes, a move preview and a five-lesson tutorial. Every deploy first plays 3,000 simulated games to prove the engine agrees with itself. I built it in under 24 hours on 2 and 3 August 2026, from written spec to deployed site.
Under the hoodTechnical detail for engineers
applyMove(state, move)is pure: noDate, noMath.random(a seeded generator lives in the state), no floats, including in AI scoring.- The cascade is an explicit FIFO queue with a fixed neighbour scan order. No recursion, which would overflow on long chains, and no iteration over a
Setor object keys. AUint8Arrayof positional flags stands in for aSet. - Desync detection exchanges a 32-bit FNV-1a hash of the full state every turn. Board size and seed travel with the offer; after that, only moves.
- Signalling uses
RTCPeerConnectionoffers compressed withCompressionStream(deflate-raw) into pasteable codes. Two public Google STUN servers handle address discovery only. There is no TURN relay. PeerJS brokered signalling (M5) and QR encoding are still outstanding. - The AI is minimax with alpha-beta pruning and iterative deepening in a Web Worker, on a time budget with an injected clock. It scores orb and cell advantage, corner and edge control, and vulnerability. Difficulties search depths 1, 3 and 6, weakened only by seeded noise.
- Move preview runs the real move through the real engine on a throwaway copy (
lib/engine/preview.ts). - The engine emits ordered
place,explode,convert,eliminateandwinevents in waves. The renderer replays them and honoursprefers-reduced-motion. - Tutorial lessons play real board positions whose stated outcomes are asserted in tests. Each player colour pairs with a distinct orb arrangement, never colour alone.
- Renamed from Rantai to Chipfire after recognising the chip-firing game (abelian sandpile model). Both
localStoragekeys migrate with a legacy fallback. - A production-only Tailwind bug hid player colours because
lib/wasn't in the class-scan path. - CI runs typecheck, lint, the full suite (including the 3,000-game simulation and cross-instance agreement), and checks the export for
.nojekylland locale routes. - Runtime dependencies:
next,react,react-dom. Engine 984 lines, AI 300 lines, networking 404 lines. About 6,700 lines of TypeScript in 66 files, 70 tests in 15 files, 26 commits.
Screenshots
Hiring, or building something similar?
I'm open to full-time roles and select freelance work.