Screen helpers · interface

FramePacingReport

Deciding which of a render loop's frames go into a recording, and measuring what came out.

Explained in Recording and clips.

interface FramePacingReport
import type { FramePacingReport } from '@driftengine/core';

In depth

The measuring half is not diagnostics bolted on afterwards — it is half the reason this module exists. A clip that "judders" has at least three possible causes (the renderer missing its rate, the encoder starved of bitrate, the content itself stepping unevenly) and they are indistinguishable by eye. What separates them is a number: how many frames the loop produced, how many were handed to the recorder, and how far apart in wall-clock time those were.

The schedule counts animation frames, not milliseconds, and that is the whole design. A recorder stamps each frame with the instant it arrived, and the only instants a render loop can offer are its display's own — so a schedule expressed in wall-clock slots asks for moments that do not exist. It then takes the nearest frame either side, alternately early and late, and the file holds frames of uneven duration: 13.9 ms, 20.8 ms, 13.9 ms on a 144 Hz panel asked for 60 a second. Capturing every n-th frame instead makes every gap identical by construction, which is what makes the clip play evenly.

The rate that comes out is therefore the display's rate divided by a whole number — 60 from 60 Hz, 72 from 144, 60 from 240 — rather than exactly what was asked for. That is the right trade: a clip at 72 fps whose frames are evenly spaced is smooth, and one at exactly 60 whose frames are not is the defect this replaced.

Two rules the schedule follows, both learned from what a stutter looks like:

  • Never two captures for one animation frame. A duplicated frame is a frozen instant followed by a double-length step, which reads as a hitch even though nothing was dropped. Counting frames makes this true by construction.
  • After falling behind, resume — do not catch up. Firing twice in quick succession to make up a missed slot converts one gap into a lurch forward. A gap is a frame nobody notices; a lurch is the artefact everybody calls lag.

Properties

NameTypeDescription
targetFpsreadonlynumber
renderedreadonlynumberAnimation frames the loop produced while the pacer was running.
capturedreadonlynumberFrames handed to the recorder.
capturedFpsreadonlynumberFrames per second actually captured, over the whole run.
meanGapMsreadonlynumberMean wall-clock gap between captured frames, ms.
worstGapMsreadonlynumberThe worst single gap, ms — the one the eye actually notices.
slippedreadonlynumberCaptures that arrived well outside the cadence the rest of the clip holds.
More

Measured against the clip's own spacing rather than against the requested rate, because the two are allowed to differ: a display that divides to 72 fps or a loop that can only manage 40 both produce a clip whose every frame is evenly spaced, and neither is a fault. What is a fault is one frame taking half again as long as its neighbours, and that is what this counts.

stridereadonlynumberAnimation frames per capture — the schedule the clip actually ran on.
elapsedMsreadonlynumber