matsandClaude Sonnet 5 f68e77472e
Build and Publish / Metadata (push) Successful in 2s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 51s
Clamp spatial-map dragging to the room and shrink initial spawn spread
Dragging (yourself or a peer, for local-only listening position) never
clamped the node's actual on-screen position — only the *derived* room
coordinate got clamped to [0,100]. Cytoscape let the dot follow the
pointer arbitrarily far past the edge while the stored position stayed
pinned at the wall, so it visually flew off, then snapped back the
instant you released. Now every drag frame writes the clamped position
back onto the node itself, so it can't separate from its real, bounded
position. Given refDistance=4/rolloffFactor=1.4, staying inside the room
already keeps everyone's audio gain above ~18%, so this is the actual
"never drag out of hearing range" bound.

Also replaced the ':grabbed' re-query in the 100ms sync loop (which
decided whether to skip re-snapping the actively-dragged node) with an
explicitly tracked node id — strictly more correct regardless of
whether it contributed to the glitch above.

Dragging a peer is local-only (repositions how loud/where only you
hear them, never sent to anyone else) but had no feedback saying so;
added a one-time toast when you grab a peer's dot.

Also shrink the initial spawn-scatter radius from 20 to 6 room units —
at radius 20, two people could spawn diametrically opposite (40 units
apart), which was already down to ~7% audio gain before anyone said a
word.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 11:51:05 +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%