Repository navigation
fix(studio): motion blur falls back for a WebGL canvas Studio reaches first - #7060
Conversation
… first
When a same-origin host page (Studio) reaches a preview element before the
scene does, for example through a MutationObserver record, Chrome gives that
element the host's prototype. The scene's getContext("webgl") then ran the
host's unpatched getContext, so the canvas never reached the accelerated
canvas set, capture motion blur stayed on, and the WebGL canvas froze during
preview playback.
The tracker now records a canvas in the set of the canvas's own window, and
installs itself in a same-origin parent too, compiled with the parent's own
Function. Cross-origin parents are skipped.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Edit accuracy: accurate 267 (base branch 267), smooth 106 of thoseThe gate passes. Quarantined, measured but not gated (8)
Unstable (3)
|
There was a problem hiding this comment.
Verdict: COMMENT (needs human) · 0 blockers · 1 important · 0 nits
The Studio fix and its tests look right, and CI is green (the new Chrome test ran in Test (runtime)). The open question is scope: the parent climb runs in every same-origin page that embeds the player, not only in Studio. A maintainer should decide whether that's acceptable. ↓ inline
— Claude
|
Holding this one. We agree with Genesis's point on the parent-window install and will push a change so Studio installs the tracker in its own window instead. Please don't merge at a2301dc. |
…mbedder's The preview runtime no longer installs the canvas tracker in its parent window. That ran on every preview boot, so any same-origin page embedding the player got its getContext wrapped, and a host CSP without unsafe-eval reported a violation on each load. Studio now installs the tracker in its own window when its Player module loads, and core exposes it at @hyperframes/core/page-motion-blur. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
miguel-heygen
left a comment
There was a problem hiding this comment.
Post-merge advisory review: this PR already merged into main at 112ba53302726f2135dc7b28b03033b5a897ffbd. Findings would be follow-ups, not gating. Reviewed head e6107f749aa90cd1cf8e491540aaf887174c9e84.
No source blocker found in the seven-path diff and affected consumers. The old parent-install concern in Genesis's inline is resolved: Player.tsx:29 installs only in Studio's window, including the root and ./player exports; core runtime installs only in its own window. pageMotionBlur.ts:38-52 records host-prototype canvases in their document window's tracked set for both accelerated contexts and OffscreenCanvas. The existing once-per-prototype guard and weak membership preserve page-lifetime installation without retaining canvases across repeated Player mounts. The new core subpath is present in both source and published export inventories.
The added tests assert scene ownership, embedding-page isolation, Studio module installation and a real-Chrome host-observer race before checking filter / accelerated-canvas. I read their source; I did not run them locally. The Chrome test has a capability skip, so job status alone does not prove that case executed.
Validation limit: original latest CI 41574348425 / attempt 1 binds this head, and its complete catalog marks runtime, Studio, Build, Lint, Typecheck and Format successful. The first runtime log download (125714384405) returned HTTP 404 BlobNotFound; I stopped without retrying. I therefore have not independently verified executed browser results, the author's main-vs-fix controls, or before/after screenshots. Captured historical checks also contain an older failed “Studio and player captures” record alongside a later successful record; this is not an all-green-history claim. No fresh browser, Desktop runtime or deployment validation was performed.
Verdict: COMMENT (post-merge: Ready on audited source; browser execution evidence unverified)
Reasoning: the narrowed implementation addresses the cross-realm tracking defect without restoring the embedding-page side effect. No additional source fix was identified; unavailable native logs limit the validation claim.
— Magi (cutroom-dev)
What
With motion blur on, a scene that draws to a WebGL canvas froze during preview playback in Studio. Paused and seeked frames looked right. This makes capture motion blur fall back for those scenes in Studio, the same way it already does in renders.
Why
Capture blur cannot handle a WebGL or WebGPU canvas (Chrome captures it at the frame it had before the blur wrapped the root). The preview knows a canvas is accelerated because the tracker wraps
getContexton the preview'sHTMLCanvasElement.prototype.In Studio the preview is a same-origin iframe, and Studio observes and walks its DOM. When the host page reaches an element first, for example through a
MutationObserverrecord, Chrome creates that element's wrapper in the host's realm, with the host's prototype. The scene'scanvas.getContext("webgl")then ran the host's unpatchedgetContext, the canvas never entered the tracked set, theaccelerated-canvascheck passed, and capture blur stayed on. Pausing unwraps the root, which is why paused frames were fine.Related work
None.
How
installAcceleratedCanvasTracker(core) records a canvas in the tracked set of the canvas's own window (ownerDocument.defaultView), not the set of the realm whosegetContextran. The__hfAccelTrackedguard still keeps repeat installs from wrapping twice.Playermodule loads, before any preview iframe exists. A canvas that picked up Studio's prototype then still lands in the preview's set.Player.tsxis the module every Studio preview goes through (NLEPreviewrenders it, and it is exported from both the.and./playerentries), so apps that embed Studio's components get the install too, not only the standalone Studio page.@hyperframes/core/page-motion-blursubpath, so the light./playerentry does not pull in the core barrel.The preview runtime only installs the tracker in its own window. An earlier version of this PR also installed it in a same-origin parent from the runtime, which ran on every preview boot: any page embedding the player got its
getContextwrapped and a global added, and a host CSP withoutunsafe-evalreported a violation on each load. That part is gone.Test plan
Unit tests added/updated
Manual testing performed
Documentation updated (if applicable)
Comments follow CONTRIBUTING.md "Comments": they say why, not what, and a bug fix says what the code must do and how to reproduce the bug
packages/engine/src/services/captureMotionBlur.pixels.browser.test.ts(real Chrome, runs inTest (runtime)): the host page installs the tracker in its own window, as Studio does, then hosts adata-motion-blur="on"composition in a same-origin iframe and observes it with aMutationObserverbefore the runtime boots. The scene mounts a WebGL canvas, the host walks it, then the scene callsgetContext. The test asserts the canvas really carries the host's prototype, then that the report falls back withtier: "filter",reasonCode: "accelerated-canvas". Withmain's core it staystier: "capture". It also staystier: "capture"with this change if the host does not install the tracker.packages/core/src/pageMotionBlur.test.ts(jsdom): with the tracker in both windows, a scene canvas carrying the host's prototype lands in the scene's set (fails onmain). A new test checks that running the tracker in a scene leaves the embedding page'sgetContextand globals untouched (fails on the earlier version of this PR).packages/studio/src/player/components/Player.test.ts: loading the Player module wrapsgetContextin Studio's own window and defines the tracked set.Full core, engine and studio test suites, core/engine/studio typecheck, and
bun run lintpass locally.Manual check in Studio on a project with a three.js globe sub-composition and motion blur on: on
mainthe globe canvas had Studio's prototype, was not tracked, and the report saidtier: "capture". With the earlier version of this PR it was tracked and the report saidtier: "filter",reasonCode: "accelerated-canvas".The current version was checked in a real Studio preview, captured below: the Studio dev server running a small test project in headless Chrome 152 with
--enable-features=CanvasDrawElement, played with Space. The project is a WebGL test scene, not the globe project: a sub-composition draws a spinning WebGL triangle every animation frame, and a rotating box in the root gives motion blur a mover that needs the capture tier (data-motion-blur="on"). In both runs the canvas carried Studio's prototype. Before is the PR's base onmain(d03bdeae9): the report saidtier: "capture"and the triangle stayed frozen while the box moved. After is this branch: the report saidtier: "filter",reasonCode: "accelerated-canvas", and the triangle keeps spinning. Each strip shows four screenshots of the preview taken during one playback, labeled with the player's time.Before
After
🤖 Generated with Claude Code