A multiplayer first-person shooter built in TypeScript, running in the browser via WebGL with server-authoritative netcode, client-side prediction, and lag-compensated hit detection.
Live: game.cyberrunnergame.dev
| Layer |
Technology |
| Rendering |
Three.js: post-processing, procedural weapon viewmodels |
| Physics |
RAPIER 3D (WASM): deterministic simulation on client and server |
| Networking |
Colyseus: room-based multiplayer with automatic state patching |
| Server |
Node.js + Express: game rooms, REST API, Google OAuth |
| Database |
PostgreSQL: users, sessions, loadouts |
| Client Build |
Vite + TypeScript |
| Server Build |
tsc + tsc-alias |
| Deployment |
DigitalOcean, Caddy (auto-TLS), systemd |

- Server-authoritative: The server runs all physics, validates all damage, and controls game state. Clients predict locally but always reconcile with the server.
- Shared physics code: Both sides run identical RAPIER simulations from
shared/ so client prediction closely matches the server.
- Binary hot path: Per-tick input uses a custom binary codec to minimize bandwidth on high-frequency messages.
- Tick-based lag compensation: The server stores position history and rewinds players to where the shooter was looking when they fired, with sub-tick interpolation. Based on the Valve/Bernier model.
| Feature |
Description |
| Client-side prediction |
Client runs the same RAPIER physics locally; pending inputs are buffered and replayed on reconciliation |
| Server reconciliation |
On each server ack: reset to authoritative position, discard confirmed inputs, replay unacked inputs, smooth the correction |
| Remote interpolation |
Smooth lerp toward latest server position with dead zones to prevent oscillation at rest |
| Lag compensation |
Server rewinds all players to the shooter's visual tick, raycasts against historical positions, then restores |
| Latency measurement |
Custom ping/pong with EMA smoothing |
- 8 weapons: Assault rifle, SMG, LMG, shotgun, sniper, pistol, rocket launcher, grenade launcher -- hitscan and projectile types with per-weapon damage falloff
- Hit detection: Per-body-part hitbox sensors (head, torso, arms, legs) with damage multipliers and head priority
- Movement: State machine (walking, sliding, crouching, prone) with sprint and dash
- Game modes: Deathmatch (FFA) and Search & Destroy (round-based, spike plant/defuse)
npm install # Install all workspace dependencies
npm run dev:server # Colyseus server (ws://localhost:2567)
npm run dev:client # Vite dev server with HMR
npm run build # Build client and server
npm run preview # Build + start server
| Variable |
Default |
Description |
PORT |
2567 |
Server listen port |
MAX_PLAYERS |
8 |
Max players per room |
MAP_ID |
shoot-house-neon |
Active map |
VITE_WS_URL |
-- |
Override WebSocket URL |