Summary
A composition whose nested static section wrappers carry data-composition-id (but no data-composition-src) never registers a sub-composition timeline. As a result every render stalls for the full playerReadyTimeout (default 45 000 ms) at capture start, prints the "Sub-composition timelines not registered" warning, and then completes normally. We are not reporting a missing knob — the budget is configurable and works. This is a contract question plus an API improvement request.
Environment (pinned)
| component |
version |
| hyperframes CLI |
0.6.52 (npm, local node_modules/.bin/hyperframes) |
| Node.js |
24.19.0 |
| OS |
macOS 26.2 (25C56), Apple Silicon |
| Chrome (puppeteer-launched) |
152.0.7977.76 (system Chrome via PUPPETEER_EXECUTABLE_PATH) |
| ffmpeg |
8.1.2 |
Render command (topic tests add --no-browser-gpu where stated):
hyperframes render --format mp4 --quality standard --fps 30 --workers 1 --output out.mp4
Reproduction
Composition (~60 lines): root <div data-composition-id="root"> plus two nested section wrappers that carry data-composition-id ("alpha", "beta") without data-composition-src. The inline runtime registers only window.__timelines['root'] (GSAP-shaped seek/progress/duration API). A raw requestAnimationFrame() loop routes the render to screenshot capture with virtual time (as designed; lint WARN visible in logs).
Observed (4/4 default-budget runs, across GPU-default and --no-browser-gpu, small and 570-frame sizes): the render stalls for exactly playerReadyTimeout at capture start, then prints
[FrameCapture] Sub-composition timelines not registered after 45000ms: alpha, beta.
Compositions that load data asynchronously (e.g. fetch) must register window.__timelines[id] after setup completes.
and completes normally (rc=0).
Controls we ran
PRODUCER_PLAYER_READY_TIMEOUT_MS=1000 → identical warning after 1 s, render completes, end-to-end 11.9 s. The budget is configurable and the setting works.
- Same page with alpha/beta also getting
__timelines entries → no warning, no stall, end-to-end 9.2 s. The polled contract is literally "every [data-composition-id] host must register".
Questions / asks
- Is an id-tagged static section wrapper (no
data-composition-src, content inlined, driven by the root timeline) a supported shape? From the bundle it looks like the first-class sub-composition contract is data-composition-src/-file hosts that are compiled-in and self-register; bare-id sections appear to be polled by the same gate.
- If static sections are supported, could they be exempted from
pollSubCompositionTimelines (or opted out via a documented attribute) instead of costing the full player-ready budget on every render? In our production pipeline a 45 s/render penalty with a misleading "load data asynchronously" hint is the worst combination — the page is static and was never going to register.
- If they are not supported, a lint-level warning at compile time ("host X declares no source and registers no timeline; render will wait the full player-ready budget") would let producers fix this without reading
dist/cli.js.
Package hygiene: full redacted logs available on request. Evidence logs were regenerated from raw output and verified by scan (0 remaining control bytes; no consumer-project paths, scene identifiers, or internal designations).
Summary
A composition whose nested static section wrappers carry
data-composition-id(but nodata-composition-src) never registers a sub-composition timeline. As a result every render stalls for the fullplayerReadyTimeout(default 45 000 ms) at capture start, prints the "Sub-composition timelines not registered" warning, and then completes normally. We are not reporting a missing knob — the budget is configurable and works. This is a contract question plus an API improvement request.Environment (pinned)
node_modules/.bin/hyperframes)PUPPETEER_EXECUTABLE_PATH)Render command (topic tests add
--no-browser-gpuwhere stated):Reproduction
Composition (~60 lines): root
<div data-composition-id="root">plus two nested section wrappers that carrydata-composition-id("alpha", "beta") withoutdata-composition-src. The inline runtime registers onlywindow.__timelines['root'](GSAP-shaped seek/progress/duration API). A rawrequestAnimationFrame()loop routes the render to screenshot capture with virtual time (as designed; lint WARN visible in logs).Observed (4/4 default-budget runs, across GPU-default and
--no-browser-gpu, small and 570-frame sizes): the render stalls for exactlyplayerReadyTimeoutat capture start, then printsand completes normally (rc=0).
Controls we ran
PRODUCER_PLAYER_READY_TIMEOUT_MS=1000→ identical warning after 1 s, render completes, end-to-end 11.9 s. The budget is configurable and the setting works.__timelinesentries → no warning, no stall, end-to-end 9.2 s. The polled contract is literally "every[data-composition-id]host must register".Questions / asks
data-composition-src, content inlined, driven by the root timeline) a supported shape? From the bundle it looks like the first-class sub-composition contract isdata-composition-src/-filehosts that are compiled-in and self-register; bare-id sections appear to be polled by the same gate.pollSubCompositionTimelines(or opted out via a documented attribute) instead of costing the full player-ready budget on every render? In our production pipeline a 45 s/render penalty with a misleading "load data asynchronously" hint is the worst combination — the page is static and was never going to register.dist/cli.js.Package hygiene: full redacted logs available on request. Evidence logs were regenerated from raw output and verified by scan (0 remaining control bytes; no consumer-project paths, scene identifiers, or internal designations).