A modular LAN multiplayer game platform. The host laptop runs the server and displays the game board, while players use their phones as controllers. Games are plugins — add a new game without modifying the platform.
| Game | Description | Players |
|---|---|---|
| Yahtzee | Score tracker with two sections (School + Figures), multi-round | 1–8 |
| Quiz | Pub quiz with ABCD options, timer, scoring based on speed & accuracy | 1–8 |
Laptop (host) Phones (players)
┌─────────────┐ ┌──────────┐
│ HostView │◄──WiFi───►│Controller│
│ (questions, │ Socket.io │ (ABCD │
│ scores) │ │ buttons)│
└──────┬──────┘ └──────────┘
│
Fastify + Socket.io
(server on laptop)
- Host starts the server on the laptop
- Players scan QR code with their phones (LAN, same WiFi)
- Host starts the game — questions/scoreboard on the laptop, controls on phones
- After the game, everyone returns to the hub for the next game
Players scan the QR code once and stay in a lobby. The host starts games one after another without players needing to reconnect:
- Multiplayer — phones as controllers
- Local — single screen, host manages everything
There's also a "Quick Game" mode — classic per-game flow without the hub.
# Requirements: Node.js 18+, pnpm
pnpm install # uses frozen lockfile by default (clean install)
pnpm devDev server:
- Frontend:
http://localhost:5173 - Backend:
http://localhost:3001 - Phones: scan QR or go to
http://<laptop-IP>:5173
Production:
pnpm build
pnpm start # Fastify serves the frontend + API on port 3001packages/
core/ @gamehub/core — shared TS types & plugin interfaces (zero deps)
server/ @gamehub/server — Fastify 5 + Socket.io 4
web/ @gamehub/web — Vite 6 + React 19
games/
yahtzee/ @gamehub/yahtzee — plugin: score tracker
quiz/ @gamehub/quiz — plugin: timed ABCD quiz
pnpm workspaces monorepo. Each game is a separate package exporting GameServerPlugin + GameClientPlugin.
Each game implements two interfaces:
Server (GameServerPlugin):
createInitialState()— initial game statehandleAction(ctx, action, payload)— game logicserializeForClient(state, isHost, playerIndex)— what the client sees (info hiding)onTick?(session, broadcast)— timer hook (every second)
Client (GameClientPlugin):
HostView— React component for the laptopControllerView— React component for phonesSummaryView— end-of-game summary screen
- Create a package in
games/your-game/ - Implement
GameServerPluginandGameClientPlugin - Add the import in
packages/server/src/plugin-loader.ts - Add a lazy import in
packages/web/src/plugin-registry.ts pnpm installand you're done
We use feature branches + PRs:
- Create a branch from
main:git checkout -b feature/your-feature - Make your changes, commit
- Push and open a Pull Request
- Get a review, merge to
main
Branch naming: feature/..., fix/..., chore/...
- Frontend: React 19, Vite 6, TypeScript
- Backend: Fastify 5, Socket.io 4, TypeScript
- Monorepo: pnpm workspaces
- Auth: tokens (in-memory, LAN-only)
- State: server-authoritative with optimistic client updates
MIT — see LICENSE.md