Play the live demo at heistd.fun
HEISTD is a multiplayer 3D action game stack built in TypeScript across the client, API, and game server. The project combines a live WebSocket-driven gameplay layer, a browser-based map editor, and a shared code model so gameplay, tooling, and backend logic stay aligned.
It is structured like a real game platform rather than a single app: one frontend, one API, one dedicated game runtime, and a shared package layer that keeps types, map data, config, and transport contracts in sync.
- Fast-paced real-time multiplayer with server-authored lobby state.
- A browser map editor for building and iterating on playable spaces.
- Auth, persistence, and role-based map management in the main API.
- Shared game contracts used by client rendering and backend simulation.
- WebSocket game transport with reconnect support on the client.
- Dynamic lobby creation based on the selected map.
- Authoritative server loop that broadcasts live player and bullet state.
- Spawn-point driven joins, disconnect handling, and lobby cleanup.
- Full input pipeline for movement, jumping, aiming, weapon switching, reloads, and shooting.
- Combat support for cooldowns, ammo state, hit detection, and kill events.
- Spectating flow for cycling through active players.
- Emoji and world interaction events for more social in-match behavior.
- Map list, create flow, and per-map editing routes.
- Scene authoring tools for models, lights, and spawn points.
- Inspector panels for transforms, visibility, details, and light tuning.
- Dynamic object controls for gameplay-relevant scene behavior.
- Day and night skybox switching.
- Save flow backed by
/editor/updatewith unsaved-change detection. - JSON import/export for moving scene data between environments.
- Collision extraction utility for model collision data generation.
- Built-in game preview toggle while editing.
- Google OAuth login handled by the API.
- JWT auth flow with
/auth/meverification on the client. - Role-aware map visibility and editing permissions.
- PostgreSQL persistence through Prisma.
heistd/
|- client/ React app for gameplay, auth callback, and the editor
|- server/ Main HTTP API for auth, users, and map CRUD
|- server-game/ Dedicated WebSocket game runtime and simulation loop
|- shared/ Shared types, config, assets, and webserver helpers
`- unity-export/ Unity-side scene export tooling
- Runtime: Bun
- Frontend: React 19, Vite, TypeScript, React Router, SCSS
- Rendering and Physics: Three.js, React Three Fiber, Rapier
- Networking: WebSocket and reconnecting-websocket
- Backend: Elysia-style routing built on the shared webserver layer
- Data: PostgreSQL and Prisma
- A player authenticates through Google OAuth.
- The client stores the JWT and validates the session with
/auth/me. - The client opens a WebSocket connection to the game server with a
mapId. - The game server joins an existing lobby for that map or creates a new one.
- Shared map data is loaded, the lobby handshake is sent, and live state streaming begins.
- Bun installed
- PostgreSQL database
- Google OAuth client credentials (for sign-in)
bun run install:allCreate environment variables for each app as needed.
Common variables used in the codebase:
DATABASE_URL: PostgreSQL connection string forserver.API_URL: Public API base URL used by OAuth redirect handling inserver.FRONTEND_URL: Client URL used after OAuth success redirect.GOOGLE_CLIENT_ID: Google OAuth client id.GOOGLE_CLIENT_SECRET: Google OAuth client secret.MAIN_API_URL: Base URL used byserver-gameto fetch map data.VITE_API_URL: API URL used by client HTTP requests and auth start link.VITE_GAME_SERVER_URL: WebSocket server URL for live gameplay.
Run everything together:
bun run devRun services individually when needed:
bun run dev:client
bun run dev:server
bun run dev:server-gameDefault ports in source:
- API server:
3063 - Game WebSocket server:
3064 - Client: Vite default (
5173unless changed)
Client build:
cd client
bun run buildServer and game server both include Bun compile scripts in their local package.json files.
- Shared contracts in
shared/typesare used across client and servers. - Editor scene payloads are persisted as JSON in the
mapstable. - The game server currently includes NPC scaffolding and extensible systems for future mechanics.


