matsandClaude Sonnet 5 5836424baf
Build and Publish / Metadata (push) Successful in 2s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 26s
Force TURN relay on WebRTC retry — works around same-LAN NAT/isolation issues
A connection failing specifically when both peers are on the same LAN
(as opposed to over the open internet) usually means direct/STUN
candidates are being silently dropped by the router — either AP/router
client isolation (common on home/guest Wi-Fi) or no NAT hairpin/
loopback support for two LAN devices reaching each other via their own
public IP.

On the first retry after a connection failure, set
iceTransportPolicy: 'relay' so the rebuilt RTCPeerConnection only offers
TURN-relayed candidates, bypassing the LAN path entirely. Confirmed the
coturn secret and pod are up in-cluster so relay auth will actually
succeed. Only one side needs to do this — since it only offers relay
candidates, whatever pairs with them is forced through the relay
regardless of what the other side additionally tried.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 12:10:12 +02:00

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):

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:

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.
S
Description
No description provided
Readme
38 MiB
0 Stars 1 Watchers 0 Forks
Languages
JavaScript 75.1%
CSS 16.2%
HTML 7.5%
Makefile 0.9%
Dockerfile 0.3%