Builds the seconds-valued monotonic clock the frame loop times itself
with.
performance.now() divided by 1000 — §7a admits no milliseconds anywhere in
this engine, and the conversion belongs at the boundary rather than in every
reader.
The source is a parameter, defaulting to globalThis, for the reason
PointerInput takes a PointerSurface and AssetManager a FetchLike:
this package names no host object it cannot be handed a stand-in for, and a
test that needs a clock it controls passes one. An application that wants a
different clock entirely — a profiler's, a fake one, a worker's — passes a
function instead (ApplicationOptions.now).
Why there is no Date.now() fallback (decision, A-1)
Date.now is banned repository-wide by an ESLint rule whose message is
"Determinism (§33): no wall clock in simulation code — inject time via
TimeState", and @fourjs/animation, @fourjs/particles, and @fourjs/motion
each state in their headers that they touch neither it nor
performance.now. The obvious fallback is therefore not available, and that
is the right answer rather than an inconvenience: Date.now is not
monotonic at all (a clock adjustment moves it backwards, which surfaces as a
negative cpuFrameTime), so it would replace "I could not measure this" with
a number that is occasionally wrong.
A host without performance gets a clock that reads NaN instead, and the
§84 durations read NaN — "not measured", the same answer the record gives
for every counter with no producer. Every browser and every Node ≥ 16 takes
the performance branch, so this is a stance about honesty in an unreachable
corner rather than a working fallback.
Why a wall clock here is not a §33 violation
§33's rule is about simulation: a fixed step must not read the wall clock,
because a replay could not reproduce it. Nothing in this module feeds a
simulation — the values are written into a diagnostics record and read by
overlays and charts, never by a system, an integrator, or a checksum. §84
asks for cpuFrameTime, which is a wall-clock measurement by definition;
measuring it with TimeState would measure the simulation's own clock and
answer a different question.
Builds the seconds-valued monotonic clock the frame loop times itself with.
performance.now()divided by 1000 — §7a admits no milliseconds anywhere in this engine, and the conversion belongs at the boundary rather than in every reader.The source is a parameter, defaulting to
globalThis, for the reasonPointerInputtakes aPointerSurfaceandAssetManageraFetchLike: this package names no host object it cannot be handed a stand-in for, and a test that needs a clock it controls passes one. An application that wants a different clock entirely — a profiler's, a fake one, a worker's — passes a function instead (ApplicationOptions.now).Why there is no
Date.now()fallback (decision, A-1)Date.nowis banned repository-wide by an ESLint rule whose message is "Determinism (§33): no wall clock in simulation code — inject time viaTimeState", and@fourjs/animation,@fourjs/particles, and@fourjs/motioneach state in their headers that they touch neither it norperformance.now. The obvious fallback is therefore not available, and that is the right answer rather than an inconvenience:Date.nowis not monotonic at all (a clock adjustment moves it backwards, which surfaces as a negativecpuFrameTime), so it would replace "I could not measure this" with a number that is occasionally wrong.A host without
performancegets a clock that readsNaNinstead, and the §84 durations readNaN— "not measured", the same answer the record gives for every counter with no producer. Every browser and every Node ≥ 16 takes theperformancebranch, so this is a stance about honesty in an unreachable corner rather than a working fallback.Why a wall clock here is not a §33 violation
§33's rule is about simulation: a fixed step must not read the wall clock, because a replay could not reproduce it. Nothing in this module feeds a simulation — the values are written into a diagnostics record and read by overlays and charts, never by a system, an integrator, or a checksum. §84 asks for
cpuFrameTime, which is a wall-clock measurement by definition; measuring it withTimeStatewould measure the simulation's own clock and answer a different question.