How to Reconcile Local Matter.js State with Server

In networked multiplayer physics simulations, client-side prediction keeps gameplay responsive, but network latency and floating-point non-determinism inevitably cause client and server states to drift apart. Server reconciliation solves this by treating the server as the ultimate authority, rolling back the local Matter.js physics engine to the authoritative server state when a mismatch occurs, and then reapplying all pending, unacknowledged local inputs to catch back up to the present. This article explains the exact architecture and mechanics required to implement robust server reconciliation using Matter.js.

The Prediction and History Architecture

To reconcile state deviations, the client must track history over time. Real-time inputs cannot simply be applied and discarded; they must be stamped and stored alongside state snapshots.

  1. Sequence Tagging: Every user input (such as directional impulses or torque applications) is tagged with a monotonically increasing sequenceNumber and sent to the server.
  2. Input History Buffer: The client stores these inputs in a circular buffer along with the exact frame or tick they were generated.
  3. State History Buffer: The client periodically captures physical snapshots of its Matter.js bodies (position, angle, linear velocity, and angular velocity) indexed by input sequence number.

Detecting State Desynchronization

The server processes incoming inputs sequentially, updates its authoritative Matter.js instance, and periodically broadcasts an authoritative snapshot back to the client. This snapshot contains:

Upon receiving this payload, the client searches its history buffer for the local state snapshot matching lastProcessedInput. It calculates the error vector:

\[\text{Error} = |\vec{P}_{\text{local}} - \vec{P}_{\text{server}}|\]

If the deviation exceeds a defined threshold (for instance, a few pixels or units to prevent unnecessary resets due to harmless floating-point noise), a reconciliation is triggered.

The Reconciliation and Resimulation Loop

When reconciliation is necessary, the client must fast-forward the physics simulation from the server's confirmed state to the current local frame.

1. Reset State to Authority

Directly override the target body's physical properties in Matter.js using the server snapshot values:

Matter.Body.setPosition(clientBody, serverState.position);
Matter.Body.setAngle(clientBody, serverState.angle);
Matter.Body.setVelocity(clientBody, serverState.velocity);
Matter.Body.setAngularVelocity(clientBody, serverState.angularVelocity);

2. Discard Acknowledged Inputs

Remove all inputs from the input history buffer whose sequenceNumber is less than or equal to lastProcessedInput, as the server has already executed them.

3. Re-simulate Remaining Inputs

Loop through all remaining unacknowledged inputs in chronological order. For each input:

for (const input of pendingInputs) {
    applyInputForces(clientBody, input);
    Matter.Engine.update(engine, FIXED_TIMESTEP);
}

Once the loop completes, the client body has recovered the divergence and sits at the correct, re-predicted present state.

Mitigating Visual Popping with Error Smoothing

Instantaneously snapping the physics body to the reconciled state produces visible stutter or "popping." To maintain visual continuity while strictly reconciling the underlying physics:

  1. Calculate the spatial difference between the old (erroneous) client position and the newly reconciled position: \[\vec{O}_{\text{error}} = \vec{P}_{\text{pre-reconciliation}} - \vec{P}_{\text{post-reconciliation}}\]
  2. Decouple the Matter.js rigid body from the rendering layer. Keep the rigid body at the true, simulated physics position, but render the visual sprite at: \[\vec{P}_{\text{render}} = \vec{P}_{\text{body}} + \vec{O}_{\text{error}}\]
  3. Exponentially decay \(\vec{O}_{\text{error}}\) to zero over a short window (e.g., 50–100ms) using linear interpolation: \[\vec{O}_{\text{error}} \leftarrow \vec{O}_{\text{error}} \times (1 - \lambda \cdot \Delta t)\]

This technique conceals physical corrections without compromising the deterministic replay required for authoritative synchronization.