Files
hyperframes/registry/components
miga-heygenandMiguel Ángel 82f49262ec feat(registry): blur every transform driver, and ship the reference case as shutter-slam (#4038)
The motion-blur snippet integrated translation only. Its output stage was one
SVG filter per target whose copies were feOffset, and feOffset only translates,
so a beat that only scaled or only turned sampled a displacement of zero and
rendered sharp. No SVG filter primitive can give a copy its own affine
transform, so the sampling loop was never the problem: the output stage was.

Copies are now DOM clones of the element in an isolation:isolate group, each at
1/N opacity with mix-blend-mode plus-lighter, each carrying the element's
resolved transform at its sample time. plus-lighter adds premultiplied colour,
so N + 1 copies at 1/N sum to the average the shutter integral asks for; the
isolation is load-bearing rather than tidy, because without a transparent
backdrop of its own the first copy adds onto the page and blows a light
background out to white. Reading the resolved matrix instead of a list of named
GSAP properties is what makes every driver work at once: translation, scale,
rotation, 3D rotation and skew arrive in the same one value.

Measured against the After Effects export the shutter model was fitted to, per
beat, previous stage to this one: scale +0.62 dB, rotate X +0.43, rotate Y
+0.84, rotate Z +1.15, translate X -0.18, translate Y -0.04. The two small
translate regressions are inherent and not tunable: the filter offset one
rasterisation so every copy was pixel-identical, where each copy is now
rasterised independently at its own fractional transform with its own subpixel
antialiasing.

Three consequences of the new stage, each carrying its own fix:

- mix-blend-mode flattens preserve-3d, so a copy cannot inherit the parent's 3D
  context. Each copy carries the parent's perspective as its own first transform
  function instead.
- A copy is styled by nothing that selected the original, since dropping the id
  also drops every #id rule that gave it size, colour and font. Each copy
  carries its own resolved style inline, read once and replayed. Those values
  are px, so a container-relative element needs them again when its box changes:
  a ResizeObserver re-snapshots rather than paying a subtree walk every frame.
- A beat that moved and faded at once kept a full-strength smear behind a
  vanishing element. The element's opacity now rides the group; the 1/N weight
  stays on the copies.

The axis option is gone. It selected which of x and y to sample, a question the
resolved matrix does not have.

shutter-slam is the new component: one word through six single-property beats,
two translate slams, a scale punch, then a full turn about each axis, which
exercises every driver the model covers in one mount. Easing is power2.inOut
because it measured best, not because it was the default. A bezier fitted to the
reference's own bbox centres looks like the better curve and renders worse: a
blurred bbox centre is the shutter window's average rather than the instant, so
on an accelerating segment the fit measures its own blur.

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
2026-09-17 16:03:14 -04:00
..