Idle against the document's own frame gaps
Keep the timeline in step with the picture while removing the slow-document readiness hold.
Where the value travels
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> | nullProducer packages/core/src/compositionReadiness.ts:271; consumer :316 and :323. First-frame wrapper :357 supplies scope.
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.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.
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.How it says no
| Signal | Meaning and action |
|---|---|
| null | No document window, no paint input is pending. |
| AbortSignal | Shared timeout or completion cancels remaining frame requests. |
| timedOut: true | Shared 8-second wait elapsed. Player warns and continues; this is not proof of idle. |
| Generation mismatch | Old 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.
| Tolerance | Unsettled ordinary windows after nine gaps | Worst observed settlement |
|---|---|---|
| 1.1x | 2 / 183 | 500 ms among those that settled |
| 1.25x | 2 / 183 | 500 ms among those that settled |
| 1.5x | 0 / 183 | 350 ms |
| 1.75x | 0 / 183 | 233 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
| Audited | Eight 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. |
|---|---|
| Trusting | No mapped boundary relies solely on a type or comment. |
| Not exercised | Full 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
- A steady slow document already settles before the cap on current main.
- A trace shows timeline commit preceding shadow picture promotion.
- A deliberately broken stabilization rule passes steady-slow and burst/recovery witnesses.