Skip to content

feat: automatic tier, 240°/32 defaults, check finding, and Studio stops opting out - #6108

Merged
vanceingalls merged 6 commits into
mainfrom
feat/capture-blur-auto-tier
Oct 23, 2026
Merged

vanceingalls merged 6 commits into
mainfrom
feat/capture-blur-auto-tier

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

What

  1. hyperframes check reports motion_blur_filter_fallback (info) when the filter tier draws because capture could not run, with the cause and the way back. The engine's per-session log line gains tier=.
  2. Studio stops sending previewMotionBlur. Every Chrome can preview blur now (the fallback covers browsers without html-in-canvas), so a render no longer has to be switched off to match a preview that could not blur. The server still accepts false from an older Studio.
  3. Automatic tier. The scan notes whether any mover turns or scales (the linear part of its transform chain changes) and whether it already forms its own stacking context while it moves. When every mover only translates, the filter tier draws as well as capture and costs about what no blur costs. Anything that turns or scales, or moves without a transform, stays on capture. data-motion-blur-tier="capture" or "filter" forces a tier. check stays quiet for these two choices.
  4. Defaults match the shutter blur Jake built: 240° (was 180°) and at least 32 samples (more for a long smear).
  5. Docs and skill note describe the two tiers, the attribute, the measured cost and the findings.

Evidence

Cost, 8 s bench (four whips), wall time including startup, two runs each: no blur 5.3–5.9 s, Jake's filter 5.4 s, ours automatic 5.2–5.4 s, capture forced 9.8 s.

Distance from Jake's own render at his settings (PSNR, higher is closer), default settings: whip 51.0 dB (was 34.6), glow 51.2, waterfall 55.7 (was 39.9), WebGL 50.6. Spin 28.2 and crash zoom 29.4 stay apart on purpose: capture streaks along the arc or zoom where his softens the whole element.

The real-Chrome pixel tests run the shared assertions (trail shape, centroid, ink conservation, backdrop untouched, glow carried, centred box) through both tiers.

Behaviour changes to be aware of

  • New public attribute data-motion-blur-tier.
  • The automatic choice changes which tier most translation-only compositions use. It has been checked on the eight shots only; the corpus run is still to do. Blur stays opt-in (data-motion-blur="on").
  • A preview in a browser without html-in-canvas uses the filter tier, so a spin looks softer there than in the render. Force a tier for an exact match.

Rebased onto main after #6106 and #6107 merged.

No visible change

The only Studio edit removes the 8 lines in useRenderQueue.ts that sent previewMotionBlur with a render, and its test. Blur now picks its tier from the page, so nothing on screen moves.

🤖 Generated with Claude Code

@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 · 0 blocker · 1 important · 2 nits · 393e017e

chooseTier does what its doc comment says: renderer-off gives off; forced or unavailable capture gives filter when allowed. check stays quiet for the two chosen cases. The optional Comments check is red because the comment-share ratchet fails on tier.ts (8/71, base 5/51). Trim 3 comment lines before this reaches main.

— Genesis (pr-review)

const sig = linearSignature(el, scene);
const first = signatures.get(el);
if (first === undefined) signatures.set(el, sig);
else if (first !== sig) notPlain.add(el);

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.

important: the linear signature is only compared on a mover's first fast step and every 10th scan step. A turn or zoom that falls between those samples is classified as translation-only, and auto picks the filter tier, which only softens the spin. Repro (Chrome 152, built runtime, 1 s composition): a box slides from 0 to 0.2 s, then spins 360° from 0.4 to 0.6 s. The report is tier: filter, reasonCode: translation-only. Move the spin to 0.3–0.45 s so step 10 lands inside it and the report is tier: capture. Fix: compare at least the element's own transform's linear part on every step it is fast. Those steps already read its rect, and a spin in place changes it. The full ancestor chain can stay on the 10-step cadence.

/** Who should draw instead of capture: it turned itself off, or the filter tier does as well for less. */
function handOff(): CaptureDefect | null {
if (disabled) return { code: disabledCode ?? "capture-failures", reason: disabled };
if (config.tier === "auto" && reader && translationOnly && filterTierAllowed()) {

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.

nit: a translation-only hand-off scans the whole timeline twice. Capture's discover() decides translationOnly, then createFilterTier rescans from scratch, and with -events="on" every callback fires twice. Now that this is the default path for most compositions, passing the Discovery (movers, pads, dynamic) into createFilterTier would make it one scan.

| `data-motion-blur-shutter` | `240` | Shutter angle in degrees, 0–720. Larger is a longer smear. |
| `data-motion-blur-samples` | at least 32 | A fixed number of samples per frame, 2–64. Left unset, each mover gets at least 32, and about one per 2 px of smear when that is more, so a fast move never shows steps. |
| `data-motion-blur-events` | off | `on` runs timeline callbacks (`onUpdate` and similar) while blur looks for movers and at each sample. Needed when the motion is set from a callback, such as a `render(t)` function, which blur cannot otherwise see. |
| `data-motion-blur-tier` | `auto` | `capture` or `filter` forces one of the two ways blur is drawn (below). |

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.

nit: capture does not strictly force. chooseTier returns filter for tier: "capture" when capture cannot run, and a later scan defect hands off too. So the advice under Cost and limits ("Force one tier … for an exact match") does not hold in a preview browser without HTML in Canvas. Suggest wording such as "capture prefers capture and falls back to filter where it cannot run".

@vanceingalls
vanceingalls force-pushed the feat/capture-blur-filter-tier branch from 09d41cc to 8e15e47 Compare October 23, 2026 05:41
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-auto-tier branch from 393e017 to 00442fe Compare October 23, 2026 05:41

@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, 3 open, 0 new · 393e017e → b40dc365

Resolved: the Comments ratchet (2 comment lines removed from tier.ts; the check is green). Still open: the earlier inline important and 2 nits. The pixel suite passes locally on this head (16/16). Once #6107 takes the test fix, this needs a rebase and a fresh review.

— Genesis (pr-review)

@vanceingalls
vanceingalls force-pushed the feat/capture-blur-auto-tier branch from b40dc36 to 4ce56ce Compare October 23, 2026 19:34
heygengenesis[bot]
heygengenesis Bot previously approved these changes Oct 23, 2026

@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 — 0 resolved, 3 open, 0 new · b40dc365 → 4ce56cea

Rebase only: the patch matches the approved head, minus the 2 test assertions that moved down into #6107.

— Genesis (pr-review)

Base automatically changed from feat/capture-blur-filter-tier to main October 23, 2026 20:46
@vanceingalls
vanceingalls dismissed heygengenesis[bot]’s stale review October 23, 2026 20:46

The base branch was changed.

@mintlify

mintlify Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Oct 23, 2026, 8:52 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

vanceingalls and others added 5 commits October 23, 2026 13:46
… Studio stops opting out

hyperframes check reports motion_blur_filter_fallback (info) with the reason capture could not
run and the way back to it. The engine's per-session log line gains tier=, and its pixel test
covers the fallback over a WebGL page. Studio no longer sends previewMotionBlur: every Chrome
can preview blur now, through the fallback where it lacks html-in-canvas; the server still
accepts false from an older Studio.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…translates

The scan now notes whether any mover turns or scales (the linear part of its transform chain
changes) and whether it already forms a stacking context while it moves. When every mover only
translates, the filter tier draws as well as capture and costs about what no blur costs: on an
8 s bench shot a render took 7.0 s against 9.9 s with capture and 7.3 s with no blur. Anything
that turns or scales, or moves without a transform, stays on capture.

data-motion-blur-tier="capture" or "filter" forces a tier. The report says which one drew
and why (translation-only, tier-attribute); hyperframes check stays quiet for those two, which
are choices, not fallbacks. The pixel tests run the shared assertions through both tiers.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d in limits

The guide described blur turning off for WebGL, a root transform and similar. It now says the
filter tier draws instead, how the tier is chosen and forced, what each does with a turn or a
zoom, the measured cost of each, and which findings check emits. The skill note drops the
off-for-WebGL advice and the 1.7x figure.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…samples

The shutter opens 240 degrees by default (was 180) and each mover gets at least 32 taps (was
sized to the smear alone, down to 2), growing with the smear as before. These are the settings
the shutter blur Jake built uses; on the card whip the default render is now 51 dB from his
(34.6 dB at the old defaults). The guide's attribute table says so.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-auto-tier branch from 4ce56ce to 0b4c3b8 Compare October 23, 2026 20:46
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 131 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 (0)

Unstable (2)

  • seqplainkeys-none-center-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true
  • seqplainkeys-none-pct-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true

@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 — 0 resolved, 3 open, 0 new · 4ce56cea → 819dff89

Rebase onto main with the same patch, plus 819dff89, which adds the reviewed deletion of useRenderQueueMotionBlur.test.tsx to ALLOWED_DELETIONS. CI is green.

— Genesis (pr-review)

@vanceingalls
vanceingalls merged commit 79b57ab into main Oct 23, 2026
126 of 127 checks passed
@vanceingalls
vanceingalls deleted the feat/capture-blur-auto-tier branch October 23, 2026 21:20
@heygengenesis heygengenesis Bot mentioned this pull request Oct 23, 2026
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