A private, invite-only real-time web living room for two people combining peer-to-peer WebRTC video calling, server-authoritative synchronized arcade games, and live chat with emoji quick-reactions — wrapped in an authentic, high-tactility Neo-Brutalist Arcade aesthetic.
Orbit is architected as a distributed real-time system cleanly separated into a stateless presentation tier, a stateful WebSocket signaling & game engine tier, and an encrypted direct peer-to-peer media plane:
flowchart TD
subgraph Clients ["Two-Player WebRTC Mesh"]
Host["Host Browser (React 19 / Canvas)"]
Peer["Peer Browser (React 19 / Canvas)"]
end
subgraph ReverseProxy ["Edge Ingress & Automated TLS"]
Caddy["Caddy Server (orbit-arcade.duckdns.org)"]
end
subgraph InternalNet ["Internal Docker Compose Network"]
Web["Next.js 15 Web Frontend (:3000)"]
WS["Socket.IO Signaling Server (:4000)"]
RL["Token Bucket Rate Limiter (Per Socket)"]
State["In-Memory Room State & Game Engines"]
DB[("PostgreSQL 16 via Prisma (:5432)")]
Reaper["Background Reaper Daemon (15m Interval)"]
end
subgraph External ["External Content APIs"]
Wiki["Wikipedia Summary REST API"]
Cache["In-Memory Thumbnail Pre-warm Cache"]
end
Host <-->|Direct P2P Encrypted Media Stream UDP| Peer
Host -->|HTTPS / WSS| Caddy
Peer -->|HTTPS / WSS| Caddy
Caddy -->|Reverse Proxy /| Web
Caddy -->|Reverse Proxy /socket.io| WS
Caddy -->|Reverse Proxy /api/room| WS
WS --> RL
RL --> State
State -->|Async Non-Blocking| DB
Web -->|Prisma Client| DB
Reaper -->|deleteMany Stale Rooms| DB
WS -->|Boot Pre-warm| Wiki
Wiki --> Cache
Cache -->|0ms Lookup| State
- Zero Relay Overhead: High-bandwidth audio and video streams never route through our Node.js server. The signaling server exclusively manages lightweight JSON control events:
- Session Description Protocol (
webrtc_offer,webrtc_answer) handshakes. - Interactive Connectivity Establishment (
ice_candidate) exchange.
- Session Description Protocol (
- Direct P2P Transport: Once ICE negotiation succeeds via Google STUN servers (
stun:stun.l.google.com:19302), media streams flow directly peer-to-peer via encrypted UDP (SRTP), ensuring sub-50ms glass-to-glass latency with zero server bandwidth consumption.
- Anti-Cheat by Design: Clients own zero game state. Browser clients transmit lightweight Intents (
make_guess,paddle_move,draw_stroke), which the server validates against active turn timers, game rules, and coordinate bounds before mutating state. - State Masking Engine (
getStateForPlayer):- To prevent client inspection via Chrome DevTools Network tabs, the server masks sensitive game fields per player role.
- In Celebrity Mystery, the Speaker receives the full name and Wikipedia portrait URL, while the Guesser receives an obfuscated payload containing only category clues and a placeholder (
?). - In Draw & Guess, the Drawer receives the secret word, while the Guesser receives a masked letter-count placeholder (
_ _ _ _ _). - In Higher or Lower, the target number is completely hidden from both players until the victory or game-over state is reached.
- Two-Phase Commit-Reveal Synchronization: In Rock Paper Scissors, players select choices asynchronously. The server holds choices in secret until both players have committed, broadcasting the reveal atomically in the same tick.
- Stable Identity vs. Ephemeral Sockets: Raw
socket.idvalues change on every network hiccup or page reload. Orbit issues persistent UUIDparticipantTokens(stored tab-scoped insessionStorage) and validates them against PostgreSQL. - 60-Second Disconnect Grace Period: If a user accidentally refreshes their tab or experiences a brief network drop, the server retains their room slot and game progress in memory for 60 seconds before triggering forfeiture.
- Two-Tier Reclamation Strategy:
- Reactive Cascade Deletion: When both participants disconnect, the room is immediately evicted from memory and an asynchronous Prisma query deletes the room and messages from PostgreSQL.
- Active Reaper Daemon (
reaper.ts): A background cron-like daemon executes on server boot and every 15 minutes. It executesprisma.room.deleteMany()for any database rooms older than 2 hours whose codes are no longer present in memory, guaranteeing zero orphan database rows.
- Targeted Event Loop Protection: A malicious user or bot could spam thousands of socket events per second, choking the Node.js single-threaded event loop.
- Keyed by
socket.id(Zero Collateral Damage): Rate limiters are keyed strictly by socket connection ID rather thanroomCode. If a rogue peer floods messages or reactions, only their specific socket is throttled, leaving the host's gameplay and chat completely unaffected.- Messages: Maximum 5 events per 2-second rolling window.
- Reactions: Maximum 8 events per 2-second rolling window.
- Memory Management: Automated cleanup hooks delete rate limiter tracking buckets immediately upon socket disconnection.
- Wikipedia REST API Pre-Warming: Celebrity portraits hardcoded from external CDNs often succumb to 404 bitrot or 403 anti-hotlink blocks.
- 0ms In-Game Cache: On signaling server boot,
preloadCelebrityImages()fetches official Wikipedia REST Summary thumbnails with a custom user-agent and caches them in an in-memoryMap. During gameplay, portrait URLs resolve in 0ms without outbound network calls during game ticks. - Referrer Shielding: The frontend renders celebrity images with
referrerPolicy="no-referrer", preventing browser cross-origin referrer leaks.
- Liveness & Readiness Heartbeats (
/health): Performs an ultra-fastSELECT 1query against PostgreSQL, returningHTTP 200when the connection pool is healthy orHTTP 503if exhausted, enabling container self-healing. - Real-Time Telemetry Endpoint (
/api/metrics): Exposes system metrics including process uptime, V8 memory heap (heapUsedMB,rssMB), active room counts, and live socket connection counts. - Safe Process Draining: Catches POSIX
SIGTERM/SIGINTsignals to close the HTTP server, disconnect active WebSockets cleanly, flush in-flight database transactions, and disconnect Prisma connection pools without deadlocks.
- The 40Hz Virtual DOM Bottleneck: Emitting 40Hz server physics ticks into standard React state (
useState) triggers 40 full Virtual DOM reconciliations per second, dropping browser frame rates to 15–20 FPS and introducing 100ms+ of input lag. - Render Shield & Native RAF Loop:
- Game components intercept server ticks into mutable React references (
useRef), shielding React's reconciliation engine from high-frequency updates. - Rendering is offloaded to a browser-native
requestAnimationFrameloop on an HTML5<canvas>. - 0ms Client Prediction: Local paddle movements update a local position reference immediately, providing 0ms responsive feedback while server roundtrips resolve.
- 30Hz Monotonic Input Throttling: Outbound paddle movements are throttled to 33.3ms intervals (
Date.now() - lastEmitTime >= 33.3), cutting client network traffic by over 70% while locking rendering at 60 FPS.
- Game components intercept server ticks into mutable React references (
- Asynchronous ICE Candidate Queueing: ICE candidates frequently arrive from the signaling channel before the browser has completed setting its
RemoteDescription, causing native WebRTCInvalidStateErrorcrashes. Our hook buffers early candidates in a memory queue and flushes them sequentially the instant the remote description resolves. - Hardware-Level Track Toggling: Microphone and camera toggles mutate
MediaStreamTrack.enableddirectly at the hardware driver level, enabling instant mute/unmute without triggering expensive WebRTC renegotiation offer/answer cycles. - Automatic Resource Cleanup: When navigating away or switching rooms, all camera and microphone tracks are stopped (
track.stop()), peer connections are closed, and media stream bindings are purged from memory.
- Cross-Tab Collision Problem: Storing tokens in
localStoragecaused second tabs on the same developer machine to read the first tab'shostToken, falsely reporting "Room is full! Only 2 players allowed". - Tab Isolation: Shifting token storage to
sessionStorageensures each browser tab maintains an independent identity sandbox. Developers and users can run Host and Peer side-by-side in two tabs of the same browser with zero token collisions.
- Bespoke Tailwind Token System: Custom configuration in
apps/web/tailwind.config.ts:border-orbit-border: High-contrast3px solid #1a162b.shadow-arcade: Solid offset drop shadow4px 4px 0px #1a162b.shadow-arcadeLg: Elevated card shadow6px 6px 0px #1a162b.bg-orbit-cream: Cozy retro substrate#f5f0e8.
- Google Font Trifecta:
- Headlines & Retro Badges:
Silkscreen(Pixel Arcade) with positive letter tracking and optical vertical-align baseline compensation. - Body & Controls:
Space Grotesk(Geometric Neo-Grotesque) for crisp legibility. - Telemetry Readouts:
JetBrains Monofor hardware status indicators and monospace timers.
- Headlines & Retro Badges:
- Optical Baseline Alignment for Pixel Typefaces: 8-bit fonts like
Silkscreenpossess high cap-height baselines. We engineered sub-pixel padding offsets (pt-0.5/pt-1) across all badge tokens, guaranteeing pixel labels sit optically centered within thick brutalist borders. - Emil Kowalski Physical Button Physics:
Clicking a button translates it down and right by 4px (
/* Physical Button Press State */ transition-all duration-150 ease-out active:translate-x-1 active:translate-y-1 active:shadow-none active:scale-[0.98]
translate-x-1 translate-y-1) while collapsing its 4px solid drop shadow (shadow-none), perfectly mimicking the physical travel depth of an arcade micro-switch. - Retro CRT Monitor Video Feeds: Video tiles feature industrial chassis framing, tactical corner alignment reticles (
⌜ ⌝ ⌞ ⌟), live telemetry badges (HOST_FEED // 60FPS), and hardware toggle switches with tactile click feedback. - Floating Emoji Reaction Physics: Emoji clicks spawn floating reactions that rise with randomized horizontal drift and fade out using CSS keyframe transforms, accompanied by tactile button depression feedback.
| Game | Real-Time Mechanism | Key Technical Architecture |
|---|---|---|
| 1. Higher or Lower | Turn-based number deduction | Server bounds checking, anti-cheat target number masking, victory state broadcast |
| 2. Draw & Guess | HTML5 Canvas vector sync | 30fps throttled coordinate streaming, secret word masking, live guess evaluator |
| 3. Celebrity Mystery | Verbal clue guessing duel | Turn-clock state machine, Wikipedia REST API pre-warmed image cache, guess limiting |
| 4. Rock Paper Scissors | Simultaneous reveal duel | Two-phase "Commit-Reveal" state synchronization with countdown timers |
| 5. Table Tennis (Pong) | 60Hz Physics simulation | Server-authoritative tick loop, 0ms client prediction, React DOM render shield |
- Phase 0 — Setup & Monorepo Foundation
- PostgreSQL 16/18 catalog configuration & Prisma schema creation.
- Monorepo npm workspaces setup (
@orbit/db,@orbit/web,@orbit/signaling-server). - Dual-server architecture: Next.js 15 on port 3000 + Node.js Socket.IO on port 4000.
- Dynamic CORS resolution supporting
localhost, local LAN IPs, and public domains.
- Phase 1 — Layout & Neo-Brutalist UI Shell
- Custom Tailwind CSS design tokens (arcade borders, offset drop shadows, calibrated palette).
- Integrated Google Fonts: Silkscreen, Space Grotesk, and JetBrains Mono.
- Fully responsive Asymmetric Bento Grid layout.
- Phase 2 — Room Creation & Token-Based Authentication
- Fun, memorable Pokemon room code generator (
cozy-pikachu-42,lofi-squirtle-22). - Server-side
RoomManagerwith 60-second disconnect grace period. - Tab-scoped
sessionStoragetoken isolation to support concurrent local tabs without collisions. - Real-time socket presence updates (
connecting/connected/disconnected).
- Fun, memorable Pokemon room code generator (
- Phase 3 — WebRTC 1:1 Video & Audio Calling
- Custom
useWebRTChook utilizingRTCPeerConnection& Google STUN servers. - SDP Offer / Answer and asynchronous ICE candidate signaling over Socket.IO.
- Retro CRT terminal stream monitors with hardware toggle switches for Mic & Camera.
- Micro-switch hardware controls with instant tactile click feedback and track muting.
- Custom
- Phase 4 — Server-Authoritative Multiplayer Game Suite
- Extensible game engine framework with state sanitization per player role.
- Real-time gameplay implementation for all 5 arcade games.
- Decoupled 60 FPS HTML5 Canvas Pong loop with 0ms client-side prediction.
- In-memory pre-warmed Wikipedia REST API image cache for Celebrity Mystery.
- Phase 5 — Real-Time Chat & Floating Emoji Quick-Reactions
- Socket.IO chat messaging with PostgreSQL history persistence.
- Pop-up floating emoji reaction animations with tactile button triggers.
- Token-bucket rate limiter per
socket.idprotecting server event loops against flooding.
- Phase 6 — Production Containerization & Cloud Deployment
- Multi-stage Dockerfiles (
Dockerfile.web,Dockerfile.signaling) with Next.js standalone SSR output. - Docker Compose orchestration with isolated internal bridge networking (
orbit-net). - Caddy 2 reverse proxy with automated Let's Encrypt TLS/SSL termination for
orbit-arcade.duckdns.org. - Liveness & readiness probes (
/health) and real-time metrics telemetry (/api/metrics). - Background Reaper daemon for automatic deletion of stale database records.
- Multi-stage Dockerfiles (
- Frontend: Next.js 15 (App Router, React 19, TypeScript)
- Styling & Motion: Tailwind CSS, PostCSS, Lucide Icons, Google Fonts (Silkscreen, Space Grotesk, JetBrains Mono)
- Realtime Signaling & Game Engines: Node.js, Express, Socket.IO 4.8
- Video/Audio Streaming: WebRTC (
RTCPeerConnection, Google STUN) - Database & Persistence: PostgreSQL 16, Prisma ORM
- Containerization & Ingress: Docker, Docker Compose, Caddy 2 (Automated TLS)
- Monorepo Tooling: npm workspaces
- Node.js v20+
- PostgreSQL 16 or 18 running locally on port 5432
# Clone the repository
git clone https://github.com/your-username/orbit.git
cd orbit
# Install monorepo dependencies
npm install
# Configure environment variables
cp .env.example .env
# Edit DATABASE_URL in packages/db/.env to match your local PostgreSQL credentials
# Push Prisma schema to local database
npm run db:push
npm run db:generate# Start all services concurrently (Web on :3000, Signaling on :4000)
npm run devVisit http://localhost:3000 in your browser. Open an Incognito window or second tab to test 1:1 video calling and multiplayer arcade games side-by-side!
Orbit is fully containerized and production-ready for self-hosting on any cloud VPS (Oracle Cloud Always-Free, DigitalOcean, AWS EC2, or Hetzner).
- Log in to DuckDNS and create a domain (e.g.,
orbit-arcade.duckdns.org). - Point the domain's A record to your VPS's public IP address.
Create a .env file in the project root:
POSTGRES_USER=orbit
POSTGRES_PASSWORD=your_secure_password_here
POSTGRES_DB=orbit
DATABASE_URL=postgresql://orbit:your_secure_password_here@postgres:5432/orbit?schema=public
NEXT_PUBLIC_SIGNALING_URL=https://orbit-arcade.duckdns.org
DOMAIN=orbit-arcade.duckdns.org# Build and start all 4 services in the background
docker compose up -d --buildCaddy will automatically request and install a Let's Encrypt SSL/TLS certificate, route incoming HTTPS traffic to Next.js, and proxy WebSocket connections (/socket.io) to the Node.js signaling server.
# View aggregated service logs
docker compose logs -f
# Check container health and memory usage
docker compose ps
# Stop all services cleanly
docker compose downRather than relying on generic AI "vibe coding" — where an LLM hallucinates arbitrary CSS styles, creates inconsistent margins, and generates non-functional mockup aesthetics — the entire interface and interaction model of Orbit was crafted using a disciplined design engineering toolchain powered by three specialized AI design skills:
┌────────────────────────────────────────────────────────────────────────┐
│ DISCIPLINED DESIGN PIPELINE │
├────────────────────────────────────────────────────────────────────────┤
│ 1. pbakaus/impeccable │ Design Director & Pre-Flight Linter │
│ 2. Leonxlnx/taste-skill │ Design System Architect & Token Engine │
│ 3. emilkowalski/skills │ Physical Motion & Physics Interaction │
└────────────────────────────────────────────────────────────────────────┘
- Audit-First Rule: Before writing code, the system executed static AST audits (
impeccable detect) to enforce layout constraints and catch visual defects. - Typographic Rigor (
/typeset): Eliminated overlapping lines caused by tight tracking, balanced font scaling across breakpoints, and enforced strict typographical hierarchy across all screens. - Optical Spacing & Geometry (
/polish): Removed "fake screenshot slop". Container dimensions were calibrated so that thick 3px brutalist borders never clipped text, and container heights were mathematically synchronized (h-12matching on inputs and triggers).
- Token-Driven Architecture: Injected strict design tokens into Tailwind (
border-orbit-border,shadow-arcade,shadow-arcadeLg,display-hero,telemetry). - Asymmetric Bento Grid: Replaced generic repeating card rows with an intentional, asymmetric bento layout (a 4-column active Pong mini-court paired with 2-column arcade game teaser cards).
- Optical Baseline Alignment for Pixel Fonts: Silkscreen and other pixel display typefaces have an unusually high cap-height baseline.
taste-skillenforced sub-pixel top padding offsets (pt-0.5/pt-1) so pixel text sits optically centered inside bordered badges and buttons. - Substrate Color Calibration: Prevented harsh
#000000pitch blacks; calibrated background substrates to warm retro cream (#f5f0e8) and high-contrast dark plum borders (#1a162b).
- The Mechanical Button Depression: Emil Kowalski's philosophy states that software should feel like a physical instrument. Every interactive element implements a true mechanical press:
Clicking a button translates it down and right by 4px (
/* Physical Button Press State */ transition-all duration-150 ease-out active:translate-x-1 active:translate-y-1 active:shadow-none active:scale-[0.98]
translate-x-1 translate-y-1) while simultaneously collapsing its 4px solid drop shadow (shadow-none), perfectly mimicking the depression of a physical arcade micro-switch. - Hardware-Accelerated Transforms: All transitions are isolated to
transformandbox-shadowwithtransform-gpu, guaranteeing butter-smooth 60 FPS motion without triggering browser layout reflows. - Spring Hover Feedback: Game cards elevate smoothly on hover (
hover:-translate-y-1 hover:shadow-arcadeLg), creating an intuitive, tactile sense of physical depth.
- Author: Abhyodaya Singh
- Email: abhyodayasingh00@gmail.com
- Domain: orbit-arcade.duckdns.org



