Skip to content

perf(core): show the first frame without waiting on far scenes' fonts - #5722

Merged
miguel-heygen merged 2 commits into
perf/hold-far-scene-scriptsfrom
perf/first-frame-own-fonts
Oct 18, 2026
Merged

miguel-heygen merged 2 commits into
perf/hold-far-scene-scriptsfrom
perf/first-frame-own-fonts

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

A preview's first frame no longer waits on web fonts that only far scenes use. The scripts that run before the first frame wait for the faces their own text uses. Everything else waits for every font, as before:

  • the root's scripts and the scenes within 10 s run first
  • far scenes that must run in document order (eager) run right after the runtime boots
  • held scenes run when the playhead nears them

Renders and thumbnail captures are unchanged: with no preview mark nothing is held, and every script waits for every font at load.

Stacked on #5719.

Why

The after-fonts gate waited for document.fonts.ready, which covers every face any laid-out text uses, hidden far scenes included. In a 9-minute film, two CJK faces of 18 MB and 25 MB are used only by scenes from 0:30 on. One of them took 5.5 s to download, so the first frame waited about 5 s for text nobody sees yet.

How

  • waitForFonts(families) waits only for the loading faces of the given families. It uses the same 5 s cap and the same diagnostic.
  • bootFontFamilies() (heldScenes.ts) collects the font families of the text outside the scenes that wait. It walks the DOM and skips their subtrees.
  • holdFarSceneScripts now also holds an eager scene that starts past the lead. It runs right after boot, through perf(core): preserve boot dependencies while holding distant scenes #5719's mount path, after the full font wait, in document order. Two kinds of scene still run at boot: one that a root script selects into (its eager reason starts with REACHED_BY_ROOT_REASON, now one constant shared by the compiler and the runtime), and a caption scene.
  • Preview and export stay identical per script: every script still runs after the fonts its own scene's text uses. Near scenes wait for theirs; every other script waits for all fonts, as in a render.

Test plan

  • Unit tests in heldScenes.test.ts: the near scene's face loads and the far eager scene's face hangs. Boot finishes having run only the near scenes and the root-reached one, and the eager scene runs once its face loads. The existing tests are updated for the eager scene now running after boot. 3 runs in a row, with entry.afterFonts and init.swapScenes (157 tests).

  • Fails without the fix. Each break was checked separately:

    • Boot waiting for all fonts again: the runtime never boots while the far face loads (expected undefined to be defined).
    • The after-boot mount removed: the eager scene never runs (expected 'near reached ' to be 'near reached tied ').
  • Core: 211 test files, 4716 tests pass. Typecheck, oxlint, oxfmt, the comment ratchet and comment citations, and package cycles pass.

  • Manual: the 9-minute film in hyperframes preview, headless Chrome driving Studio, same machine, cache cleared.

    Before (perf(core): preserve boot dependencies while holding distant scenes #5719 alone, 3 runs) After (3 runs)
    Preview ready 6.9 to 7.2 s 2.3 to 4.5 s
    First frame 7.0 to 7.3 s 2.5 to 5.5 s

    Two after-runs paint the first frame 1 to 2 s after ready, and their longest task is 0.88 s, because the far eager scenes' scripts now run right after boot. Median seek was 0.95 to 0.97 s in two runs and 2.05 s in one. Every seek lands on its frame, and playback frame rates are within noise.

  • Comments follow CONTRIBUTING.md "Comments"

@miguel-heygen
miguel-heygen force-pushed the perf/first-frame-own-fonts branch from c9db90c to 525d817 Compare October 17, 2026 18:19

@terencecho terencecho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes — one introduced P2

Reviewed HEAD 525d817b0d462ba2969900c08a03df2a56d32af0 against effective BASE 2ff895a6945a758103c0a645b25664d0f3e41d27.

The new boot/post-boot split can defer a provider that a boot-time reader needs: the compiler preserves the first eager reason, while the runtime now interprets that diagnostic string as scheduling priority. The inline comment gives a compiler-produced, provider-before-reader counterexample whose visible result differs from BASE. The follow-up repairs newly marked providers, but not providers already eager for another reason.

Source-only review: all seven changed BASE files read in full before all seven changed HEAD files (13,545 lines total), production compiler/preview/runner integration traced, both independent review reports considered, and all 21 unordered interactions among seven behavioral groups audited. No earlier-pin verdict transferred; pre-existing #5719 findings are not refiled here.

No repository/dependency code, browser, tests, builds, native/media tools, or CI was executed or queried. The ordering witness is source-derived, not an executed runtime reproduction; author performance/test claims remain unverified.

— Review by tai (pr-review)

return { host, start, end: start + duration, scripts: [] };
const eager = host.getAttribute(SCENE_EAGER_ATTR);
if (eager == null) return { host, start, end: start + duration, scripts: [] };
if (eager.startsWith(REACHED_BY_ROOT_REASON) || eager.endsWith(READ_BY_SCENE_REASON)) return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep already-eager dependency providers in the boot batch

keepScenesReadAtBootEager still skips any provider already carrying SCENE_EAGER_ATTR, so the absence of these two reason strings does not mean the scene is safe to move after boot. A valid compiler-produced example has host/script order c, b, a, starts 40s, 30s, 0s, and distinct paused timelines: b reads window.__timelines.c during initialization, and a initializes its visible label from whether window.__timelines.b exists. The compiler emits c=b reads it, b=its script names c, a=its script names b. BASE runs c,b,a, so a displays ready; HEAD keeps c,a in the boot batch and queues b afterward, so a displays missing even with every font already ready. Mounting b does not rerun a's initialization.

The follow-up protects a newly marked provider but misses this already-eager case. Preserve boot-required dependency priority independently of the first diagnostic reason (or promote it safely), and cover the compiler-produced c,b,a chain rather than only a manually stamped a reads it host. This is a source-traced ordering counterexample, not an executed browser reproduction.

@miguel-heygen miguel-heygen reopened this Oct 18, 2026
@miguel-heygen
miguel-heygen merged commit 525d817 into perf/hold-far-scene-scripts Oct 18, 2026
28 checks passed
@miguel-heygen
miguel-heygen deleted the perf/first-frame-own-fonts branch October 18, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants