Skill Tracer
Skills › HyperFrames › remotion-to-hyperframes › transitions.md

transitions.md

114 lines · skills/remotion-to-hyperframes/references/transitions.md at a595755

Open on GitHub
1# Transitions translation: @remotion/transitions → HF crossfades / shader-transitions
2
3The `@remotion/transitions` package is Remotion's library of pre-built
4scene-to-scene transitions. HF has two paths to translate them:
5
61. **Manual GSAP crossfade** — for simple opacity/transform transitions. Free, no extra package.
72. **HF shader-transitions package** — for visually-rich transitions that match the @remotion/transitions presets.
8

Transitions translation: @remotion/transitions → HF crossfades / shader-transitions

The @remotion/transitions package is Remotion's library of pre-built scene-to-scene transitions. HF has two paths to translate them:

  1. Manual GSAP crossfade — for simple opacity/transform transitions. Free, no extra package.
  2. HF shader-transitions package — for visually-rich transitions that match the @remotion/transitions presets.
Raw lines 1 to 8
9## Pattern: `<TransitionSeries>` is `<Series>` with overlap
11```tsx
12<TransitionSeries>
13 <TransitionSeries.Sequence durationInFrames={60}>
14 <SceneA />
15 </TransitionSeries.Sequence>
16 <TransitionSeries.Transition
17 presentation={fade()}
18 timing={linearTiming({ durationInFrames: 15 })}
19 />
20 <TransitionSeries.Sequence durationInFrames={60}>
21 <SceneB />
22 </TransitionSeries.Sequence>
23</TransitionSeries>
24```
26Translates to scenes that overlap by the transition duration:
28- SceneA: [0, 60] = `data-start="0" data-duration="2"`
29- SceneB: [60-15, 60-15+60] = `data-start="1.5" data-duration="2"` (the transition window overlaps the end of A and start of B)
31Then drive the transition with GSAP:
33```js
34// Manual fade (presentation={fade()})
35tl.to(sceneA, { opacity: 0, duration: 0.5, ease: "none" }, 1.5);
36tl.fromTo(sceneB, { opacity: 0 }, { opacity: 1, duration: 0.5, ease: "none" }, 1.5);
37```

Pattern: <TransitionSeries> is <Series> with overlap

<TransitionSeries>
  <TransitionSeries.Sequence durationInFrames={60}>
    <SceneA />
  </TransitionSeries.Sequence>
  <TransitionSeries.Transition
    presentation={fade()}
    timing={linearTiming({ durationInFrames: 15 })}
  />
  <TransitionSeries.Sequence durationInFrames={60}>
    <SceneB />
  </TransitionSeries.Sequence>
</TransitionSeries>

Translates to scenes that overlap by the transition duration:

  • SceneA: [0, 60] = data-start="0" data-duration="2"
  • SceneB: [60-15, 60-15+60] = data-start="1.5" data-duration="2" (the transition window overlaps the end of A and start of B)

Then drive the transition with GSAP:

// Manual fade (presentation={fade()})
tl.to(sceneA, { opacity: 0, duration: 0.5, ease: "none" }, 1.5);
tl.fromTo(sceneB, { opacity: 0 }, { opacity: 1, duration: 0.5, ease: "none" }, 1.5);
Raw lines 9 to 38
39## Presentation table
41| Remotion `presentation` | HF translation |
42| ---------------------------------- | ----------------------------------------------------------------------------------------- |
43| `fade()` | manual `gsap.to(opacity)` crossfade |
44| `slide({direction: "from-right"})` | `gsap.fromTo(translateX: "100%" → 0)` on incoming + `to(translateX: "-100%")` on outgoing |
45| `wipe({direction: "from-left"})` | `gsap.fromTo(clip-path: inset(0 100% 0 0) → inset(0 0 0 0))` on incoming |
46| `clockWipe()` | use HF's `sdf-iris` shader-transition (`npx hyperframes add sdf-iris`) |
47| `flip()` | `gsap.to(rotateY)` 180° split between scenes |
48| `cube()` | use HF's `cinematic-zoom` or build manually with `rotateY` + `transform-origin` |
49| `iris()` | use HF's `sdf-iris` shader-transition |
50| `none()` | no transition; hard cut at the boundary |

Presentation table

Remotion presentationHF translation
fade()manual gsap.to(opacity) crossfade
slide({direction: "from-right"})gsap.fromTo(translateX: "100%" → 0) on incoming + to(translateX: "-100%") on outgoing
wipe({direction: "from-left"})gsap.fromTo(clip-path: inset(0 100% 0 0) → inset(0 0 0 0)) on incoming
clockWipe()use HF's sdf-iris shader-transition (npx hyperframes add sdf-iris)
flip()gsap.to(rotateY) 180° split between scenes
cube()use HF's cinematic-zoom or build manually with rotateY + transform-origin
iris()use HF's sdf-iris shader-transition
none()no transition; hard cut at the boundary
Raw lines 39 to 51
52## Timing translations
54```tsx
55linearTiming({durationInFrames: 15}) → ease: "none"
56linearTiming({durationInFrames: 15, easing: ...}) → ease per the easing table in timing.md
57springTiming({config: {damping: 12}}) → ease: "back.out(1.4)" (~0.7 s)
58```
60Convert `durationInFrames` to seconds (`/fps`).

Timing translations

linearTiming({durationInFrames: 15})              → ease: "none"
linearTiming({durationInFrames: 15, easing: ...}) → ease per the easing table in timing.md
springTiming({config: {damping: 12}})             → ease: "back.out(1.4)" (~0.7 s)

Convert durationInFrames to seconds (/fps).

Raw lines 52 to 61
62## When to use HF shader-transitions
64For transitions Remotion presets that have visually-rich GLSL equivalents
65(iris, ripple, zoom, glitch), use HF's [shader-transitions](https://hyperframes.heygen.com/catalog/blocks)
66package. They produce richer output than manual GSAP transforms.
68```bash
69npx hyperframes add sdf-iris
70```
72Then in the composition:
74```html
75<div id="iris-transition" class="hf-shader-transition" data-start="1.5" data-duration="0.5">
76 <!-- bound scenes via the shader-transition's data-from / data-to -->
77</div>
78```
80Each shader-transition has its own data attributes; see the catalog page
81for the specific block.

When to use HF shader-transitions

For transitions Remotion presets that have visually-rich GLSL equivalents (iris, ripple, zoom, glitch), use HF's shader-transitions package. They produce richer output than manual GSAP transforms.

npx hyperframes add sdf-iris

Then in the composition:

<div id="iris-transition" class="hf-shader-transition" data-start="1.5" data-duration="0.5">
  <!-- bound scenes via the shader-transition's data-from / data-to -->
</div>

Each shader-transition has its own data attributes; see the catalog page for the specific block.

Raw lines 62 to 82
83## When the source uses a custom Presentation
85Remotion supports custom `presentation` implementations:
87```tsx
88const customPresentation: PresentationComponent = ({
89 children,
90 presentationProgress,
91 presentationDirection,
92}) => {
93 return (
94 <div
95 style={
96 {
97 /* compute transform from progress */
98 }
99 }
100 >
101 {children}
102 </div>
103 );
104};
105```
107Translation: extract the math from the `style={...}` block and emit
108equivalent GSAP tweens. Specifically the transform formula maps directly
109to a `gsap.to(target, { transform: ... })` parameterized by `progress`.
111If the custom presentation uses `useCurrentFrame()` internally to
112animate something _outside_ the simple progress curve, treat the source
113as untranslatable and bow out to the runtime interop pattern (see
114[escape-hatch.md](escape-hatch.md)).

When the source uses a custom Presentation

Remotion supports custom presentation implementations:

const customPresentation: PresentationComponent = ({
  children,
  presentationProgress,
  presentationDirection,
}) => {
  return (
    <div
      style={
        {
          /* compute transform from progress */
        }
      }
    >
      {children}
    </div>
  );
};

Translation: extract the math from the style={...} block and emit equivalent GSAP tweens. Specifically the transform formula maps directly to a gsap.to(target, { transform: ... }) parameterized by progress.

If the custom presentation uses useCurrentFrame() internally to animate something outside the simple progress curve, treat the source as untranslatable and bow out to the runtime interop pattern (see escape-hatch.md).

Raw lines 83 to 114