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

media.md

166 lines · skills/remotion-to-hyperframes/references/media.md at a595755

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Media translation: Audio, Video, Img, IFrame, staticFile
2

Media translation: Audio, Video, Img, IFrame, staticFile

Raw lines 1 to 2
3## Asset paths
4
5Remotion's `staticFile("x.png")` resolves to the project's `public/` directory.
6HF uses relative paths from the composition's `index.html`, conventionally
7`assets/`:
8
9```tsx
10<Img src={staticFile("logo.png")} />
11```
13```html
14<img src="assets/logo.png" />
15```
17When translating, copy the asset from `remotion-src/public/x` to
18`hf-src/assets/x`. Multiple files can be batched with a setup script;
19see T2's `setup.sh` for an example pattern.

Asset paths

Remotion's staticFile("x.png") resolves to the project's public/ directory. HF uses relative paths from the composition's index.html, conventionally assets/:

<Img src={staticFile("logo.png")} />
<img src="assets/logo.png" />

When translating, copy the asset from remotion-src/public/x to hf-src/assets/x. Multiple files can be batched with a setup script; see T2's setup.sh for an example pattern.

Raw lines 3 to 20
21## `<Audio>`
23```tsx
24<Audio src={staticFile("music.wav")} volume={0.5} />
25```
27```html
28<audio
29 data-start="0"
30 data-duration="6"
31 data-track-index="2"
32 data-volume="0.5"
33 src="assets/music.wav"
34></audio>
35```
37`data-start` and `data-duration` are required — the runtime needs them to
38schedule the audio. Default to the composition's full duration if Remotion
39didn't specify trim.

<Audio>

<Audio src={staticFile("music.wav")} volume={0.5} />
<audio
  data-start="0"
  data-duration="6"
  data-track-index="2"
  data-volume="0.5"
  src="assets/music.wav"
></audio>

data-start and data-duration are required — the runtime needs them to schedule the audio. Default to the composition's full duration if Remotion didn't specify trim.

Raw lines 21 to 40
41### Volume ramps
43```tsx
44<Audio src={staticFile("music.wav")} volume={(f) => interpolate(f, [0, 30], [0, 1])} />
45```
47Volume ramps become a `data-automation` volume lane on the `<audio>`
48(form in `/media-use` → `references/mixing.md`); `data-volume`
49is the static baseline.

Volume ramps

<Audio src={staticFile("music.wav")} volume={(f) => interpolate(f, [0, 30], [0, 1])} />

Volume ramps become a data-automation volume lane on the <audio> (form in /media-use → references/mixing.md); data-volume is the static baseline.

Raw lines 41 to 50
Names a skill media-use :48
51### Trim / playbackRate
53```tsx
54<Audio src={staticFile("music.wav")} startFrom={60} endAt={180} playbackRate={1.5} />
55```
57```html
58<audio
59 data-start="0"
60 data-duration="<resolved from trim>"
61 data-trim-start="2"
62 data-trim-end="6"
63 data-playback-rate="1.5"
64 src="assets/music.wav"
65></audio>
66```
68`startFrom` / `endAt` are frame indexes; convert to seconds.

Trim / playbackRate

<Audio src={staticFile("music.wav")} startFrom={60} endAt={180} playbackRate={1.5} />
<audio
  data-start="0"
  data-duration="<resolved from trim>"
  data-trim-start="2"
  data-trim-end="6"
  data-playback-rate="1.5"
  src="assets/music.wav"
></audio>

startFrom / endAt are frame indexes; convert to seconds.

Raw lines 51 to 69
70## `<Video>` and `<OffthreadVideo>`
72```tsx
73<Video src={staticFile("intro.mp4")} playsInline />
74<OffthreadVideo src={staticFile("intro.mp4")} />
75```
77```html
78<!-- intro.mp4 has an audio stream -->
79<video
80 playsinline
81 data-has-audio="true"
82 data-start="0"
83 data-duration="5"
84 data-track-index="0"
85 src="assets/intro.mp4"
86></video>
88<!-- intro.mp4 is silent (or the Remotion element is muted) -->
89<video
90 playsinline
91 muted
92 data-start="0"
93 data-duration="5"
94 data-track-index="0"
95 src="assets/intro.mp4"
96></video>
97```
99`<OffthreadVideo>` is a Remotion-specific optimization for headless
100rendering. HF runs in headless Chrome already, so the off-thread variant
101collapses to a regular `<video>`.
103`playsinline` is required for the runtime to autoplay (browser policy); always
104emit it. Check the source file for an audio stream first:
105`ffprobe -v error -select_streams a -show_entries stream=codec_type -of csv=p=0 <file>`
106(non-empty output means it has audio). Remotion `<Video>` maps to
107`<video playsinline>` + `data-has-audio="true"` only when the file has an audio
108stream and the Remotion source does not set `muted` or `volume={0}`; otherwise
109emit `muted`. Silent b-roll with `data-has-audio="true"` aborts the render
110(`ASSET_MEDIA_TYPE_MISMATCH`).

<Video> and <OffthreadVideo>

<Video src={staticFile("intro.mp4")} playsInline />
<OffthreadVideo src={staticFile("intro.mp4")} />
<!-- intro.mp4 has an audio stream -->
<video
  playsinline
  data-has-audio="true"
  data-start="0"
  data-duration="5"
  data-track-index="0"
  src="assets/intro.mp4"
></video>

<!-- intro.mp4 is silent (or the Remotion element is muted) -->
<video
  playsinline
  muted
  data-start="0"
  data-duration="5"
  data-track-index="0"
  src="assets/intro.mp4"
></video>

<OffthreadVideo> is a Remotion-specific optimization for headless rendering. HF runs in headless Chrome already, so the off-thread variant collapses to a regular <video>.

playsinline is required for the runtime to autoplay (browser policy); always emit it. Check the source file for an audio stream first: ffprobe -v error -select_streams a -show_entries stream=codec_type -of csv=p=0 <file> (non-empty output means it has audio). Remotion <Video> maps to <video playsinline> + data-has-audio="true" only when the file has an audio stream and the Remotion source does not set muted or volume={0}; otherwise emit muted. Silent b-roll with data-has-audio="true" aborts the render (ASSET_MEDIA_TYPE_MISMATCH).

Raw lines 70 to 111
112## `<Img>`
114```tsx
115<Img src={staticFile("logo.png")} style={{ width: 200, height: 200 }} />
116```
118```html
119<img src="assets/logo.png" style="width: 200px; height: 200px;" />
120```
122Width/height get rounded to integer px. If the original style has
123animated dimensions, the GSAP tween animates them — see [timing.md](timing.md).

<Img>

<Img src={staticFile("logo.png")} style={{ width: 200, height: 200 }} />
<img src="assets/logo.png" style="width: 200px; height: 200px;" />

Width/height get rounded to integer px. If the original style has animated dimensions, the GSAP tween animates them — see timing.md.

Raw lines 112 to 124
125## `<IFrame>`
127```tsx
128<IFrame src="https://example.com" />
129```
131```html
132<iframe src="https://example.com"></iframe>
133```
135When HF detects a nested iframe in a composition, it auto-falls back to
136**screenshot mode** rather than the deterministic BeginFrame mode. This
137costs render performance but produces visibly-correct output. See
138[hyperframes-vs-remotion.mdx](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/hyperframes-vs-remotion.mdx)
139for details.

<IFrame>

<IFrame src="https://example.com" />
<iframe src="https://example.com"></iframe>

When HF detects a nested iframe in a composition, it auto-falls back to screenshot mode rather than the deterministic BeginFrame mode. This costs render performance but produces visibly-correct output. See hyperframes-vs-remotion.mdx for details.

Raw lines 125 to 140
141## `delayRender()` / `continueRender()`
143```tsx
144const handle = delayRender();
145useEffect(() => {
146 loadAsset().then(() => continueRender(handle));
147}, []);
148```
150Drop. HF waits on asset readiness via the [Frame Adapter pattern](https://hyperframes.heygen.com/concepts/frame-adapters)
151— images, videos, fonts, and Lottie animations all signal load
152completion natively. There's nothing to do at the application level.

delayRender() / continueRender()

const handle = delayRender();
useEffect(() => {
  loadAsset().then(() => continueRender(handle));
}, []);

Drop. HF waits on asset readiness via the Frame Adapter pattern — images, videos, fonts, and Lottie animations all signal load completion natively. There's nothing to do at the application level.

Raw lines 141 to 153
154## When the asset isn't a file
156If Remotion's media source is a Buffer, dataURL, or URL.createObjectURL,
157the asset doesn't exist on disk and can't be copied via setup.sh. Two
158options:
1601. Materialize the asset at translation time — write the buffer to a file
161 in `hf-src/assets/`.
1622. Embed as a data URL directly in the HTML (`src="data:image/png;base64,..."`)
163 for small assets (< 100 KB).
165For audio/video Buffers, option 1 is preferred — base64-encoded media
166bloats the HTML and slows the renderer.

When the asset isn't a file

If Remotion's media source is a Buffer, dataURL, or URL.createObjectURL, the asset doesn't exist on disk and can't be copied via setup.sh. Two options:

  1. Materialize the asset at translation time — write the buffer to a file in hf-src/assets/.
  2. Embed as a data URL directly in the HTML (src="data:image/png;base64,...") for small assets (< 100 KB).

For audio/video Buffers, option 1 is preferred — base64-encoded media bloats the HTML and slows the renderer.

Raw lines 154 to 166