🕐 useCurrentFrame() — the clock
On a web page, you would use requestAnimationFrame or a setInterval and accumulate state. In Remotion, forget all that. You just ask: "what frame am I on right now?" — e useCurrentFrame() returns an integer that grows from 0 to the end of the video.
🎯 The central idea
The frame is the entrance and the image is the output. When the frame changes, everything is recalculated from scratch. This hook is the starting point for practically every template in the library.
- •Returns a number: 0 at the beginning, then increases by 1 each time.
- •The component is re-executed for every frame during rendering.
- •You never “advance” time — Remotion does that for you.
import { useCurrentFrame } from "remotion"; export const MyVideo = () => { const frame = useCurrentFrame(); // 0, 1, 2, 3 … return <div>Frame atual: {frame}</div>; };
💡 Practical tip
Want to try it? Open Remotion Studio and drag the timeline. The number that appears is exactly what useCurrentFrame() returns. Seeing the number change as you drag makes the concept concrete.
⚙️ fps and durationInFrames — the measuring stick
The frame alone does not tell you how much time has passed. The one that sets the scale is the fps (frames per second). At 30fps, frame 30 is "1 second." The durationInFrames tells you how many frames the video has in total—it’s your time limit.
📊 Conversions you’ll use all the time
- seconds × fps = frames — 2s at 30fps = 60 frames
- frames ÷ fps = seconds — 90 frames at 30fps = 3s
- 30fps — web/social standard, lightweight to render
- 60fps — smoother, doubles render cost
✓ What to DO
- ✓Set fps and duration in
<Composition>. - ✓Derive timings from fps (
fps * 1.5for 1.5s). - ✓Think of the animation within the durationInFrames window.
✗ What NOT to do
- ✗Fixing "frame 45" without remembering that it changes if the fps changes.
- ✗Animate beyond durationInFrames (it disappears in the final render).
- ✗Mix 30 and 60fps between scenes without converting.
📐 useVideoConfig() — dimensions and fps
How do you get the fps inside the component without hardcoding it? And the width, to center something? The useVideoConfig() returns everything: width, height, fps e durationInFrames from the composition being rendered.
const { width, height, fps } = useVideoConfig(); const frame = useCurrentFrame(); // "1 segundo" funciona em 30 ou 60fps: const apareceu = frame > fps; // centralizar sem saber o tamanho exato: const cx = width / 2;
💡 Why this matters
The same component can run in 16:9 (1920×1080) and 9:16 (1080×1920) without changing a line—if you read width/height from useVideoConfig() instead of hard-coding numbers. That’s how a template becomes reusable across YouTube and Reels.
🎯 The frame as the source of truth
Here’s the golden rule: the image of a frame depends ONLY on the frame number. No useState accumulating, no timers, no “how long has it been since I clicked.” Each frame is a pure function.
The render requests frames out of order
To render quickly, Remotion renders multiple frames in parallel — it may calculate frame 80 before 20. This only works because each one is independent.
Real-time state breaks this
A setInterval assumes time runs continuously. That doesn’t happen in rendering — each frame is an isolated calculation. That’s why the animation comes from the frame, not timers.
Everything derives from an input
Position, opacity, color, scale — they’re all frame functions. When the frame changes, everything is recalculated. This discipline is what keeps the video consistent.
🔁 Determinism — same frame, same image
Direct consequence of the previous rule: rendering frame 42 today, tomorrow, or on another machine produces exactly the same pixel. This is called determinism — and it has a clear enemy: unseeded randomness.
⚠️ Warning: what destroys determinism
- ✗
Math.random()— returns a different value on each calculation, making particles "blink". - ✗
Date.now()/new Date()— changes with each run. - ✗Any external data that changes between renders.
✅ The solution: seeded random()
Remotion offers random("seed"): given the same seed, it always returns the same number. You get visual “randomness” without losing reproducibility.
import { random } from "remotion"; // mesma seed → mesmo valor, sempre: const x = random("particula-7") * width;
⏱️ Think in frames, not seconds
The module’s final habit change: your unit of work becomes the frame. “The title comes in within half a second” becomes “the title comes in within 15 frames.” Seconds only appear at the system boundary—when a human types in a value.
✓ Frame mindset
- ✓"Appears from frame 0 to 15" — straight in the API.
- ✓Stagger: item N starts at frame N×5.
- ✓Convert seconds only at the start:
0.5 * fps.
✗ Second mindset
- ✗Calculate "0.5s" in the middle of the entire animation over and over.
- ✗Mix seconds and frames in the same calculation.
- ✗Forgetting that changing the fps rescales everything in seconds.
💡 Rule of thumb
At 30fps: 15 frames = 0.5s, 30 frames = 1s, 45 frames = 1.5s. Memorize these three and you can estimate any timing in your head.
📌 Module summary
Next module:
1.2 — interpolate(): transform the frame number into opacity, position, and scale.