Loop architecture
FrameScheduler turns frame timestamps into fixed steps and a render decision. GameLoop connects it to a frame source and calls the host's callbacks.
A host with its own frame pump skips FrameSource and GameLoop and calls advance() itself.
Frame sources
Sources only supply timestamps. They never cap the frame rate.
| Source | Frames come from | Use for |
|---|---|---|
| RequestAnimationFrameSource | requestAnimationFrame | browsers, the default |
| AnimationLoopFrameSource | a renderer's setAnimationLoop() | renderers that own the frame pump |
| ManualFrameSource | step() and run() on a ManualClock | tests and replays |
Utilities
None of these are required by the core.
| Utility | Where it plugs in |
|---|---|
| Interpolated | push a sample after each fixedUpdate, read at(alpha) in update |
| FrameBudget | a deadline for optional work inside a frame, timed by a Clock |
| FrameRateGovernor | reads each FrameSchedule from frame, sets scheduler.maxFps |
| suspendWhenHidden | stops and restarts a GameLoop as its element leaves and enters view |
| Clock | time for FrameBudget and ManualFrameSource |
One frame
| Case | FrameSchedule | GameLoop |
|---|---|---|
First advance() | zero delta, render: true | calls update only |
| Long gap | clamped: true | emits clamp |
| Overload | panicked: true, droppedMs | emits panic |
maxFps cap | render: false | still runs fixed steps |
| Paused | effective timeScale is 0 | keeps receiving and rendering frames |
The lag policy explains why excess time is dropped.
Lifecycle
A sleeping loop stops its source until invalidate() wakes it. Pausing is a separate flag and does not change these states.
See the API section and the glossary for details.