Product Docs Pricing Devlog About
Sign In Get Started
Client-Side Prediction: A Game Dev Guide
Back to Devlog

Client-Side Prediction: A Game Dev Guide

Kenji Watanabe

Why does my game feel fine at 60ms RTT but broken at 200ms? Client-side prediction hides the delay between input and server confirmation. Without it, each action waits a full round trip before appearing on screen, making controls feel unresponsive.

The problem prediction addresses

With a basic authoritative server, the flow is: the player presses W, the input packet reaches the server, the server processes movement, the server sends the new position to every client, and the client renders it. The player waits at least one full RTT to see movement. At 100ms latency, every input carries 100ms of lag. The character pauses after each keypress, so the game feels unresponsive even when the code works correctly.

Client-side prediction removes that wait. On a W press, the client applies the expected outcome immediately, moving the character, while also sending the input to the server. Movement appears at once. When the authoritative response arrives, the server confirms it or corrects it.

The important observation is that most player-controlled actions have highly predictable results. A player on flat ground who presses forward will move forward. That prediction is nearly always right, letting the client display the result immediately while server confirmation identifies any mismatch.

The prediction and reconciliation loop

Client-side prediction revolves around a compact loop: predict locally, send the input, receive the authoritative state, then reconcile differences.

The client keeps a circular buffer containing every sent input not yet confirmed by the server. Each record includes the input, the predicted state produced by that input, and a sequence number. When a confirmed state arrives for a sequence number, the client compares its prediction at that point with the server's state.

When they agree, no visible change is needed. The prediction worked, which should be the usual outcome by far.

When they disagree, the client adopts the authoritative state and replays every still-unconfirmed input to reach the current predicted position. It does not move to the server's confirmed position, since that state is from the past. Instead, it re-simulates forward from the correction point and applies the correction to the current position.

A small mismatch causes a brief position snap or pop that feels like a minor stutter. A large one causes rubber-banding: the character jumps backward to a position far from the player's expectation. Rubber-banding is not prediction failing; it means prediction exposed the correct authoritative result. The large mismatch usually indicates major client-server divergence caused by a missed input, lag spike, or game logic the client predicted incorrectly.

What prediction covers, and what it does not

Prediction suits local movement, jump physics, and other deterministic actions unaffected by other players. Without outside interaction, you know how the player will move, so the client can simulate that outcome locally.

Interactions with other objects or players are more difficult. Where was the other player during the last 100ms? The client's displayed position for them comes from their last received state and may be stale. Their movement cannot be predicted reliably because you do not control it and receive its information late.

This creates an asymmetry: prediction keeps your character responsive, while other players can still stutter if their state is interpolated poorly. Entity interpolation, rather than prediction, addresses this separately. The client blends the last two received positions instead of displaying the latest one, creating smoother apparent movement while accepting a little extra lag for other entities.

Hit registration uses both systems. Your character occupies a predicted position when you fire, while the enemy occupies an interpolated one. The server checks the hit against its authoritative record of both positions at the relevant time. Lag compensation, which reconstructs server state when you fired, is the link between prediction and interpolation.

Where prediction breaks down

Input buffer overflow

When the server falls behind or packet loss affects the player's connection, unconfirmed inputs accumulate in the client buffer. Once confirmations arrive in bulk, reconciliation handles many frames together. Depending on the re-simulation design, this can create short freezes or major position corrections. Setting a maximum buffer depth and handling overflow gracefully avoids the worst outcomes.

Physics divergence

Different client and server physics code, or different physics tick rates, causes predicted and authoritative state to diverge over time. Small floating-point differences in physics constants also accumulate. In practice, client physics must match server physics exactly. If the server runs authoritative physics while the client predicts, both sides need to execute identical code.

Server-side effects unknown to the client

If another player lands on you and slows you down, the client predicting forward movement will not know about the collision until the server confirms it. The client shows free movement; the server reports that you slowed; reconciliation corrects the state. That correction is right, but players experience it as a stutter. Good design limits interactions the client cannot anticipate, especially in player movement. Keeping unexpected external forces in the reconciliation correction bucket instead of predicting them is usually the better tradeoff.

Building prediction with Tashi

Tashi's netcode module provides the prediction-reconciliation loop at the session level. You register an input-to-state simulation function with the SDK, which uses it for client prediction and for replaying unconfirmed inputs during reconciliation. The SDK tracks sequence numbers, stores the unconfirmed input buffer, and starts reconciliation when server confirmations arrive.

Your integration supplies the deterministic simulation function (input + current state = next state), the threshold for significant divergence that needs visible reconciliation versus a small correction that can be blended, and server-only game logic that should stay unpredicted. The simulation function is the critical piece: it must be deterministic and return identical results on client and server for identical inputs.

Client-side prediction becomes much easier to implement once the concept is clear. Each stage of the prediction-reconciliation loop has a defined job. After the loop works, tuning means deciding how strongly to correct errors versus blend them, and which inputs to predict versus wait for. Those choices affect game feel, not the architecture.