A multiplayer chess game with FPS battle mode for captures, now using WebRTC peer-to-peer connections for LAN gameplay!
- Modern web browser (Chrome, Firefox, Safari, Edge)
- Node.js (for running Vite dev server)
- Both players on the same LAN (Wi-Fi or Ethernet)
npm installnpm run devThis starts Vite on http://localhost:5173 (or similar port).
Player 1 (Host):
- Open the game in your browser
- Click "Host Game (White)"
- Your local IP addresses will be displayed (e.g.,
192.168.1.100) - Share one of these IPs with the other player
- Wait for guest to connect
You'll see something like:
๐ Your Local IPs:
192.168.1.100 โญ (en0)
10.0.0.5 (eth0)
The star โญ indicates your primary network interface (usually Wi-Fi).
Player 2 (Guest):
- Open the game in your browser (can be on a different device)
- Enter the host's IP address in the input field
- Click "Join Game (Black)"
- Wait for connection to establish
Once connected:
- White (Host) moves first
- Click pieces to select and move (standard chess rules)
- Captures trigger FPS battles!
- WASD to move
- Mouse to aim
- Click to shoot
- Win the battle to win the square
- Default mode is LAN host candidates (
iceTransportPolicy: "all") with no TURN required. Ensure both players are on the same subnet and can reach port 8080 on the host. - If you previously set a TURN override in
localStorage.turn_config, clear it for LAN play:localStorage.removeItem('turn_config'); location.reload();
- If you do need TURN later, set
localStorage.turn_configorwindow.TURN_CONFIGbefore loading the page:Replace with your TURN host/IP/creds and open/forward the TURN ports if youโre not on LAN.localStorage.setItem('turn_config', JSON.stringify({ iceServers: [ { urls: ['turn:YOUR_TURN_IP:3478?transport=udp', 'turn:YOUR_TURN_IP:3478?transport=tcp'], username: 'user', credential: 'pass' } ], iceTransportPolicy: 'relay' })); location.reload();
- Host must allow inbound TCP 8080 (signaling) on the host machine.
- Browser must be allowed through OS firewall.
- TURN not needed on LAN; leave it unset unless youโre crossing networks.
- WASD: Move camera
- Space/Shift: Up/Down
- Mouse: Look around
- Click: Select/move pieces (when it's your turn)
- WASD: Move in arena
- Mouse: Aim
- Click: Shoot
- Health: 100 HP (20 damage per hit)
1. HOST STARTUP:
โโ Start HTTP signaling server (port 8080)
โโ Display local IP addresses
โโ Create WebRTC offer
โโ Wait for guest
2. GUEST CONNECTION:
โโ Enter host IP
โโ Fetch SDP offer via HTTP
โโ Create WebRTC answer
โโ Exchange ICE candidates
3. P2P ESTABLISHED:
โโ Direct data channel connection
โโ HTTP signaling server shuts down
โโ All game data flows peer-to-peer
- Host (White): Game state authority, validates all moves
- Guest (Black): Sends move requests, receives authoritative state
- Battle Mode: Distributed authority (each player calculates own damage)
-
"game-state" (Reliable, Ordered)
- Chess moves
- Battle start/end
- Game state updates
-
"battle-updates" (Unreliable, Unordered)
- Real-time position updates (50ms interval)
- Shooting/health data
- Lower latency for smooth FPS gameplay
Causes:
- Host hasn't started yet
- Wrong IP address entered
- Firewall blocking port 8080
- Not on same network
Solutions:
- Verify both devices on same Wi-Fi/ethernet
- Double-check IP address is correct
- Try pinging host IP:
ping 192.168.1.100 - Disable firewall temporarily or allow browser through firewall
macOS:
System Preferences โ Security & Privacy โ Firewall
โ Firewall Options โ Allow your browser
Windows:
Windows Defender Firewall โ Allow an app
โ Add your browser (Chrome/Firefox/etc)
Linux:
sudo ufw allow 8080/tcp- Check you're connected to a network (Wi-Fi or ethernet)
- Try restarting the browser
- Check network adapter is enabled
- Ensure strong Wi-Fi signal (or use ethernet)
- Close bandwidth-heavy apps (streaming, downloads)
- Reduce distance between devices on LAN
- Check for network interference
If board states differ between players:
- Host periodically sends full state sync (every 5 seconds)
- Guest always accepts host's state as truth
- Connection drops will require reconnection
You can test with two browser windows on the same machine:
- Open first window: Click "Host Game"
- Note the IP (will show
127.0.0.1or local IP) - Open second window (or incognito): Enter
localhostor127.0.0.1 - Click "Join Game"
fps-chess/
โโโ main.js # Client game logic & UI
โโโ chess.js # Pure chess game rules
โโโ battle.js # FPS battle arena system
โโโ network-manager.js # High-level networking API
โโโ webrtc-connection.js # WebRTC peer connection wrapper
โโโ signaling-server.js # HTTP signaling for connection setup
โโโ package.json # Dependencies
โโโ index.html # Entry point
โโโ README.md # This file
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' }
]STUN servers help with NAT traversal, even on LANs.
All messages are JSON over WebRTC Data Channels:
// Guest sends move request
{ type: 'moveRequest', fromRow: 6, fromCol: 4, toRow: 4, toCol: 4 }
// Host broadcasts game state
{ type: 'gameState', board: [[...]], currentTurn: 'black' }
// Battle update (50ms interval)
{ type: 'battleUpdate', position: {x,y,z}, rotation: {x,y,z}, shot: {...}, health: 80 }- Connection Time: < 5 seconds
- Move Latency: < 200ms
- Battle Update Rate: 20 updates/sec (50ms interval)
- Battle Latency: < 100ms
- Data Usage: ~2KB per move, ~150 bytes per battle update
- No internet gameplay (LAN only)
- No spectator mode
- No game save/resume
- No reconnection after disconnect (must restart)
- No check/checkmate detection (simplified chess rules)
- No en passant, castling, or pawn promotion
MIT License