Idle against the document's own frame gaps

Keep the timeline in step with the picture while removing the slow-document readiness hold.

hyperframes · implementation audited at 23b8c9c8 · 2026-10-07 · audited 8 · trusting 0 · not exercised 2 · decisions 0
In one breath. Core owns the paint-and-idle decision; the player reports painted only after loader clearance, and Studio commits the shadow timeline when that picture becomes live.

Where the value travels

Document frame timestamps → core settlement → player assetsready → loader hidden → painted → Studio readyToShow → shadow promotion and timeline commit

The contract, per boundary

Core paint-and-idle input

The established under-50 ms path remains exact. Slower gaps qualify up to 1.5x the shortest gap observed in this wait. Two consecutive qualifying gaps settle early; continuing stalls retain the existing 1.5-second cap.

export function paintAndIdleReadinessInput(
  doc: Document,
  signal: AbortSignal,
): Promise<void> | null
Producer packages/core/src/compositionReadiness.ts:271; consumer :316 and :323. First-frame wrapper :357 supplies scope.
existsreachableread and written

Player settlement

The player calls core for an accessible document. A generation check rejects stale loads before readiness is emitted.

settleFirstFrameCompositionReadiness(doc, ({ timedOut }) => {
  if (generation !== this._assetsGeneration) return;
  if (timedOut) this._warnStuckAssets(doc);
  this._settleAssetsReady(generation);
}, { timeoutMs: ASSETS_READY_TIMEOUT_MS });
Producer/consumer packages/player/src/hyperframes-player.ts:1343; settlement :1396; painted write :1405. Loader consumer packages/player/src/shader-loader-state.ts:55.
existsreachableread and written

Studio and timeline promotion

An empty or slow timeline must not be committed ahead of its picture.

const readyToShow = firstFrameShown && !assetsLoading;
iframeRef.current = shadow;
pending.commit();
Readiness producer packages/studio/src/player/components/Player.tsx:435-456. Consumer packages/studio/src/player/hooks/useShadowPreviewReload.ts:243; gate :162; live iframe and timeline writes :182-183.
existsreachableread and written

Runtime and Studio Play callers

The same core gate serves runtime-reported first-frame readiness and Studio's full-timeline Play gate.

postRuntimeMessage({ source: "hf-preview", type: "assets-ready", timedOut });
if (generation === timelineReadyGeneration) set({ timelineReady: true });
Runtime producer packages/core/src/runtime/init.ts:3282-3285; message consumer packages/player/src/runtime-message-handler.ts:103 and generation-gated callback packages/player/src/hyperframes-player.ts:1250-1252. Studio producer/consumer packages/studio/src/player/store/readinessSlice.ts:45-46.
existsreachableread and written

How it says no

SignalMeaning and action
nullNo document window, no paint input is pending.
AbortSignalShared timeout or completion cancels remaining frame requests.
timedOut: trueShared 8-second wait elapsed. Player warns and continues; this is not proof of idle.
Generation mismatchOld load cannot mark the current picture ready.

Chosen rule after measurement

1.5x the shortest gap in the current readiness attempt

Fast gaps retain the strict < 50 ms comparison, including zero timestamps. Above that path, two consecutive relative gaps must qualify. A slower gap resets the streak. The existing paint cap, shared timeout and abort semantics remain.

ToleranceUnsettled ordinary windows after nine gapsWorst observed settlement
1.1x2 / 183500 ms among those that settled
1.25x2 / 183500 ms among those that settled
1.5x0 / 183350 ms
1.75x0 / 183233 ms

1.5x is the smallest tested tolerance that covers every ordinary window. The baseline belongs to one bounded readiness attempt and never rises during that attempt. These are overlapping nine-gap windows from nine controlled browser traces, excluding windows containing the deliberately injected burst. They are not independent trials. The injected 250 ms gap exceeds the selected threshold.

Sixfold CPU throttling, inline content and 60 ms frame work: main settles steady-slow in 1682 to 1701 ms, jittered in 1652 to 1703 ms, and burst/recovery in 1637 to 1686 ms. Final candidate at 23b8c9c8: 352 to 429 ms, 368 to 376 ms and 603 to 622 ms respectively, three runs each. Timer begins before the first workload; these wall times include that work and scheduling. Concurrent machine activity limits absolute timing claims.

Stable cadence is not proof of completed computation. The separate compute, media, loader and generation gates retain their authority.

Coverage, honestly

AuditedEight modules at implementation 23b8c9c8: core input/orchestrator, player caller and generation checks, loader callback, Studio Player, shadow promotion, runtime message producer, runtime message consumer, and Studio Play caller. Core ran in a throttled browser; existing consumer tests passed.
TrustingNo mapped boundary relies solely on a type or comment.
Not exercisedFull Studio reload and opaque-origin message delivery in a real browser. DOM-harness consumer tests cover their corresponding guards.

What would prove this page wrong