Vestro.
▪ P—05 · Showcase / live demo

Reconcile.

A deterministic, off-thread demo of client-side prediction and server reconciliation in C++ and WebAssembly. Watch a pawn diverge under network lag — then snap back when the ack lands.

C++20 WebAssembly Emscripten Web Worker Canvas 2D
Jun 2026 Runs in your browser Live demo ↓
01 — What this is

Client-side prediction is one of those problems that looks simple until you try to ship it. The client runs the sim ahead of the server, the server runs the authoritative version, and every so often an ack arrives and the client has to decide how much of its prediction was wrong — and fix it without the player noticing.

This demo drives a C++ World controller compiled to WebAssembly. The World ticks a deterministic fixed-point simulation: client predicts, server runs authority, the channel between them loses packets, reorders them, adds jitter. The pawn you see is the client's best guess; the ghost is where the server says it is. The ribbon at the bottom is the acknowledgement history: when packets land, what state they confirmed.

This is modeled on how Unreal Engine solves client prediction and reconciliation — the same prediction-key and server-reconciliation ideas behind FPredictionKey, FScopedPredictionWindow, and Character Movement's save-and-replay — rebuilt from scratch in C++20 and compiled to WebAssembly so it runs in your browser. The centerpiece is the choice between a per-key acknowledgement model (Unreal's FReplicatedPredictionKeyMap, here FastArray) and the naive high-water-mark shortcut, and what happens to each under packet loss.

Determinism is the non-negotiable. Every value in the sim is q16.16 fixed-point — no floats in the decision path, so the same seed produces the same hashes on native and WASM. A CI gate runs a 26-scenario golden CSV through both builds and fails on a single-bit difference. The floats you see on the canvas are display-only conversions and feed nothing that matters.

What's an ack? Every input you send gets a prediction key — a little ticket. When the server has processed that input it sends back an acknowledgement (an "ack") for that key: confirmation your guess for that moment was accepted. Until the ack arrives, the client is only predicting.

FastArray (per-key): the server confirms each key by its own ticket number. If one ack is lost, only that key stays pending — every other key is unaffected. This is what Unreal's FReplicatedPredictionKeyMap does, and it survives packet loss.

HighWater (one number): the server tracks only the highest key it has confirmed and assumes everything below is fine too. Cheaper, but it lies under loss: if the ack for key N is dropped while key N+1 arrives, the high-water mark jumps past N and falsely marks it confirmed. Turn Loss up, switch to HighWater, and watch the timeline light up mis-confirmed (red).

02 — Try it

Press Run to start the simulation. The cyan dot is the client pawn (predicted), magenta is the server (authoritative). The channel controls add lag and loss between them.

reconcile / world 16.6ms · 60fps
Tick
Divergence now
Divergence avg
Divergence peak
Corrections
Replays
Issued / in-flight
Acked keys
Mis-confirmed
Mode
Configured loss
Ack rate
Press Run, then drive with WASD / arrows (or toggle the bot). client prediction · server authority · 60Hz
connecting…
  • Client — predicted
  • Server — authoritative
  • Ghost — reconcile target
  • Channel — inputs up, acks down
  • Issued
  • Inflight
  • Acked
  • Replayed
  • Mis-confirmed
Simulation controls
Seed
Mode
Ack model flip under loss to see mis-confirms

confirms each input by its own key; survives packet loss.

RTT ticks
Loss × 10 = %
Jitter ticks
Reorder × 10 = %
Duplicate × 10 = %
Speed ticks/sec
Advanced
Bandwidth bytes/tick
Live freeze or restart the sim
Timeline drag the track or slider to scrub
03 — How it works

Three roles, one deterministic tick. The client predicts, the server confirms, and the ack closes the loop.

The C++ World maintains two pawn states in the same tick: a client integrating player inputs immediately (prediction), and a server receiving those inputs over a simulated channel (authority). Every tick the gap between them is the visible effect of network latency.

When an ack arrives, the client checks which prediction keys the server confirmed. In FastArray mode each key is tracked independently — a key transitions to authoritative only when its own ack is received, mirroring Unreal's FReplicatedPredictionKeyMap semantics exactly. In HighWater mode the client keeps only the highest confirmed key and assumes everything below it is safe — which is wrong under packet loss: a dropped ack leaves a gap that the high-water mark silently bridges, mis-confirming keys that were never actually acknowledged. The ribbon visualises which inputs were acknowledged and when.

Three net modes let you compare the approaches side by side: Real (predict + reconcile), NaiveNoReconcile (predict but never correct — the rubber-band effect), and NaiveNoPredict (wait for server ack — the laggy effect). All three run the same deterministic fixed-point sim; only the reconciliation policy changes.

04 — Notes

The engine behind this is the same simulation layer I would reach for in a networked game: C++20, fixed-point arithmetic throughout, WASM boundary with embind, zero-copy typed-array transfers, and a determinism gate in CI. If you want to talk engine work, the contact link is below.