Two Problems, Two Transports
BingeCall needs to solve two different synchronization problems:
These have different latency requirements, so we use different transports for each.
Socket.io for Video Sync
For video sync events (play, pause, seek, heartbeat), we use a stateless Socket.io server. When you press play:
The `isSyncAction` flag is the anti-echo pattern. Without it, when you receive a "play" command and apply it, the video's `play` event fires — which would re-broadcast to everyone, creating an infinite loop.
Drift Correction via Heartbeat
Every 3 seconds, the host sends a heartbeat with the current video timestamp. Receivers compare it to their local position:
This handles cases where someone's stream buffers and falls behind without any explicit "pause to buffer" event.
Supabase Realtime for Presence
For presence (who's in the room), we use Supabase Realtime channels. The ~150ms latency here is acceptable — you don't need sub-50ms updates to know that someone joined the party.
Using Supabase for this also means the extension gets presence updates even if the Socket.io connection momentarily drops, since they're on separate transports.
WebRTC for Video Calls
Video calls use P2P WebRTC with a mesh topology (everyone connects to everyone). For rooms up to 6 people, this is simpler and lower latency than a media server. For larger rooms, we'd need to switch to an SFU — but that's a future problem.
TURN credentials are generated on-demand by an Edge Function using HMAC-SHA1, valid for 1 hour. They're never stored.