Skip to content

fix(core): a mover holding a fixed child stays sharp in the filter tier - #7292

Merged
vanceingalls merged 2 commits into
mainfrom
fix/blur-fixed-child-filter
Nov 6, 2026
Merged

vanceingalls merged 2 commits into
mainfrom
fix/blur-fixed-child-filter

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

What

In the filter tier, a mover that holds a position: fixed descendant is now left sharp instead of blurred. The filter tier puts a CSS filter on the mover, which makes the mover the containing block of its fixed children, so a badge that should stay put jumps into the mover (sweep defect F: 3495 px differ from the 32-sample oracle for the filter tier against 721 for capture, for a fixed child of a mover that moves via left). Sharp is correct; a jumped badge is wrong, and nothing done in place can avoid the jump. The other movers still blur, and the capture tier is unchanged.

holdsFixedChild(el) walks the mover's descendants (capped at CONTENT_NODE_LIMIT, 256, now exported from frames.ts, the same cap grownBox uses) and reads each computed position. It runs once per scan in discover, not per frame.

Why now: #7288 makes auto draw with the filter tier, which would put this defect on the default path (before that change auto kept a left-moving mover on capture). This PR is independent of #7288 and fixes the same defect for anyone who already forces data-motion-blur-tier="filter" or hits the filter fallback.

Tests

  • index.test.ts: leaves a mover with a fixed descendant out of the filter tier and blurs the others: two movers, one with a position: fixed child; report().moverNames lists only the other and the dropped mover gets no filter style.
  • The sweep case fixed-child-left (filter tier) loses its bug field and S.F_FILTER and now passes as an ordinary outside-the-trail check. Its old region included the child's true spot, which is outside every trail, so it still fails if the child jumps.
  • With only the filter line removed, the new unit test and the un-pinned sweep case both fail. Core captureMotionBlur 213 pass; engine sweep 17 pass on Chrome 153.

Update after review

Genesis found that dropping every mover that holds a fixed child also stopped blurring transform-moved movers, where the filter does nothing to the fixed child (any transform, translate, rotate, scale or perspective other than none already makes the mover its containing block). Fixed in 32368adeb: discover only flags the movers that hold a fixed descendant, and plansAt leaves a flagged mover sharp on a frame only when it is not already a containing block at that frame's own pose (computed transform/translate/rotate/scale/perspective all none, no will-change of transform/perspective/filter, no contain of paint/layout/strict/content). A GSAP-x card with a fixed badge keeps its blur. report().movers and moverNames stay the scan's full list, including movers held sharp on a frame.

Tests added: the left-moved holder stays sharp, a transform mover with a fixed child still gets url(#hf-mb-, and a mover whose transform is none at one frame and set at the next is sharp then blurred. Known limit: an author-set filter on a flagged mover is not counted as a containing block (computed filter cannot be told apart from our own smear), so such a mover stays sharp.

Limits

  • A mover with a fixed descendant is fully sharp in the filter tier; only capture can blur it.
  • A fixed descendant deeper than the 256-node cap is not detected.
  • hyperframes check says motion_blur_no_movers ("nothing moves fast enough") when every mover is dropped this way; a follow-up would report which movers were left sharp and why.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 120 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

@heygengenesis heygengenesis Bot 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.

Verdict: REQUEST CHANGES · 1 blocker · 0 important · 0 nits · head 90528fb6

The fix is right for the left mover, but it also drops movers that auto sends to the filter tier today, where they already blur correctly. ↓ inline

Scope

Read the full diff, filterTier.ts, discover.ts (scanMovers), and the auto hand-off in index.ts (handOff, translationOnly). Probed headless Chromium 1237: a fixed child of a mover with transform: translate(40px,0) is at 250,110 with or without filter: blur(1px), while a fixed child of a left mover moves from 10,10 to 210,310 once the filter is added. At review time CI, PR captures, preview-regression and the Player perf/audio runs were green; CodeQL, regression, Windows render verification and Player Safari perf were still running.

vanceingalls and others added 2 commits November 5, 2026 20:22
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ready a containing block

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@vanceingalls
vanceingalls force-pushed the fix/blur-fixed-child-filter branch from 90528fb to 1d4d398 Compare November 6, 2026 04:23

@heygengenesis heygengenesis Bot 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.

Verdict: APPROVE — 1 resolved, 0 open, 0 new · 90528fb6 → 1d4d3985

Scope

Re-read filterTier.ts at head: the containing-block check runs after readMoverFrames restores frame t, so it sees the frame's own pose and not the temporary UNTRANSFORMED read style. Core captureMotionBlur 215/215 pass locally. CI, regression, preview-regression, CodeQL, Player perf/Safari audio are green; Windows render verification, Player Safari perf and a duplicate CI run were still running at review time.

@vanceingalls
vanceingalls added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit b6f2bec Nov 6, 2026
144 of 145 checks passed
@vanceingalls
vanceingalls deleted the fix/blur-fixed-child-filter branch November 6, 2026 05:25
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…view

Adopts heygen-com#7294 (line_unread findings carry the measured frames), heygen-com#7271
(blur mutes discrete callbacks instead of undoing them), heygen-com#7293 (the
filter tier's svg keeps the last child), heygen-com#7292 (a mover holding a fixed
child stays sharp), heygen-com#7288 (auto motion blur draws with the filter tier)
and heygen-com#7298 (Shift no longer re-renders the timeline). The fork's seek
recovery in captureMotionBlur stays. Defers heygen-com#7134 (no packages/share)
and heygen-com#7295 (needs the heygen-com#7080 crop pipeline). Watermark advances to
66cbabc.

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-authored-by: Vance Ingalls <vance@heygen.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…-com#7288)

537b88d adopted heygen-com#7288, which makes auto motion blur draw with the
filter tier, but left out the engine side. Pixel tests that expect the
capture tier then failed on Windows CI. Take upstream e2bb7da's
change to captureMotionBlur.pixels.browser.test.ts, which asks those
tests for the capture tier explicitly. The other engine files that
heygen-com#7271/heygen-com#7288/heygen-com#7292/heygen-com#7293 touch (fixtures, thresholds, sweep and
differential tests) do not exist in this fork.

Co-authored-by: Vance Ingalls <vance@heygen.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant