Repository navigation
fix(core): capture blur strobing, backdrop box, glow drop-out and callback motion - #6106
Conversation
…exactly A fast move strobed at the default 16 taps (8 px steps on a 1560 px whip). With no data-motion-blur-samples set, taps are now about one per 2 px of travel (2 to 128); an explicit samples count still wins. Canvas quantizes image globalAlpha to 1/255, so 1/n weights left coverage short (n = 104 summed to 0.81, a lighter card body). Each tap now carries a whole number of 1/255 units that sum to exactly 255. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…layer The crop came from a capture of the whole scene, so the backdrop under a see-through mover smeared with it (a lighter box with the dotted background erased) and a glow or shadow outside the mover's box was cut off, leaving a hard-edged box. Each frame now captures the movers alone, everything else visibility:hidden, so a mover's pixels carry their true alpha and no backdrop; the background capture is unchanged. The crop grows by the mover's ink reach (box-shadow, text-shadow, filter, outline, measured during the scan), so glows travel with it. Two paints per frame, as before. The output is cleared on wrap instead of showing the previous wrap's last frame, since the first paint is no longer a drawable sharp frame. Core's unpacked size budget goes to 3,950,000 for the new module. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The mover scan seeked with callbacks suppressed even where the root allows them, so a composition whose motion lives in a timeline callback (a render(t) driven from onUpdate) reported 0 movers and rendered sharp. With data-motion-blur-events="on" the scan now seeks the way the frame reads do. Without it nothing changes: callbacks stay suppressed. The no-movers finding in hyperframes check now names the attribute. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Edit accuracy: accurate 271 (base branch 271), smooth 159 of thoseThe gate passes. Quarantined, measured but not gated (0) |
There was a problem hiding this comment.
Verdict: APPROVE · 0 blocker · 1 important · 0 nits · aabbb1d5
Tap weights check out (whole 1/255 units summing to 255 for every n ≤ 255; taps cap at 128). The visible-root isolation keeps rotated and scaled movers in place. One gap in the isolation CSS, see the inline comment.
Scope
Read index.ts, composite.ts, ink.ts, discover.ts and pose.ts end to end. Reproduced findings in chrome-headless-shell 152.0.7977.30 with the built runtime, comparing main (f6cbf87) with this head. CI is green.
Not flagged: a static element stacked above a mover is now covered by the trail more than on main (bar pixel 70,0,185 on main, 168,0,87 here). The docs in #6108 list this as a known capture limit.
— Genesis (pr-review)
| `[${ONLY_ATTR}]::before, [${ONLY_ATTR}]::after, [${ONLY_ATTR}] * { visibility: hidden !important; }`, | ||
| `[${ONLY_ATTR}] [${MOVER_ATTR}], [${ONLY_ATTR}] [${MOVER_ATTR}] * { visibility: visible !important; }`, |
There was a problem hiding this comment.
important: [ONLY] [MOVER] * { visibility: visible !important } un-hides anything inside a mover that the author hid with visibility: hidden, so it gets cropped and smeared into the trail. Repro (Chrome 152, built runtime): a red box mover with a visibility:hidden green child gives 0 green px in the sharp frame and 80 green px in the blurred frame (main: 0). This persists through #6108 for anything still on capture.
Keep the hide rule off the mover subtree and let the mover's descendants inherit. With this change the repro gives 0 green px, and the root rule is untouched:
| `[${ONLY_ATTR}]::before, [${ONLY_ATTR}]::after, [${ONLY_ATTR}] * { visibility: hidden !important; }`, | |
| `[${ONLY_ATTR}] [${MOVER_ATTR}], [${ONLY_ATTR}] [${MOVER_ATTR}] * { visibility: visible !important; }`, | |
| `[${ONLY_ATTR}]::before, [${ONLY_ATTR}]::after, [${ONLY_ATTR}] *:not([${MOVER_ATTR}], [${MOVER_ATTR}] *) { visibility: hidden !important; }`, | |
| `[${ONLY_ATTR}] [${MOVER_ATTR}] { visibility: visible !important; }`, |
The isolation CSS re-showed every descendant of a mover, so a child the author hid with visibility:hidden was cropped and smeared into the trail (240 green px in the repro; 0 in the sharp frame). The hide rule now skips the mover subtree and the mover alone is made visible, so its descendants inherit and keep their own visibility. Found by review of #6106. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The isolation CSS re-showed every descendant of a mover, so a child the author hid with visibility:hidden was cropped and smeared into the trail (240 green px in the repro; 0 in the sharp frame). The hide rule now skips the mover subtree and the mover alone is made visible, so its descendants inherit and keep their own visibility. Found by review of #6106. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
What
Four fixes to capture motion blur, found by rendering Jake's eight comparison shots ("Motion blur, two ways") on the merged code and fixing each place it lost.
data-motion-blur-samples, taps are now sized to the smear (about one per 2 px, 2 to 128); an explicit count still wins.globalAlphato 1/255 when drawing an image, so 1/n tap weights left coverage short (n = 104 summed to 0.81). Weights are now whole 1/255 units that sum to exactly 255. This also affected the old thinned and explicit counts.visibility:hidden), and the crop grows by the mover's ink reach (box-shadow, text-shadow, filter, outline, measured during the scan).render(t)driven fromonUpdatereported 0 movers even withdata-motion-blur-events="on". With events on, the scan now seeks the way frame reads do. Without it nothing changes.Notes for review
visibility:hidden. Translation is fine. The isolation therefore keeps the root visible (background, border, shadow and outline cleared) and hides everything beneath it except the movers. There is a real-browser test for rotate and scale that fails if the root is hidden again.failedFrames) rather than a half frame.-events="on"it fires timeline callbacks. Authors who opt into events already get callbacks at arbitrary sub-frame times.ink.tsmodule (a later PR in this stack raises it again for the filter tier).Evidence
Rendered Jake's eight shots before and after (frames named in his notes):
render(t)frame 13: 1 mover found (was 0) with events on.Tests
Stack: this is the bottom of three (quality fixes, then the filter fallback, then the automatic tier and defaults). Review bottom-up.
🤖 Generated with Claude Code