A video feed and a state channel travel over different paths with different buffering. If you render state the instant it arrives, the UI narrates the future: the result appears before the viewer sees it happen. Fixing that is mostly about sequencing, not speed.
Two clocks, one round
Video is delayed by encoding, CDN and player buffer — typically several seconds. The WebSocket message is delayed by milliseconds. So state must be held back to the stream, not pushed ahead of it.
const STREAM_DELAY_MS = () => player.buffered.length
? (player.buffered.end(0) - player.currentTime) * 1000
: 0;
function schedule(event) {
const wait = Math.max(0, event.showAtMs - Date.now() + STREAM_DELAY_MS());
setTimeout(() => render(event), wait);
}
Server timestamps carry the intent (showAtMs); the client aligns them to its own buffer.
Sequence numbers beat timestamps
Clock skew makes wall-clock ordering unreliable. Give every state change a monotonic seq per round and drop anything you have already applied:
let applied = -1;
function onMessage(msg) {
if (msg.seq <= applied) return; // duplicate or out-of-order
if (msg.seq > applied + 1) return resync(msg.roundId); // gap -> refetch
applied = msg.seq;
schedule(msg);
}
The gap branch matters more than the duplicate branch: a missed transition leaves the UI confidently wrong, which is worse than a brief spinner.
Reconnect by snapshot, not by replay
After a disconnect, replaying the backlog re-animates events the viewer already missed. Fetch a snapshot of the current round instead, set applied to its seq, and resume the live channel from there. Backlogs are for audit logs; snapshots are for UIs.
Make the states explicit
betting_open -> betting_closed -> spinning -> resolved as an enum, with illegal transitions rejected, keeps a late message from reopening a closed round. The same discipline that protects a payments ledger protects a round.
Reference
Live-dealer formats are the demanding case, since bets close while the wheel is already moving. Looking at how a live catalogue such as Crazy Time organizes its rounds and bet windows is a practical way to see which transitions have to be strictly ordered and which can tolerate a late arrival.
Takeaway
Delay state to the stream, order by sequence rather than clock, resync from a snapshot, and forbid illegal transitions. The feed and the UI then tell the same story.
Top comments (0)