Synchronization Architecture
Watch N Chill uses a host-driven synchronization model where the host’s player state is the source of truth, and guests continuously sync to the host’s position.Sync Model Overview
Host: Periodic Sync Checks
The host sends sync checks every 5 seconds to ensure guests stay in sync:hooks/use-video-sync.ts:199-235
The 5-second interval is a balance between:
- Responsiveness: Frequent enough to catch guests drifting out of sync
- Performance: Not so frequent as to overwhelm the server or network
When Sync Starts
Sync checks are started when a host enters a room with a video:room/[roomId]/page.tsx:131-143
Guest: Sync Update Handling
Guests receive sync updates and adjust their video position:room/[roomId]/page.tsx:96-115
Time Calculation & Drift Correction
Calculating Adjusted Time
The server sends the video time at a specific timestamp. Guests must calculate the current time accounting for network latency:hooks/use-video-sync.ts:86-91
calculateCurrentTime function in lib/video-utils.ts:
lib/video-utils.ts:40-51
- Playing Video
- Paused Video
For a playing video, the adjusted time accounts for the time elapsed since the sync event:Example:
- Server sends: currentTime = 10.0s, timestamp = 1000ms
- Guest receives after 200ms: Now = 1200ms
- Adjusted time = 10.0 + (1200 - 1000) / 1000 = 10.2s
Drift Threshold
Guests only seek if the drift exceeds 1.5 seconds:hooks/use-video-sync.ts:93-104
YouTube Player State Management
The host detects player state changes and broadcasts them:hooks/use-video-sync.ts:318-371
YouTube Player States
UNSTARTED
State: -1Video not loaded yet. Requires seek + play to start.
PLAYING
State: 1Video is actively playing. Broadcasts play event.
PAUSED
State: 2Video is paused. Broadcasts pause event.
BUFFERING
State: 3Video is loading. Detects seeks during buffering.
CUED
State: 5Video is cued but not started. Similar to UNSTARTED.
ENDED
State: 0Video finished playing.
Handling Special Cases
Buffering & Seek Detection
Seeks can occur during buffering, which must be detected and broadcast:hooks/use-video-sync.ts:355-367
Preventing Feedback Loops
Guests must not sync to their own control actions:hooks/use-video-sync.ts:79-85
Player Readiness
Sync requests are queued if the YouTube player isn’t ready yet:hooks/use-video-sync.ts:54-69
hooks/use-video-sync.ts:149-197
Initial Sync on Join
When a guest joins a room with a playing video, they receive immediate sync:Server-Side: Immediate Sync
socket/room-handler.ts:191-201
Client-Side: Initial Sync
room/[roomId]/page.tsx:153-182
Sync Flow Diagram
Performance Considerations
Network Latency
Network Latency
The
calculateCurrentTime function compensates for network delay by using timestamps:- Server includes
Date.now()in every sync event - Client calculates elapsed time since that timestamp
- Accounts for round-trip time automatically
- Local: < 10ms
- Same region: 20-50ms
- Cross-region: 100-300ms
Sync Frequency
Sync Frequency
Why 5 seconds?
- Frequent enough to catch drift from buffering or manual seeks
- Infrequent enough to avoid network/server overhead
- Balance between responsiveness and performance
- 1 second: Too many events, unnecessary load
- 10 seconds: Guests can drift too far before correction
- 5 seconds: Optimal balance
Player Ready Checks
Player Ready Checks
The player ready polling runs every 50ms for up to 10 seconds:Why polling?
- YouTube IFrame API doesn’t provide a ready callback for metadata
- Must check if
getDuration()returns valid value - 50ms interval is imperceptible to users
Troubleshooting Sync Issues
Debug Logging
The sync code includes extensive logging:Next Steps
Backend Architecture
Learn how the server handles sync events
Frontend Architecture
Explore the React hooks that power synchronization
Development Guide
Set up your environment and test sync locally
Architecture Overview
Return to the architecture overview