╔═══════════════════════════════════════════════════════╗
║ A I R H O C K E Y 3 D ║
║ Real-time multiplayer in the browser ║
║ ║
║ [Player 1] ←───── WebSocket ─────→ [Player 2] ║
║ │ Yjs CRDT │ ║
║ Three.js Three.js ║
║ Physics Physics ║
╚═══════════════════════════════════════════════════════╝
A real-time multiplayer 3D Air Hockey game running entirely in the browser. Built with React Three Fiber, a custom Verlet physics engine, and Yjs CRDT for conflict-free state synchronization — no game server required beyond the WebSocket signaling layer.
- 🎮 Real-time Multiplayer — low-latency synchronization via WebSockets + Yjs CRDT
- 🎱 Custom Physics Engine — Verlet integration with elastic collisions and Magnus spin effect
- 🖥️ Immersive 3D Visuals — Three.js + React Three Fiber for a futuristic arena aesthetic
- 🖱️ Mouse/Touch Controls — drag your mallet across the table; works on desktop and mobile
- 🐛 Debug Tools — toggleable trajectory prediction, hitbox visualization, and debug panel
- 🌐 Zero Game Server — state is distributed via CRDTs; only a lightweight WebSocket relay is needed
┌─────────────┐ WebSocket ┌─────────────────────┐
│ Client A │◄──────────────────►│ y-websocket server │
│ (Player 1) │ │ (port 1234) │
└──────┬──────┘ └──────────┬──────────┘
│ │
│ Yjs Doc (CRDT) │ Yjs Doc (CRDT)
│ ┌─────────────────────┐ │
│ │ Y.Map: puck │ │
│ │ Y.Map: player1Mallet│ │
│ │ Y.Map: player2Mallet│ │
│ │ Y.Map: score │ │
│ └─────────────────────┘ │
│ │
▼ ┌─────┴──────┐
┌─────────────┐ │ Client B │
│ Three.js / │ │ (Player 2) │
│ R3F Scene │ └────────────┘
│ useFrame() │
│ Physics tick│
└─────────────┘
Data flow: Each client runs the physics simulation locally. The puck position and mallet positions are synced via Yjs shared maps over WebSocket. Yjs handles merge conflicts automatically via CRDT semantics — no custom conflict resolution needed.
The physics simulation runs on a fixed-timestep Verlet integration loop inside useFrame:
position_next = position + (position - position_prev) + acceleration * dt²
Verlet is chosen over Euler for its superior energy conservation — the puck doesn't lose speed artificially on longer frames.
Puck-to-mallet and puck-to-wall collisions are resolved with restitution coefficient e = 1.0 (perfectly elastic), preserving kinetic energy and reflecting the velocity vector correctly.
When a mallet strikes the puck off-center, angular velocity is imparted. The Magnus force is then applied each tick:
F_magnus = ω × v_puck * magnus_coefficient
This causes curved trajectories after spin shots — the same physics that makes real air hockey skill-based.
| Layer | Tech |
|---|---|
| 3D Rendering | Three.js + React Three Fiber |
| State Sync | Yjs (CRDT) + y-websocket |
| Frontend | React 19 + Vite |
| Physics | Custom Verlet engine |
| Styling | CSS Modules |
- Node.js 16+
- npm
git clone https://github.com/yomero243/collaborative-3d-app.git
cd collaborative-3d-app
npm install
npm startThis starts both:
- WebSocket server on
ws://localhost:1234 - Vite dev client on
http://localhost:5173
Open two browser tabs (or two machines on the same network) to play.
To run only the client:
npm run dev(requires an external WebSocket relay)
| Input | Action |
|---|---|
| Mouse drag / Touch | Move mallet |
P |
Toggle trajectory prediction |
D |
Toggle debug panel |
H |
Toggle hitbox visualization |
Built by Gabriel — Creative 3D Developer specializing in real-time multiplayer experiences, physics simulations, and WebGL/WebGPU rendering.
💼 Available for freelance projects — multiplayer 3D apps, interactive web experiences, real-time collaboration tools, WebGL/WebGPU, and creative coding. Let's connect →