Modern OpenGL Programming
A practical, engine‑oriented journey into today’s programmable graphics pipeline.
OpenGL Banner
Chapter 5. Time, Ticks, and Deterministic Simulation Loops
Designing a Stable, Predictable, Real Time Update System for Games & Physics

Every real time engine — whether for games, physics simulations, or visualization — must solve one fundamental problem:

How do we advance the world forward in time in a stable, predictable way?

This article explains:

This is the foundation for the physics module, the renderer, and the entire engine runtime.

The Nature of Time in Real Time Engines

Real time engines run in a loop:

while (running) {
    process_input();
    update();
    simulate();
    render();
}

But the timing of these steps determines:

Time is not just a number — it is a design constraint.

Variable Timestep vs Fixed Timestep

There are two main approaches to advancing time.

1. Variable Timestep (Δt changes every frame)

Definition

Each frame uses the actual time elapsed since the previous frame:

float dt = time_now - time_prev;
simulate(dt);

Pros

Cons

Variable timestep is not acceptable for physics simulation.

2. Fixed Timestep (Δt is constant)

Definition

Simulation always advances in fixed increments:

const float dt = 1.0f / 60.0f; // 60 Hz simulation

Rendering may run at 144 FPS, but simulation always runs at 60 Hz.

Pros Cons

Fixed timestep is the correct choice for the GusOnGames engine.

The Accumulator Pattern (The Gold Standard)

This is the industry standard solution used by:

1. The Pattern

float accumulator = 0.0f;
float dt = 1.0f / 60.0f;

while (running) {
    float frame_time = time_now - time_prev;
    time_prev = time_now;

    accumulator += frame_time;

    while (accumulator >= dt) {
        simulate(dt);
        accumulator -= dt;
    }

    render(interpolate(accumulator / dt));
}

2. Why it works

This is the loop we will implement in the engine.
Designing the Engine’s Time Module

The time module belongs in the platform layer because:

1. Required API

namespace btg_platform {
    void start_timer();         // initialize timer
    double get_elapsed_time();  // get elapsed time in seconds since last call to start_timer or get_elapsed_time
    double get_last_elapsed_time(); // get the last elapsed time without resetting the timer
}

2. Responsibilities

3. Implementation Notes

Determinism: Why It Matters

Determinism means:

Same inputssame outputs

every time.

This is essential for:

1. Sources of Non Determinism

2. How We Guarantee Determinism

This is why the engine’s architecture is designed the way it is.

Rendering vs Simulation: Two Different Clocks

Rendering runs as fast as possible:

Simulation runs at a fixed rate:

1. Why They Must Be Separate

Rendering is visual. Simulation is logical.

Rendering can interpolate. Simulation cannot.

Rendering can skip frames. Simulation must not.

2. Interpolation

To avoid jitter:

render(interpolate(alpha));

Where:

alpha = accumulator / dt;

This blends between:

This produces smooth motion even when simulation runs slower than rendering.

The Final Engine Loop

Here is the final loop that the GusOnGames engine will use:

float accumulator = 0.0f;
const float dt = 1.0f / 60.0f;

while (window.is_open()) {
    Time::update();
    float frame_time = Time::delta();

    accumulator += frame_time;

    input.update();

    while (accumulator >= dt) {
        simulation.update(dt);
        accumulator -= dt;
    }

    float alpha = accumulator / dt;
    renderer.render(alpha);
}

This loop is:

Summary

This article defined the time system for the GusOnGames engine:

This is one of the most important architectural decisions in the entire engine.