FoldBack is an authoritative game server engine written in Common Lisp. It represents the entire game world as a single immutable value using persistent data structures (FSet). Because all state is immutable, rollback is a reduce over a history of inputs rather than a manual save/restore operation.
The server runs a game loop at 60Hz. Each tick, a pure function takes the current state and all player inputs, and returns the next state:
NewState = SimulationFn(CurrentState, Inputs)
Because the function is pure (no side effects, no mutation), the engine can:
- Roll back by starting from a past state and re-applying inputs from that point forward.
- Handle late packets by rewinding to the tick the input was meant for, inserting it, and re-simulating to the present.
- Enable client-side prediction by running the same function in the browser (ported to JavaScript) so the local player's actions feel instant, then reconciling with the server when its authoritative state arrives.
All physics use fixed-point integer arithmetic and a seeded PRNG to ensure the Lisp server and JavaScript client produce identical results from the same inputs.
The project has three layers:
| Layer | Language | Role |
|---|---|---|
Engine (src/) |
Common Lisp | Pure simulation loop, immutable state history, input buffering, delta-encoded broadcasts |
Gateway (gateway/) |
Go | WebSocket/WebRTC proxy between browsers and the Lisp UDP server. Game-agnostic — no changes needed for new games |
Client (gateway/[game]/) |
JavaScript | Mirrors the Lisp simulation for prediction, handles rendering, reconciliation, and interpolation |
src/engine.lisp— genericupdate-gameand rollback loop, accepts a pluggablesimulation-fnsrc/state.lisp—worldstruct managing immutable state history and input buffersrc/server.lisp— UDP server handling connections, heartbeats, and broadcastssrc/fixed-point.lisp— fixed-point arithmetic helperssrc/physics.lisp— shared collision primitives (circle-circle, circle-line-segment)
Each game provides 3 Lisp functions (join, update, serialize) and a JavaScript client. Games that need instant feedback use client-side prediction (CSP), where the JS client mirrors the Lisp simulation and reconciles with the server. Simpler games can use authoritative-only mode (prediction: false) where the client just renders what the server sends. See the tutorial for the full walkthrough and checklist.
| Game | Source | Mode | Key Mechanic |
|---|---|---|---|
| Tic-Tac-Toe | GDD, src/games/tictactoe.lisp |
Authoritative-only | Turn-based, no CSP — simplest example |
| Go Fish | GDD, src/games/gofish.lisp |
Authoritative-only | 2-5 players, hidden state (per-player serialization) |
| Pong | GDD, src/games/pong.lisp |
CSP | Tutorial game — simplest CSP example |
| Bomberman | GDD, src/games/bomberman.lisp |
CSP | Bomb placement, chain reactions, bot AI |
| Air Hockey | GDD, src/games/airhockey.lisp |
CSP | 1:1 paddle tracking, puck bounces, scoring to 11 |
| Jump and Bump | GDD, src/games/jumpnbump.lisp |
CSP | Platformer: head-stomping, screen wrapping |
CSP games have their simulation mirrored in JavaScript under gateway/[game]/logic.js. Authoritative-only games only need rendering and input handling on the client. Go Fish demonstrates per-player serialization for hidden state — each player sees their own hand but only card counts for opponents.
make setup
# Terminal 1 — start a game server (pick one)
make lisp-tictactoe
make lisp-gofish
make lisp-pong
make lisp-bomberman
make lisp-airhockey
make lisp-jumpnbump
# Terminal 2 — start the gateway
make gatewayThen open http://localhost:8080 in a browser.
Instead of manually starting game servers, the gateway can spawn and manage them on demand:
make gateway ARGS="--spawn"In spawn mode, the gateway starts an SBCL instance for each game when the first client connects to it. Ports are auto-assigned starting at 4445. Idle game servers are automatically killed after a period of inactivity. A /health endpoint reports the status of all spawned servers. Clients connect via /ws/{game} and /offer/{game} paths. Spawned processes are cleaned up on gateway shutdown (SIGINT/SIGTERM).
make testThis runs Lisp unit tests, Go gateway tests, and cross-platform determinism tests (same inputs through both Lisp and JS, comparing final state) for all games.
make lisp-bomberman &
make gateway &
sleep 5
npm run test:multiplayerLaunches two headless browsers and verifies that players can connect, see each other, and interact.
- Tutorial: Building Games with FoldBack — authoritative-only and CSP modes, state contract, wiring, and a new-game checklist
- Debugging Reference — SBCL/FSet/ASDF gotchas and solutions
- Game Design Documents: Tic-Tac-Toe, Go Fish, Pong, Bomberman, Air Hockey, Jump and Bump
- Wire Protocol Schemas:
schemas/[game]/— JSON Schema definitions for each game's messages