MonoGame Timestep Control: Fixed vs Variable and the Accumulator Pattern
MonoGame's default fixed timestep can cap your update rate, while a variable timestep introduces non-determinism. A time accumulator lets you lock updates at 60 FPS and render smoothly at any frame rate.
14 Dec 2025, 03:47 UTC

The problem with inconsistent game loops
You're building a MonoGame game and notice that your update logic runs at different speeds depending on the hardware. On a 60Hz monitor it feels smooth, on a 144Hz monitor it races ahead, and on low-refresh laptops it stutters. The culprit is the game loop's timestep behavior.
Useful takeaway
MonoGame's IsFixedTimeStep and SynchronizeWithVerticalRetrace give you clear control, but the real pattern for consistent gameplay is a time accumulator that processes fixed-step updates while rendering interpolated frames.
The default fixed timestep
MonoGame's Game class starts with IsFixedTimeStep set to true. When enabled, the Update method is clamped to a consistent interval--typically the monitor's vertical refresh rate (30 or 60 FPS). This clamps totalGameTime.ElapsedGameTime to that interval, which prevents the update loop from racing ahead on high-refresh displays but can introduce input lag or audio glitches if the frame time varies significantly.
Variable timestep and its trade-offs
Setting IsFixedTimeStep = false disables the clamp. The Update call now runs as fast as the hardware allows, and totalGameTime.ElapsedGameTime reflects the actual time since the last frame. This gives maximum frame rate but introduces non-deterministic update intervals. Game logic that multiplies movement by ElapsedGameTime without a proper accumulator can desync across platforms, and netcode or replay systems may break.
The time-accumulator pattern
The hybrid approach keeps a fixed update rate while rendering at whatever frame rate the GPU delivers. The core idea: accumulate elapsed time in a counter, process fixed-size update chunks, and render a fractionally interpolated frame between chunks.
// In your Game-derived class
const float fixedTimestep = 1f / 60f; // 60 updates per second
float accumulator = 0f;
// Constructor
public MyGame() {
IsFixedTimeStep = true; // or false, depending on your goal
SynchronizeWithVerticalRetrace = true; // caps to refresh rate when fixed is true
}
// Update method
protected override void Update(GameTime gameTime) {
accumulator += (float)gameTime.ElapsedGameTime.TotalSeconds;
while (accumulator >= fixedTimestep) {
UpdateLogic(fixedTimestep);
accumulator -= fixedTimestep;
}
base.Update(gameTime);
}
// Helper that receives a deterministic delta
void UpdateLogic(float delta) {
// Move player, simulate physics, etc.
player.Position += playerVelocity * delta;
}
Run this in your game's main project. No special permissions are needed. To verify behavior, add System.Diagnostics.Debug.WriteLine(gameTime.ElapsedGameTime) inside Update and confirm the delta stays stable as you resize the window or change display refresh rates. One risk: if a single frame takes longer than several fixed steps (a breakpoint or a hitch), the while loop can spiral--cap the accumulated time (for example, clamp accumulator to a maximum like 0.25 seconds) to avoid a death spiral of catch-up updates.
When to choose which
- Fixed timestep + accumulator: Best for games where deterministic updates matter--platformers, physics-heavy titles, local multiplayer, or anything with replays/netcode. The accumulator ensures the game state advances at a steady rate, while the renderer draws smoothly even if the display runs at 144 Hz.
- Variable timestep: Acceptable for simple games, visualizations, or experiences where frame-perfect consistency isn't required and you want to maximize available FPS without writing extra accumulation logic.
If you're unsure, start with IsFixedTimeStep = true and add the accumulator. You can always switch to variable timestep later if your game logic doesn't depend on a steady update rate.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.