Optimizing OffthreadVideo Performance in Remotion for Long Videos
Increase the frame cache size to 30–50% of available RAM and allocate 1–4 compositor threads per CPU core to prevent frame extraction bottlenecks when rendering long videos with Remotion's OffthreadVideo component.
The OffthreadVideo component in the remotion-dev/remotion repository renders video frames on a separate process to keep the main thread responsive. For long videos, this off-thread architecture can become a bottleneck when extracting thousands of frames on-the-fly, requiring specific tuning of cache sizes and worker threads to maintain smooth rendering performance.
How OffthreadVideo Processes Long Videos
Remotion's off-thread architecture moves heavy video decoding work away from the main React render thread. Understanding this pipeline is essential for diagnosing performance issues in long renders.
Step 1: Proxy URL Generation
During server-side rendering, OffthreadVideoForRendering constructs a proxy URL pointing to a local HTTP server. This URL includes src, time, transparent, and toneMapped query parameters that tell the compositor exactly how to extract the frame.
In packages/core/src/video/offthread-video-source.ts, the getOffthreadVideoSource function generates these URLs:
// packages/core/src/video/offthread-video-source.ts
export const getOffthreadVideoSource = (options: {
src: string;
time: number;
transparent: boolean;
toneMapped: boolean;
}) => {
// Returns: http://localhost:<port>/proxy?src=...&time=...&transparent=...
};
Step 2: Asset Download and Caching
Before extracting frames, the server ensures the video file is cached locally. The downloadAsset function in packages/renderer/src/assets/download-and-map-assets-to-file.ts downloads the source video once per render run, storing it on disk for the compositor to access.
Step 3: Frame Extraction and Parallel Processing
The startOffthreadVideoServer in packages/renderer/src/offthread-video-server.ts receives proxy requests and forwards them to the compositor. The compositor executes the ExtractFrame command, which can run across multiple parallel workers controlled by the offthreadVideoThreads option.
// Inside offthread-video-server.ts
compositor.executeCommand('ExtractFrame', {
src: localAssetPath,
time: requestedTime,
transparent: query.transparent,
});
Step 4: Frame Cache Management
The compositor maintains an in-memory frame cache whose size is controlled by the offthreadVideoCacheSizeInBytes option. By default, this cache uses half of available system memory, but for long videos you should tune this explicitly via the --offthreadvideo-cache-size-in-bytes CLI flag or setOffthreadVideoCacheSizeInBytes API.
Identifying Performance Bottlenecks in Long Video Renders
Long videos amplify specific architectural constraints. Recognizing these symptoms helps you apply the correct optimization.
| Symptom | Root Cause | Solution |
|---|---|---|
| Long initial pause before first frame | Compositor cache is cold; initial asset download is slow | Pre-warm cache or increase delayRenderTimeoutInMilliseconds |
| Frequent "Server returned status 500" errors | Cache too small causing frame eviction and recomputation | Increase offthreadVideoCacheSizeInBytes to 30-50% of RAM |
| High CPU usage with low throughput | Insufficient parallel workers for frame extraction | Increase offthreadVideoThreads to 1-4 per CPU core |
| "Failed to fetch... low disk space" | Temporary asset storage exhausted | Clean CI runner disk or reduce concurrent renders |
| Out-of-memory crashes | Cache size exceeds available RAM | Set cache size explicitly below system memory limits |
Tuning OffthreadVideo for Maximum Performance
Remotion exposes several configuration options to optimize the off-thread rendering pipeline. Adjust these based on your hardware and video length.
Frame Cache Size
The offthreadVideoCacheSizeInBytes option controls how many extracted frames remain in memory. For long videos, set this to 30–50% of available RAM to minimize recomputation without causing swapping.
// remotion.config.ts
import {setOffthreadVideoCacheSizeInBytes} from 'remotion';
// 12 GB cache for a 32 GB machine
setOffthreadVideoCacheSizeInBytes(12 * 1024 * 1024 * 1024);
CLI override:
bunx remotion render MyComp.tsx --offthreadvideo-cache-size-in-bytes=12884901888
Compositor Worker Threads
The offthreadVideoThreads option sets the number of parallel workers extracting frames. The optimal range is 1–4 threads per CPU core, depending on other system load.
// remotion.config.ts
import {setOffthreadVideoThreads} from 'remotion';
setOffthreadVideoThreads(8);
CLI usage:
bunx remotion render MyComp.tsx --offthreadvideo-threads=8
Component-Level Optimizations
Use the pauseWhenBuffering prop to prevent CPU saturation when the cache empties. Keep this enabled for long renders.
<OffthreadVideo
src="long-video.mp4"
pauseWhenBuffering // Prevents busy-waiting when cache is cold
/>
Disable transparent unless you need an alpha channel. This switches output from PNG (RGBA) to BMP (RGB), reducing memory pressure and CPU overhead.
<OffthreadVideo
src="long-video.mp4"
transparent={false} // Uses faster BMP format
/>
Network and Timeout Settings
For CI environments with limited bandwidth, increase delayRenderTimeoutInMilliseconds and delayRenderRetries:
<OffthreadVideo
src="https://example.com/long-video.mp4"
delayRenderTimeoutInMilliseconds={60000} // 60 seconds
delayRenderRetries={3}
/>
Practical Implementation Guide
Follow these steps to optimize a long video render:
-
Configure the frame cache in
remotion.config.ts:import {setOffthreadVideoCacheSizeInBytes} from 'remotion'; // Allocate 8GB for a 16GB machine setOffthreadVideoCacheSizeInBytes(8 * 1024 * 1024 * 1024); -
Set worker threads based on your CPU:
import {setOffthreadVideoThreads} from 'remotion'; setOffthreadVideoThreads(4); -
Render with CLI overrides for one-off tuning:
bunx remotion render MyVideo.tsx \ --offthreadvideo-cache-size-in-bytes=10737418240 \ --offthreadvideo-threads=6 \ out/optimized.mp4 -
Optimize the component by disabling transparency and enabling buffering:
<OffthreadVideo src="file:///assets/long-movie.mp4" transparent={false} pauseWhenBuffering />
Common Pitfalls When Optimizing OffthreadVideo
Avoid these mistakes that degrade performance or cause failures:
- Never set cache size to 0 – The validator in
packages/renderer/src/options/offthreadvideo-cache-size.tsxthrowsExpected a positive number. - Do not use OffthreadVideo in client-side rendering – The component checks
environment.isClientSideRenderinginpackages/core/src/video/OffthreadVideoForRendering.tsxand throws an error. Use<Video>from@remotion/mediafor preview mode. - Avoid large transparent videos – PNG frames with alpha channels can exceed 10 MiB each, causing excessive memory pressure and cache thrashing.
- Don't ignore disk space – The asset download in
packages/renderer/src/assets/download-and-map-assets-to-file.tsrequires sufficient local storage. CI runners often fail with "low disk space" errors on long videos.
Summary
Optimizing OffthreadVideo performance in Remotion for long videos requires tuning the frame cache, parallel worker threads, and component props:
- Set
offthreadVideoCacheSizeInBytesto 30–50% of RAM to minimize frame recomputation without causing out-of-memory crashes. - Configure
offthreadVideoThreadsto 1–4 per CPU core to maximize parallel frame extraction throughput. - Disable
transparentwhen alpha channels are unnecessary to switch from PNG to faster BMP rendering. - Keep
pauseWhenBufferingenabled to prevent CPU saturation when the cache empties during long renders. - Monitor disk space and network timeouts to prevent asset download failures on CI systems.
Frequently Asked Questions
How does OffthreadVideo differ from the standard Video component in Remotion?
The standard <Video> component from @remotion/media runs in the browser environment and decodes frames on the main thread, which causes UI freezing during long renders. OffthreadVideo delegates frame extraction to a separate compositor process via an HTTP proxy server, enabling parallel decoding and caching while keeping the React render thread responsive.
What is the optimal cache size for rendering videos longer than 30 minutes?
For videos longer than 30 minutes, set offthreadVideoCacheSizeInBytes to approximately 30–50% of your system's RAM. For example, on a 32 GB machine, allocate 12–16 GB to the cache. This prevents the compositor from evicting frequently used frames while leaving sufficient memory for the operating system and Remotion's renderer.
Why does my render fail with "Server returned status 500" when using OffthreadVideo?
HTTP 500 errors from the offthread video server typically indicate that the frame cache is too small for your video length, causing the compositor to evict frames and fail to recompute them under memory pressure. Increase --offthreadvideo-cache-size-in-bytes to at least 30% of your RAM, or check that your disk has sufficient space for the asset download cache in packages/renderer/src/assets/download-and-map-assets-to-file.ts.
Can I use OffthreadVideo during preview development in the browser?
No, OffthreadVideo is strictly for server-side rendering and will throw an error if used in client-side rendering environments. The component explicitly checks environment.isClientSideRendering in packages/core/src/video/OffthreadVideoForRendering.tsx. For browser previews, use the standard <Video> component from @remotion/media, which renders on the main thread but is suitable for development workflows.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →