createWebSocketTimeline
Keep server snapshots on a timeline for multiplayer games. Render what the server moves slightly in the past, interpolated between snapshots, and what your inputs move right away, predicted and corrected when the server confirms them.
Available only from react-websocket/timeline. It has no React dependency, and its size (about 0.85 kB min+gzip) is separate from the main entry's.
Usage
Then, once per frame:
And on input:
Annotate send's input parameter: that's what timeline.input() accepts. The guide is Real-time games. For interpolation alone, outside games, see Build live delivery tracking.
Parameters
TimelineSnapshot
Returns
WebSocketTimeline<S, I>:
What at() returns, by render time:
Behavior
- Clock. The render time is
edge - delay, whereedgeis the server time of a snapshot arriving now. Withlatency, that'sserverNow() - rtt / 2, sodelaydoesn't depend on the player's ping. Without it, or before the first pong with a time, it's estimated from the least delayed of the buffered snapshots. Your pongs and your snapshots must use the same server clock. - Never backward.
renderTime()holds still instead of going backward, for example when adelayfunction grows or the clock estimate improves. - Ordering. A snapshot whose
timeisn't after the newest one is ignored, along with itsack. - Inputs. Inputs are sent with
volatile, so they're never queued or replayed after a reconnect. Only inputs that were sent are predicted. Sequence numbers start at 1 and keep counting across reconnects andreset(), so a server that resumes sessions always sees them increase. - Reconciliation. When a snapshot's
ackconfirms inputs, they're dropped, andpredicted()replays the rest on top of the new snapshot. Up to 1000 unconfirmed inputs are kept. - Reconnects. On
open, unconfirmed inputs are dropped, since the old connection can't confirm them. The old snapshots stay visible until the new connection's first snapshot, which replaces them even if its time is earlier (a restarted server). - Doesn't connect. It listens from creation but doesn't hold the connection. Hold it with
useWebSocketStatusin the component that renders the game. - Snapshots are messages. Snapshots also reach
onMessage,lastMessageand state stores. WithuseWebSocket, that's a render per snapshot, so preferuseWebSocketStatus. - Not a React store.
at(),predicted()andrenderTime()change with time, not with notifications. Read them inrequestAnimationFrameor React Three Fiber'suseFrame, not withuseSyncExternalStore.latest()andpending()are fine there. - Errors. A
snapshotthat throws leaves the timeline unchanged, and the client re-throws the error asynchronously. - Requires TypeScript 5.4+ for the inferred message types (it uses
NoInfer).