Code Scenes
Code scenes are the heart of Program. They're React components that render animated, interactive, data-driven visuals using Remotion.
What makes code scenes powerful
Unlike traditional video editing where you're limited to existing assets, code scenes let you create anything you can imagine. Animate text character by character. Visualize live data. Generate graphics procedurally. If you can code it in React, you can render it as video.
Anatomy of a code scene
Every code scene is a React component that exports a default function:
import { AbsoluteFill, useCurrentFrame, interpolate } from "remotion";
export default function MyScene() {
const frame = useCurrentFrame();
const opacity = interpolate(frame, [0, 30], [0, 1]);
return (
<AbsoluteFill style={{ backgroundColor: "#1a1a1a" }}>
<h1 style={{ color: "white", opacity }}>
Hello, video
</h1>
</AbsoluteFill>
);
}AbsoluteFill: a full-screen container that fills the video frameuseCurrentFrame(): returns the current frame number (0 to duration in frames)interpolate(): maps frame numbers to visual values (opacity, position, scale)
Adding code scenes
Via AI
Describe what you want and the AI writes the component:
Add a scene with the text "Welcome" that fades in over 2 secondsThe AI creates the React code, adds the scene to your composition, and you see it render immediately.
Via CLI
Use the addScene tool with your component code:
program tool exec addScene \
--title "Intro" \
--duration 5 \
--code 'export default function Intro() {
return (
<AbsoluteFill style={{ backgroundColor: "#000" }}>
<h1 style={{ color: "white" }}>Welcome</h1>
</AbsoluteFill>
);
}'For more complex scenes, you can pass the code from a file:
program tool exec addScene \
--title "Intro" \
--duration 5 \
--code "$(cat intro-scene.tsx)"In the editor
Click "New Layer" in the timeline to create a blank scene. Select it and switch to code view to write your component directly. Changes save automatically and the preview updates in real-time.
Available APIs
Code scenes have access to Remotion's full API and more:
Remotion core
| API | Purpose |
|---|---|
useCurrentFrame() | Current frame number |
useVideoConfig() | FPS, dimensions, total duration |
interpolate() | Smooth value transitions |
spring() | Physics-based animations |
Easing | Easing functions (ease-in, ease-out, etc.) |
Sequence | Time-offset nested content |
Series | Sequential content blocks |
Media
| API | Purpose |
|---|---|
OffthreadVideo | Efficient video embedding |
Audio | Audio playback |
Img | Image display |
IFrame | Embed external content |
Utilities
| API | Purpose |
|---|---|
staticFile() | Reference static assets |
prefetch() | Preload assets |
delayRender() / continueRender() | Async loading |
React hooks
Standard React hooks work in code scenes: useState, useEffect, useMemo, useCallback, useRef, and more.
Animation basics
Animations in Remotion are declarative. Instead of timelines and keyframes, you calculate visual values from the current frame.
Interpolation
interpolate() maps an input range to an output range:
const frame = useCurrentFrame();
// Fade in over first 30 frames
const opacity = interpolate(frame, [0, 30], [0, 1]);
// Slide in from left
const translateX = interpolate(frame, [0, 60], [-100, 0]);Add easing for natural motion:
import { Easing } from "remotion";
const scale = interpolate(
frame,
[0, 30],
[0.5, 1],
{ easing: Easing.out(Easing.ease) }
);Springs
For physics-based motion that feels organic:
import { spring, useVideoConfig } from "remotion";
const { fps } = useVideoConfig();
const scale = spring({
frame,
fps,
config: { damping: 10, mass: 0.5 }
});Springs naturally settle into their final value with bounce and overshoot.
Sequences
Break your scene into timed segments:
import { Sequence, AbsoluteFill } from "remotion";
export default function MultiPartScene() {
return (
<AbsoluteFill>
<Sequence from={0} durationInFrames={60}>
<TitleSection />
</Sequence>
<Sequence from={60} durationInFrames={90}>
<ContentSection />
</Sequence>
</AbsoluteFill>
);
}Each Sequence resets useCurrentFrame() to 0 for its children.
Multi-file components
Code scenes can span multiple files. Create utility functions, shared styles, or sub-components:
index.tsx # Main component (entry point)
animations.ts # Reusable animation functions
Logo.tsx # Sub-component
constants.ts # Shared valuesImport between files with relative paths:
import { fadeIn } from "./animations";
import { Logo } from "./Logo";External dependencies
Use npm packages directly in your code:
import { motion } from "framer-motion";
import confetti from "canvas-confetti";Packages are loaded from CDN at render time. Most popular libraries work out of the box.
Styling
Multiple styling approaches work:
Inline styles (most common):<div style={{ backgroundColor: "blue", padding: 20 }} /><div className="bg-blue-500 p-5 rounded-lg" />// At the top of your component
// @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');Props and customization
Make scenes reusable by accepting props:
interface Props {
title: string;
color?: string;
animate?: boolean;
}
export default function TitleScene({
title,
color = "white",
animate = true
}: Props) {
const frame = useCurrentFrame();
const opacity = animate
? interpolate(frame, [0, 30], [0, 1])
: 1;
return (
<AbsoluteFill>
<h1 style={{ color, opacity }}>{title}</h1>
</AbsoluteFill>
);
}Props are passed when the scene is added to a composition.
Performance tips
Memoize expensive calculations:const processedData = useMemo(() => {
return heavyComputation(rawData);
}, [rawData]);prefetch() for videos and images to avoid playback stutter.
Use OffthreadVideo: For embedded videos, OffthreadVideo renders off the main thread.
Error handling
Code scenes are wrapped in an error boundary. If your code throws:
1. The error is caught and logged 2. A fallback is shown in the preview 3. Fix the code and the preview recovers automatically
Check the browser console for detailed error messages.
Common patterns
Typewriter effect
const frame = useCurrentFrame();
const text = "Hello, world";
const charsToShow = Math.floor(frame / 3);
const displayText = text.slice(0, charsToShow);Staggered list items
{items.map((item, index) => {
const delay = index * 10;
const opacity = interpolate(
frame,
[delay, delay + 20],
[0, 1],
{ extrapolateLeft: "clamp" }
);
return <li style={{ opacity }}>{item}</li>;
})}Looping animations
const progress = (frame % 60) / 60; // 0 to 1 every 60 frames
const rotation = progress * 360;For embedding existing video content, see Video Scenes.