Skip to content

feat(producer,studio): wire capture motion blur into renders and Studio - #5969

Merged
vanceingalls merged 1 commit into
mainfrom
feat/capture-blur-defects
Oct 21, 2026
Merged

vanceingalls merged 1 commit into
mainfrom
feat/capture-blur-defects

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

What

Carries capture motion blur through renders and Studio, and tightens which elements the scan blurs.

  • Engine: installPageMotionBlur sets PAGE_MOTION_BLUR_OFF_GLOBAL before any page script runs, when the engine's own sub-frame blur is on (--motion-blur) or when the render must match a Studio preview that could not blur, and tells the page the render's device scale. CaptureOptions.pageMotionBlur carries the request.
  • Producer: a composition whose root (the one the runtime would pick: data-root="true" first) sets data-motion-blur="on" gets a captureMotionBlur render-mode hint and goes through screenshot capture, because html-in-canvas needs a real paint; it is not pinned to one worker. A script-set attribute is not seen: it has to be in the HTML. The probe session carries the request's pageMotionBlur like every other session, so "off" survives a render with no data-duration.
  • Studio and Studio server: each render request carries previewMotionBlur, true only if Studio's own browser has drawElementImage and requestPaint. The render route turns previewMotionBlur: false into pageMotionBlur: "off" once, so the Studio dev server and the CLI server both receive the final value.
  • Mover scan: a large element that is see-through while it moves (a translucent sweep, a light leak, a large image or canvas with alpha) is no mover, because its crop would carry the scene beneath it along its path; its fast children still blur. An opaque large photo still blurs; <video> counts as opaque. The scan also skips elements moved by a 3D rotate axis or a z translate.
  • CLI snapshot: injects video frames before the seek settles, matching the engine's order.

Why

Part 4 of 9 in the capture motion blur stack: #5875 → #5967 → #5968 → #5969 → #5970 → #5971 → #5990 → #5991 → #5992. It builds on #5968; this is where a render can use the runtime.

Stack goal: opt-in capture motion blur for compositions, in Studio preview and in renders, drawn with Chrome's html-in-canvas. Nothing blurs without data-motion-blur="on" on the root.

Notes

  • WYSIWYG: when Studio sends previewMotionBlur: false the render has no blur, even for opted-in compositions.
  • Engine sub-frame blur (--motion-blur) and page blur never both run.
  • Cost: about 1.7x median render time for a blurred composition. k3-promo, 16.5 s, 1080p30: no blur 13 s, capture motion blur 28 s, engine --motion-blur 27 min. Compositions that are blocked or static cost nothing extra.
  • Output can differ across worker counts, at about the size of the chunk-split noise a render without blur already has; making every worker's scan find the same movers is feat(core,producer): decide blur defects once per timeline, off on routes that cannot host it #5990.
  • <video> in Studio preview: a video drawn through the capture is about 2–3% darker than normal playback, visible as a small brightness change on play/pause. Renders are unaffected (video frames are injected as images). Accepted.
  • Not detected as see-through movers, so they are treated as opaque: a CSS background-image, mask-image or clip-path that lets the scene show through; an element that is see-through over less than half of the scene is not recognised as a sweep. A large image or canvas whose pixels cannot be read (cross-origin) counts as see-through and is not blurred. All documented, not fixed.
  • Limits that apply to what a render now shows: trails are drawn over foreground elements above a mover; a transparent mover carries a halo of its background inside its box; trails leak past overflow:hidden; a 2D flip through scaleX = 0 blends as rotation; sub-pixel left steps land in whole pixels; a fast child inside a parent that moved earlier is not blurred; SVG elements are not scanned.
  • Not in this PR: blur does not yet check for the things html-in-canvas captures wrongly (feat(core): block capture motion blur on html-in-canvas defects #5971); the engine side that tracks WebGL canvases before page scripts is feat(core,engine): track accelerated canvases before any page script runs #5970.

Before

k3-promo at 9.1 s, no motion blur:

Before: no motion blur

After

Same frame with capture motion blur (360° shutter): the pins, the text and the box smear along their paths.

After: capture motion blur

Testing

  • Engine pageMotionBlur.test.ts: the off switch for engine blur and for an unsupported preview, device scale.
  • Producer htmlCompiler.test.ts: the render-mode hint on the runtime's root, not applied when the preview could not blur; probeStage.test.ts: the probe session carries pageMotionBlur; renderOrchestrator.test.ts and request validation: pageMotionBlur is passed through and only "off" is accepted.
  • Studio-server render.test.ts and CLI studioServer.renderBrowser.test.ts / Studio vite.adapter.render.test.ts: the adapters receive pageMotionBlur: "off" for previewMotionBlur: false; Studio useRenderQueueMotionBlur.test.tsx: the request body carries it.
  • Core discover.test.ts: translucent, fading, colour and paint-less sweeps are no mover, opaque ones stay, large images and canvases with and without alpha, 3D individual transforms; CLI snapshot.test.ts and captureCompositionFrame.test.ts: the order of video injection and seek.
  • bun run build and tsc --noEmit in core, engine, producer, cli, studio-server and studio pass; comment ratchet against feat(core): harden the capture motion blur runtime lifecycle #5968's branch passes; no package size budget changes.

🤖 Generated with Claude Code

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta review at full headfcf0512a4427331842036cdc7f73da6eb142ccd0, relative to68737fe2.

Audited: htmlInCanvas.ts form detection/marking, package subpath exports, defect predicates/version bounds, blur wrap/unwrap marking, VFX capture-form adoption and Studio capability consumer. Detection prefers layoutSubtree before drawable/updateElementGeometry, and nested-canvas checks include both forms. Defect version bounds match the author's measured claims; the next PR intentionally makes them conservative across versions.
Trusting: the supplied real-Chrome defect measurements and private test audit; no fresh browser comparison by Home.

The inherited #5875 pose and #5967 timeout findings remain; this delta approval does not clear the complete stack. Saved/fresh prior human reviews and inline comments were empty. Exact-head hosted checks pass/skip; the private focused core run passed124 authored tests. Inspected child regression logs execute3 visual/audio cases, not every new unit test. Browser-version screenshots, HDR output, worker parity, colour and performance measurements were not independently rerun. No provider call, merge or release.

— Magi (Home)
Verdict: APPROVE
Reasoning: No additional blocking defect found in this change relative to its stack parent; the foundation fixes remain required.

@vanceingalls
vanceingalls marked this pull request as draft October 20, 2026 21:58
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-render branch from 68737fe to c47bef3 Compare October 21, 2026 01:55
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-defects branch from fcf0512 to af9d2eb Compare October 21, 2026 01:55
@vanceingalls vanceingalls changed the title feat(core): block capture motion blur on html-in-canvas defects feat(producer,studio): wire capture motion blur into renders and Studio Oct 21, 2026
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-render branch from c47bef3 to 27f076c Compare October 21, 2026 03:53
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-defects branch from af9d2eb to 9ec57dd Compare October 21, 2026 03:53
@vanceingalls
vanceingalls marked this pull request as ready for review October 21, 2026 04:16
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-render branch from 27f076c to 3a35ca0 Compare October 21, 2026 05:18
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-defects branch from 9ec57dd to 774f8ab Compare October 21, 2026 05:18
miguel-heygen
miguel-heygen previously approved these changes Oct 21, 2026

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 774f8abccd4d0290d2c28257d259ad435a15145d relative to 3a35ca04264c42a87c610b9ecfa15620b3629569.

Audited preview availability/explicit false through Studio, server, CLI adapter, producer probe/capture and engine pre-navigation installation; compiler root/opt-in hint and video injection before paint settlement. False remains off end-to-end; absence/true preserves root opt-in. Read changed integration sites in larger producer/engine files, not every unrelated existing line. Author browser/colour/performance measurements were not rerun.

Independent revision-bound review of the nine-PR composition. Earlier approvals/requests covered older heads. Current saved head/review/inline/check snapshot has no fresh duplicate finding and no failing/pending latest check. Stacked broad-suite skips are not executed tests.

Audited: Studio capability/off choice through server, CLI adapter, producer probe/build/capture and engine pre-navigation installer; compiler opt-in/root choice and video injection before paint settle.
Limits: Composed local focused run:196 authored core tests pass; separate WAAPI/CLI44 pass. Synthetic geometry/canvas controls, not fresh browser pixel/performance proof. No app/model/provider call, source fix, merge or release.

— Magi / desktop-review

Verdict: APPROVE
Reasoning: No additional blocker in this own delta. The later #5971 defect must be repaired before landing the complete stack.

@vanceingalls
vanceingalls force-pushed the feat/capture-blur-render branch from 3a35ca0 to d2489eb Compare October 21, 2026 17:43
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-defects branch from 774f8ab to 7780f04 Compare October 21, 2026 17:43
Base automatically changed from feat/capture-blur-render to main October 21, 2026 18:04
@vanceingalls
vanceingalls dismissed miguel-heygen’s stale review October 21, 2026 18:04

The base branch was changed.

@vanceingalls
vanceingalls force-pushed the feat/capture-blur-defects branch 2 times, most recently from 11ed12a to 3436c8f Compare October 21, 2026 18:41
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

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

  • 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

@vanceingalls
vanceingalls force-pushed the feat/capture-blur-defects branch from 3436c8f to d082f58 Compare October 21, 2026 19:04
Carry the runtime's blur through the render pipeline and the Studio render
panel.

- engine: installPageMotionBlur sets the page-side off switch and the render's
  device scale before any author script runs; CaptureOptions.pageMotionBlur
  lets a caller stand the page blur down.
- producer: a composition that asks for data-motion-blur on the root the
  runtime would pick forces screenshot capture, because html-in-canvas needs
  a real paint; the probe session carries the request's pageMotionBlur like
  every other session.
- studio-server and Studio: the render panel sends previewMotionBlur, the
  render route turns "off" into pageMotionBlur once, and the Studio dev server
  and the CLI server both receive the final value.
- mover scan: a large element that is see-through while it moves (a sweep, a
  light leak) is no mover, including a large image or canvas with alpha; the
  scan skips elements moved by a 3D individual transform property.
- cli snapshot injects video frames before the seek settles, matching the
  engine's order.

Folded review fixes: R1, R2, R3 (scan half), R7, R16, R18, T3 (render
adapters).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@vanceingalls
vanceingalls force-pushed the feat/capture-blur-defects branch from d082f58 to dd4f93c Compare October 21, 2026 19:48
@vanceingalls
vanceingalls merged commit 1f8ead7 into main Oct 21, 2026
82 checks passed
@vanceingalls
vanceingalls deleted the feat/capture-blur-defects branch October 21, 2026 20:32
WaterrrForever added a commit that referenced this pull request Oct 22, 2026
… defect checks

#5971 and #5990-#5992 added htmlInCanvasDefects.ts and its package export
after #5969 last raised this budget, taking core to 3935667 bytes unpacked.
main's Build has failed on that since 4371981.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
dahikino pushed a commit to dahikino/hyperframes that referenced this pull request Oct 22, 2026
… defect checks (heygen-com#6055)

heygen-com#5971 and heygen-com#5990-heygen-com#5992 added htmlInCanvasDefects.ts and its package export
after heygen-com#5969 last raised this budget, taking core to 3935667 bytes unpacked.
main's Build has failed on that since 4371981.

Co-authored-by: Claude Opus 5.5 (1M context) <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.

2 participants