Atom Engine

Fixed Timestep Game Loop

Purpose

This module implements a fixed timestep game loop using requestAnimationFrame, with support for:

It is commonly used in games and simulations where stable physics updates are required regardless of frame rate fluctuations.


Lifecycle

Creation → Start → Update/Render Loop → Stop

  1. Creation: A loop state is created via createLoop
  2. Start: The loop begins via startLoop
  3. Update/Render Loop: Repeated tick calls manage update and render phases
  4. Stop: The loop is halted via stopLoop

Public API

LoopState

Represents the internal state of the loop.


createLoop(update, render, fixedDelta?)

Creates and returns a new loop state.

Parameters

Returns


startLoop(state)

Starts the animation loop if it is not already running.

Behavior


stopLoop(state)

Stops the loop and cancels pending animation frames.

Behavior


Internal Behavior

tick(state, time)

Core loop function executed every animation frame.

Steps:

  1. Guard Clause
    • Exits immediately if state.running is false
  2. Initialize Time
    • On first frame, sets lastTime = time
  3. Delta Calculation
    • Computes elapsed time in seconds:
      delta = (time - lastTime) / 1000
      
    • Clamps delta to a maximum of 0.25s to avoid spiral-of-death on lag spikes
  4. Accumulate Time
    • Adds delta to state.accumulator
  5. Fixed Updates
    • Runs state.update(fixedDelta) as many times as needed:
      while (accumulator >= fixedDelta)
      
    • Ensures deterministic simulation steps
  6. Interpolation Factor
    • Computes:
      alpha = accumulator / fixedDelta
      
    • Used for smooth rendering between fixed updates
  7. Render Call
    • Calls state.render(alpha)
  8. Loop Continuation
    • Schedules next frame via requestAnimationFrame

Example

const loop = createLoop(
    (dt) => {
        // physics / game logic update
        position += velocity * dt;
    },
    (alpha) => {
        // interpolation for smooth rendering
        renderSprite(position + velocity * alpha);
    }
);

startLoop(loop);

// later...
stopLoop(loop);