Skip to content

Motion blur: per-frame reads fall back to running callbacks more often than they need to, and the events-on scan runs twice #6867

Open

Description

@vanceingalls

Follow-up to #6865, which made motion blur's per-frame pose reads run timeline callbacks only when the mover scan shows callbacks change something. These are the cases where that check, or the cost of making it, is more than it needs to be. None of them makes a frame wrong: each either keeps the old, slower behavior or spends extra time scanning.

The reads fall back to running callbacks when nothing moved

  • scanMovers (packages/core/src/runtime/captureMotionBlur/discover.ts) compares the boxes of every element whose inline style changed. A callback that only restyles, such as an onUpdate that fades a label's opacity, puts that element in the eventful scan's boxes and not the silent scan's. That turns callbacks back on for every read. On a timeline that also drives a stateful simulation (the case fix(core): motion blur preview no longer slows down later in the timeline #6865 fixed), the per-frame replay comes back. It could compare only boxes that move, or only movers' boxes.
  • boxesKey mixes in each element's index in its scan's own all snapshot. A callback that adds nodes before a tweened element (particles, a typewriter, a D3 join) shifts that index between the two scans, so the hashes differ although no box moved. Keying by element rather than by index fixes it.

Scan cost

  • With events on, which is Studio's Subtle/Strong default, every discovery runs discoverMovers twice. A capture-to-filter handoff (settleTier in captureMotionBlur/index.ts) builds a fresh filter tier that scans twice more. On an 8 s composition at 30 fps that is about 966 whole-composition seeks before the first blurred frame, against about 484 before fix(core): motion blur preview no longer slows down later in the timeline #6865. Passing the capture tier's Discovery and readEvents into the filter tier on handoff halves it.
  • The silent pass does all of discovery's work and throws the result away. Only its per-step boxes are used, and it keeps seeking after the first differing step has already decided. A boxes-only pass over the eventful scan's dynamic elements, which returns at the first difference, would be cheaper.

Found by an adversarial review of #6865. A real-GSAP reproduction for each is available on request.

Activity

  1. Muzzy5150 commented on Oct 31, 2026

    @Muzzy5150
    Contributor

    I’m investigating the two narrow scanMovers false positives described here: style-only callbacks and DOM insertion changing snapshot order. I plan to key geometry comparisons by stable element identity, preserve the #6865 scan/landing semantics, and add capture/filter regression coverage. I’ll coordinate here if overlapping work appears and will leave the broader scan-caching/tier-handoff ideas for separate follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions