[RELEASE] A free solution to ping for server authoritative data. Introducing Mirage

Hey devs,

I just published Mirage, a small Wally package that adds client-side prediction & reconciliation for whatever custom state you stream from the server. Coins, inventory, quest progress, a boss’s health, anything. I didn’t want to rebuild my whole game loop around a full resimulation system just to make a pickup feel instant, so Mirage is built to layer on top of whatever you’re already doing, opt-in per key.

The problem it solves

At higher ping, anything gated behind a round trip feels sluggish. A coin pickup, a resource node grab, a shop purchase confirming. Because the client waits a full round trip time (client to server communication) before showing the result. Mirage lets the client show it now, and gives you a clean rollback hook for when the server says no.

How it works

  • Register the same key + applyAction on both server and client.
  • The client mutates its own Predicted copy immediately on player input.
  • The server stays fully authoritative. Validate and reject however you want, Mirage never makes anticheat decisions for you.
  • Rejected actions fire OnMispredict so you can roll back that specific visual.
  • Render:OnBlend hands you the actual value to draw, blended from Confirmed state + any still-pending predictions.

Two blend modes:

  • "Discrete" — snap immediately, correct only on rejection. Good for pickups and one-off events.
  • "Continuous" — always ease toward Confirmed over a few frames. Good for meters, health bars, positions — so corrections don’t visibly pop.

Two key scopes:

  • "Global" — one shared state broadcast to everyone (a coin grid, a world boss’s HP).
  • "PerPlayer" — private per-client state (inventory, currency, quest progress).

Zero runtime dependencies, installing it doesn’t pull in anything else.

Why use this over sAuth?

Roblox’s sAuth is a full deterministic resimulation system. You mark instances as predicted, run your game logic through BindToSimulation() inside a fixed timestep, and route input through the Input Actions system. When the server disagrees with the client, Roblox rolls back to the last confirmed frame and resimulates every frame since, physics and all — the same lockstep-rollback model fighting games use for netcode. Custom data (health, ammo, inventory) can ride along via attributes on those predicted instances, but it’s still bound to that fixed-timestep simulation loop. It’s powerful, it’s native (no Lua library overhead), and it’s the right tool when your prediction needs are tied to physics/movement. It’s also currently in beta, and adopting it means restructuring your input handling and simulation loop around Roblox’s model, whether or not the thing you actually care about (say, a coin counter) is physics-like at all.

Mirage doesn’t try to be a simulation system. It’s an opt-in-per-key library: you register one piece of state, define how an action applies to it, and Mirage predicts just that key, reconciles just that key, and lets you choose how corrections look (snap vs. ease) per key. No fixed timestep, no resimulating unrelated frames, no rearchitecting your input pipeline. It works with whatever RemoteEvent/data setup you already have.

So why pick Mirage over sAuth:

  • Your data isn’t physics-shaped — inventory counts, currency, quest flags, a shop confirming a purchase — modeling that as attributes inside a deterministic sim loop is a stretch; Mirage’s model matches the data better.
  • You want prediction on one system without adopting a beta engine-wide framework for your whole game.
  • You want built-in visual smoothing (Continuous blend) for things like a health bar or currency counter easing toward the true value, rather than a raw resimulation snap.
  • Lower integration cost — add it to one feature today without touching how the rest of your game handles input or simulation.

Where sAuth still wins: actual physics/character movement, combat that needs frame-perfect resimulation, anything where being native and engine-optimized matters more than flexibility. The honest framing is that they occupy different niches more than they compete directly — Mirage is for “make this specific bit of data feel instant,” sAuth is for “make my whole simulation feel instant and stay authoritative.”

Demo — how it actually feels at different pings

I built a test place specifically to stress-test this at real latencies instead of just trusting the theory:

Video: https://youtu.be/KPk9QT_cwOw

Test place: GitHub - aslyumm/MirageTestGame · GitHub

Links

Where it’s at right now

This is a fresh 0.1.0 release, MIT licensed, tests run against real Roblox Studio, CI-enforced lint/format. It has not seen production traffic yet, so treat it as early. If you try it in a prototype, I’d genuinely appreciate bug reports, edge cases, your custom edits with pull requests on github or “this fell over on my use case” replies here before anyone calls it battle-tested .

Happy to answer questions about the design (why Discrete vs Continuous, why per-key opt-in instead of a global system, etc.) in this thread. If you need help you can also leave a comment here but just make sure it was not already answered in the docs. Thank you for reading!

Native client-sided prediction already exists if you implement it. It seems like you’re describing server authority as if that does not exist already.

Besides that, Mirage does seem like it can do some heavy lifting when it comes to setting up predicted states for data. :3

1 Like

Thank you for the feedback! Didn’t mean to imply nothing like this exists. You’re right that people hand-roll prediction all the time, and Roblox’s own Server Authority model now does a native version of this for simulation-bound data (health, ammo, position, etc. via attributes + BindToSimulation).

What Mirage’s going for is a lighter alternative for stuff that isn’t really “simulation” in the physics sense. economy, inventory, quest flags, UI-facing counters. sAuth’s rollback works by resimulating your whole fixed-timestep loop since the last confirmed frame, which is great for movement/combat but a lot of buy-in for “does this coin pickup register instantly.” Mirage just predicts + reconciles the one key involved, with an explicit reject/rollback signal and blend modes (snap vs. ease) built for that kind of data, without asking you to restructure input handling around Input Actions or adopt the beta sim loop.

So less “this didn’t exist before” and more “here’s a smaller, opt-in way to get the reconciliation part of that pattern for data that isn’t physics.” Appreciate you pushing on the framing! I will soon update my post to include this explanation aswell

1 Like