Skip to main content

<OffthreadVideo>

Available from Remotion 3.0.11

This component imports and displays a video, similar to <Video/>, but during rendering, extracts the exact frame from the video and displays it in a <Img> tag. This extraction process happens outside the browser using FFmpeg.

This component was designed to combat limitations of the default <Video> element. See: <Video> vs <OffthreadVideo>.

Example

tsx
import { AbsoluteFill, OffthreadVideo, staticFile } from "remotion";
 
export const MyVideo = () => {
return (
<AbsoluteFill>
<OffthreadVideo src={staticFile("video.webm")} />
</AbsoluteFill>
);
};
tsx
import { AbsoluteFill, OffthreadVideo, staticFile } from "remotion";
 
export const MyVideo = () => {
return (
<AbsoluteFill>
<OffthreadVideo src={staticFile("video.webm")} />
</AbsoluteFill>
);
};

You can load a video from an URL as well:

tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" />
</AbsoluteFill>
);
};
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" />
</AbsoluteFill>
);
};

Props

src

The URL of the video to be rendered. Can be a remote URL or a local file referenced with staticFile().

startFrom?

Will remove a portion of the video at the beginning.

In the following example, we assume that the fps of the composition is 30.

By passing startFrom={60}, the playback starts immediately, but with the first 2 seconds of the video trimmed away.
By passing endAt={120}, any video after the 4 second mark in the file will be trimmed away.

The video will play the range from 00:02:00 to 00:04:00, meaning the video will play for 2 seconds.

tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo
src={staticFile("video.webm")}
startFrom={60}
endAt={120}
/>
</AbsoluteFill>
);
};
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo
src={staticFile("video.webm")}
startFrom={60}
endAt={120}
/>
</AbsoluteFill>
);
};

endAt?

Removes a portion of the video at the end. See startAt for an explanation.

transparent?v4.0.0

If set to true, frames will be extracted as PNG, enabling transparency but also slowing down your render.

If set to false (default), frames will be extracted as bitmap (BMP), which is faster.

volume?

Allows you to control the volume for the whole track or change it on a per-frame basis. Refer to the using audio guide to learn how to use it.

Example using static volume
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo volume={0.5} src={staticFile("video.webm")} />
</AbsoluteFill>
);
};
Example using static volume
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo volume={0.5} src={staticFile("video.webm")} />
</AbsoluteFill>
);
};
Example of a ramp up over 100 frames
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo
volume={(f) =>
interpolate(f, [0, 100], [0, 1], { extrapolateLeft: "clamp" })
}
src={staticFile("video.webm")}
/>
</AbsoluteFill>
);
};
Example of a ramp up over 100 frames
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo
volume={(f) =>
interpolate(f, [0, 100], [0, 1], { extrapolateLeft: "clamp" })
}
src={staticFile("video.webm")}
/>
</AbsoluteFill>
);
};
note

On iOS Safari, it's not possible to granularly change the volume of a media tag.
Only values 0 and 1 will be respected by the browser.

loopVolumeCurveBehavior?v4.0.142

Controls the frame which is returned when using the volume callback function and wrapping OffthreadVideo in a <Loop>.

Can be either "repeat" (default, start from 0 on each iteration) or "extend" (keep increasing frames).

style?

You can pass any style you can pass to a native HTML element. Keep in mind that during rendering, <OffthreadVideo> renders an <Img> tag, but a <video> tag is used during preview.

tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<Img
src={staticFile("video.webm")}
style={{ height: 720, width: 1280 }}
/>
</AbsoluteFill>
);
};
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<Img
src={staticFile("video.webm")}
style={{ height: 720, width: 1280 }}
/>
</AbsoluteFill>
);
};

allowAmplificationDuringRender?v3.3.17

Make values for volume greater than 1 result in amplification during renders.
During Preview, the volume will be limited to 1, since the browser cannot amplify audio.

name?v4.0.71

A name and that will be shown as the label of the sequence in the timeline of the Remotion Studio. This property is purely for helping you keep track of items in the timeline.

toneFrequency?v4.0.47

Adjust the pitch of the audio - will only be applied during rendering.

Accepts a number between 0.01 and 2, where 1 represents the original pitch. Values less than 1 will decrease the pitch, while values greater than 1 will increase it.

A toneFrequency of 0.5 would lower the pitch by half, and a toneFrequency of 1.5 would increase the pitch by 50%.

onError?

Handle an error playing the video. From v3.3.89, if you pass an onError callback, then no exception will be thrown. Previously, the error could not be caught.

playbackRate?v2.2.0

Controls the speed of the video. 1 is the default and means regular speed, 0.5 slows down the video so it's twice as long and 2 speeds up the video so it's twice as fast.

While Remotion doesn't limit the range of possible playback speeds, in development mode the HTMLMediaElement.playbackRate API is used which throws errors on extreme values. At the time of writing, Google Chrome throws an exception if the playback rate is below 0.0625 or above 16.

Example of a video playing twice as fast
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo playbackRate={2} src={staticFile("video.webm")} />
</AbsoluteFill>
);
};
Example of a video playing twice as fast
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo playbackRate={2} src={staticFile("video.webm")} />
</AbsoluteFill>
);
};

muted?

You can drop the audio of the video by adding a muted prop:

Example of a muted video
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo
muted
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
/>
</AbsoluteFill>
);
};
Example of a muted video
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo
muted
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
/>
</AbsoluteFill>
);
};

acceptableTimeShiftInSeconds?v3.2.42

In the Studio or in the Remotion Player, Remotion will seek the video if it gets too much out of sync with Remotion's internal time - be it due to the video loading or the page being too slow to keep up in real-time. By default, a seek is triggered if 0.45 seconds of time shift is encountered. Using this prop, you can customize the threshold.

allowAmplificationDuringRender?v3.3.17

Make values for volume greater than 1 result in amplification during renders.
During Preview, the volume will be limited to 1, since the browser cannot amplify audio.

toneFrequency?v4.0.47

Adjust the pitch of the audio - will only be applied during rendering.

Accepts a number between 0.01 and 2, where 1 represents the original pitch. Values less than 1 will decrease the pitch, while values greater than 1 will increase it.

A toneFrequency of 0.5 would lower the pitch by half, and a toneFrequency of 1.5 would increase the pitch by 50%.

pauseWhenBuffering?v4.0.111

If set to true and the video is loading, the Player will enter into the native buffering state. The default is false, but will become true in Remotion 5.0.

toneMapped?v4.0.117

Since Remotion 4.0.117, Remotion will adjust the colors of videos in different color spaces (such as HDR) when converting to RGB, to counteract color shifts.
Since the browser is painting in sRGB, this is necessary to ensure that the colors are displayed correctly.
This behavior is by default true and can be disabled by setting toneMapped to false.
Disabling tone mapping will speed up rendering, but may result in less vibrant colors.

Prior to Remotion 4.0.117, tone mapping was always disabled, and the toneMapped prop was not available.

showInTimeline?v4.0.122

If set to false, no layer will be shown in the timeline of the Remotion Studio. The default is true.

delayRenderTimeoutInMilliseconds?v4.0.150

Customize the timeout of the delayRender() call that this component makes.

delayRenderRetries?v4.0.178

Customize the number of retries of the delayRender() call that this component makes.

onAutoPlayError?v4.0.187

A callback function that gets called when the video fails to play due to autoplay restrictions.
If you don't pass a callback, the video will be muted and be retried once.
This prop is useful if you want to handle the error yourself, e.g. for pausing the Player.
Read more here about autoplay restrictions.

onVideoFrame?v4.0.190

A callback function that gets called when a frame is extracted from the video.
Useful for video manipulation.
The callback is called with a CanvasImageSource object.
During preview, this is a HTMLVideoElement object, during rendering, it is an HTMLImageElement.

crossOrigin?v4.0.190

Corresponds to the crossOrigin attribute of the <video> element.
One of "anonymous", "use-credentials" or undefined.
Default: "anonymous" if onVideoFrame is specified, undefined, otherwise.

imageFormat v3.0.22

removed in v4.0.0

Either jpeg or png. Default jpeg.
With png, transparent videos (VP8, VP9, ProRes) can be displayed, however it is around 40% slower, with VP8 videos being much slower.

Other props

The props onError, className and style are supported and get passed to the underlying HTML element. Remember that during render, this is a <img> element, and during Preview, this is a <video> element.

Performance tips

Only set transparent to true if you need transparency. It is slower than non-transparent frame extraction.
If you don't care about color accuracy, you can set toneMapped to false as well to save time on color conversion.

Looping a video

Unlike <Video>, OffthreadVideo does not currently implement the loop property.

You can use the following snippet that uses @remotion/media-utils to loop a video.

Note that this will mount a <video> tag in the browser, meaning only codecs supported by the browser can be used.

LoopedOffthreadVideo.tsx
tsx
import {
Loop,
OffthreadVideo,
useVideoConfig,
} from "remotion";
 
export const LoopedOffthreadVideo: React.FC<{
durationInSeconds: number | null;
src: string;
}> = ({durationInSeconds, src}) => {
const { fps } = useVideoConfig();
 
if (durationInSeconds === null) {
return null;
}
 
return (
<Loop durationInFrames={Math.floor(fps * durationInSeconds)}>
<OffthreadVideo src={src} />
</Loop>
);
};
LoopedOffthreadVideo.tsx
tsx
import {
Loop,
OffthreadVideo,
useVideoConfig,
} from "remotion";
 
export const LoopedOffthreadVideo: React.FC<{
durationInSeconds: number | null;
src: string;
}> = ({durationInSeconds, src}) => {
const { fps } = useVideoConfig();
 
if (durationInSeconds === null) {
return null;
}
 
return (
<Loop durationInFrames={Math.floor(fps * durationInSeconds)}>
<OffthreadVideo src={src} />
</Loop>
);
};
Root.tsx
tsx
import React, { useState } from "react";
import { Composition, staticFile } from "remotion";
import { getVideoMetadata } from "@remotion/media-utils";
 
export const RemotionRoot: React.FC = () => {
return (
<Composition
id="MyComp"
component={LoopedOffthreadVideo}
defaultProps={{
durationInSeconds: null,
src: staticFile("myvideo.mp4"),
}}
calculateMetadata={async ({props}) => {
const { durationInSeconds, width, height } = await getVideoMetadata(props.src);
const fps = 30;
 
return {
// Set any duration, here as an example, we loop the video 5 times
durationInFrames: Math.floor(durationInSeconds * fps * 5),
fps,
width,
height,
props: {
...props,
// Pass the durationInSeconds as prop to the React component
durationInSeconds,
}
};
}}
/>
)
}
Root.tsx
tsx
import React, { useState } from "react";
import { Composition, staticFile } from "remotion";
import { getVideoMetadata } from "@remotion/media-utils";
 
export const RemotionRoot: React.FC = () => {
return (
<Composition
id="MyComp"
component={LoopedOffthreadVideo}
defaultProps={{
durationInSeconds: null,
src: staticFile("myvideo.mp4"),
}}
calculateMetadata={async ({props}) => {
const { durationInSeconds, width, height } = await getVideoMetadata(props.src);
const fps = 30;
 
return {
// Set any duration, here as an example, we loop the video 5 times
durationInFrames: Math.floor(durationInSeconds * fps * 5),
fps,
width,
height,
props: {
...props,
// Pass the durationInSeconds as prop to the React component
durationInSeconds,
}
};
}}
/>
)
}

Supported codecs by <OffthreadVideo>

The following codecs can be read by <OffthreadVideo>:

  • H.264 ("MP4")
  • H.265 ("HEVC")
  • VP8 and VP9 ("WebM")
  • AV1 (from v4.0.6)
  • ProRes

See also