Skip to content

Non-deterministic render: sub-compositions double-mount when video metadata loads after capture starts (timeline_rebind_after_media_metadata) #2550

Description

@jurczykpawel

Summary

When a composition mounts data-composition-src sub-compositions over a full-length <video>, the runtime can re-run the montage after the video's loadedmetadata arrives (diagnostic timeline_rebind_after_media_metadata). If that happens after frame capture has started, every sub-composition is mounted a second time: the data-composition-id collision is uniquified to …-1 (+ data-hf-original-composition-id), the renamed instance's scoped GSAP selectors stop matching, and captured frames composite two phase-offset copies of the block. The same project renders correctly when the metadata wins the race — so the output is non-deterministic across environments.

Evidence — same project, same CLI, different environment

Identical project directory (index.html + sub-compositions + local mp4/wav), rendered with stock npx hyperframes@0.7.60 render, frame at t=14s inside a 3 s flowchart-vertical window:

macOS arm64 native (~34 s render) Docker linux/amd64, same files (~5 min render)
clean duplicated
ONE instance TWO phase-offset instances

Removing the <video> (changing nothing else) renders a single clean instance in both environments — the media-metadata event is the trigger.

Mechanism (as observed via runtime diagnostics)

On loadedmetadata/durationchange the runtime compares the captured root-timeline duration g with the media duration floor h and, when V(h) && (!V(g) || h >= g + T), emits timeline_rebind_after_media_metadata and re-runs the montage:

  • metadata before capture (fast env): the !e.capturedTimeline early path re-binds before anything is mounted — harmless;
  • metadata after capture starts (slow env — big DOM, large media, slow I/O): the guarded rebind re-mounts every data-composition-src host mid-capture → duplicate instance → id uniquify breaks one instance's selectors → both get composited at different timeline phases.

Which blocks make it visible

The re-mount hits every sub-composition, but visibility depends on the block: a block whose internal timeline far exceeds its window and whose animation targets scoped-id selectors (e.g. registry flowchart-vertical: 12 s timeline in a 3 s window) shows two drifting copies; blocks with timeline ≈ window duplicate "invisibly" (the twin overlaps the original). Light demo projects don't reproduce at all — metadata always beats capture — which makes this easy to miss in small tests.

Expected

Render output should be deterministic regardless of media-load timing. Candidate directions: wait for all media metadata before starting capture; freeze the montage once capture begins; or make the post-metadata rebind idempotent (skip mounting a data-composition-src host whose composition is already materialized).

Environment

  • hyperframes@0.7.60, mp4 render, captureMode beginframe in both environments
  • Reproduces deterministically in the slow environment (10/10 renders), never natively on the same machine

Full public repro: https://github.com/jurczykpawel/hyperframes-double-mount-repro (same composition with generated media — reproduces identically). Happy to test candidate fixes and report back.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions