# gibbz voice — POC Discord-clone proof of concept: peer-to-peer WebRTC voice chat + screen sharing with position-based spatial audio and pluggable STUN/TURN. Run (Node 18+, only dep is `ws`): ```sh npm install PORT=8081 npm start # open http://localhost:8081, click a voice channel, allow mic ``` ## What works - Mesh WebRTC voice (perfect negotiation; lowest peer id initiates) - Screen share with audio (mic is spatialized, screen audio plays direct) - Spatial audio: drag your avatar on the plane; remote mics run through a `PannerNode` (HRTF, inverse distance). Positions sync over signaling. - Custom STUN/TURN: gear icon → JSON override in `localStorage`, applied on next join. Server default comes from env (below). - Mute (M), deafen (D), per-peer volume, speaking indicators. ## ICE configuration (server side) | Env var | Meaning | |---|---| | `STUN_URLS` | comma-separated `stun:` urls (Google defaults) | | `TURN_URLS` | comma-separated `turn:`/`turns:` urls | | `TURN_USERNAME` / `TURN_CREDENTIAL` | static TURN creds | | `ICE_SERVERS_JSON` | full `{"iceServers":[...]}` override, wins over the above | | `PORT` / `HOST` | listen address (default 8081 / 0.0.0.0) | Example with coturn: ```sh TURN_URLS='turn:turn.example.com:3478?transport=udp,turns:turn.example.com:5349' \ TURN_USERNAME=mats TURN_CREDENTIAL=secret npm start ``` ## POC shortcuts (productionize later) - No auth, no reconnection, rooms are in-memory, signaling is unencrypted ws behind the preview ingress (use wss/TLS in front for real deploys). - No media server: N² mesh — fine for small rooms, needs SFU (mediasoup/livekit) beyond ~6–8 participants. - Text channels are decorative. No persistence, no rate limiting. - Screen-share negotiation storms on join/leave are expected at small scale.