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:

  1. Configure the frame cache in remotion.config.ts:

    import {setOffthreadVideoCacheSizeInBytes} from 'remotion';
    
    // Allocate 8GB for a 16GB machine
    setOffthreadVideoCacheSizeInBytes(8 * 1024 * 1024 * 1024);
  2. Set worker threads based on your CPU:

    import {setOffthreadVideoThreads} from 'remotion';
    
    setOffthreadVideoThreads(4);
  3. 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
  4. 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:

Summary

Optimizing OffthreadVideo performance in Remotion for long videos requires tuning the frame cache, parallel worker threads, and component props:

  • Set offthreadVideoCacheSizeInBytes to 30–50% of RAM to minimize frame recomputation without causing out-of-memory crashes.
  • Configure offthreadVideoThreads to 1–4 per CPU core to maximize parallel frame extraction throughput.
  • Disable transparent when alpha channels are unnecessary to switch from PNG to faster BMP rendering.
  • Keep pauseWhenBuffering enabled 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →