PURPOSE
Smoothed FPS sampler for HUD/diagnostic display. Informational only — does not feed quality scaling or any gameplay decision.
OWNS
- Module-local mutable
_smoothvalue (current exponentially smoothed FPS estimate). - Smoothing constant
ALPHA(0.05) controlling the EMA weight on each raw sample. - The three exported functions
tickFps,getSmoothedFps, andresetSmoothedFps.
READS FROM
- Raw per-frame FPS values supplied by the caller as the
rawFpsargument totickFps. No imports, no globals, no time source of its own.
PUSHES TO
- Returns the current smoothed value via
getSmoothedFpsto any caller that asks. Has no side effects beyond updating the module-local_smooth.
DOES NOT
- Does not sample
performance.now,requestAnimationFrame, or any clock — it only smooths whatever raw FPS the caller computes. - Does not influence quality scaling, throttling, simulation rate, or any gameplay system.
- Does not emit telemetry events or write to any store.
- Does not validate the smoothing constant or expose it to callers.
- Does not coordinate with
engine/telemetry/sampler.ts’s owntickFps— they are independent rolling-FPS estimators.
Signals
- Non-finite or non-positive
rawFpssamples are ignored:tickFpsearly-returns without mutating_smooth. This prevents NaN/Infinity from poisoning the smoothed value during a stalled or zero-delta frame. - Initial value of
_smoothis 60, so the HUD reads a sensible figure before any frame has been ticked. resetSmoothedFpsrestores_smoothto 60 — used on engine boot and on loop restart to clear any stale post-pause reading.
Entry points
tickFps(rawFps: number): void— fold a single raw FPS sample into the smoothed estimate via_smooth += (rawFps - _smooth) * ALPHA. Called fromengine/bridge.tsinside the per-frame update path with1 / rawDt.getSmoothedFps(): number— return the current smoothed value. Called fromengine/rendering/hud.tsto render the FPS line.resetSmoothedFps(): void— reset to 60. Called fromengine/core/loop.tson loop start and fromengine/bridge.tson engine reset.
Pattern notes
- Classic exponential moving average. With
ALPHA = 0.05each raw sample contributes 5% and the prior estimate retains 95%, giving roughly a 20-frame effective window. Tune by editingALPHA— there is no runtime knob. - Module-level
let _smoothis the singleton state; there is no class, no instance, and no per-scene reset beyond the explicitresetSmoothedFpscall. - Guard against bad samples lives at the boundary (
!isFinite(rawFps) || rawFps <= 0) and crashes nothing — consistent with the “defensive at boundaries, crash internally” rule. - The HUD pulls smoothed FPS lazily via
getSmoothedFpsrather than receiving a push, so reads are free and require no subscription. - Separate from
Sampler.tickFps(rawDt)inengine/telemetry/sampler.ts, which keeps its own rolling FPS buffer for telemetry payloads. Both are fed in the same bridge frame but serve different consumers.