E1 · First MP4 in React
10 minRun the "install, render, and check" block.
Done when: npm: 0, render: 0, and OK: 5,0 s · 1920×1080 · 30 fps · sem áudio with saída: 0.
Lesson 5 · 15 min
Remotion does the same thing as HyperFrames in another language: each frame is a React component that receives the frame number. interpolate maps frames to values and spring gives physics to an entrance. In this lesson you render the same idea from lesson 2 in React, read the license, and decide when each engine is worth more.
Start the lesson
Goal: render a Remotion composition with interpolate and spring and choose between Remotion and HyperFrames.
Tap each piece. Compare with the diagram from lesson 1: it is the same drawing in React.
HyperFrames: time in seconds and GSAP. Remotion: time in frames and React.
Title with spring, rule, and subtitle with interpolate. The package.json pins remotion 4.0.537 (npm, 10/19/2026).
import {AbsoluteFill, Composition, Easing, interpolate, registerRoot, spring, useCurrentFrame, useVideoConfig} from 'remotion';
// Everything is a function of the current frame: the same frame always draws the same image.
const Abertura: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
// spring: enters with physics (0 → 1), starting at frame 10
const entra = spring({frame: frame - 10, fps, config: {damping: 200}});
// interpolate: maps frames to values; clamp holds the value at the ends
const apoio = interpolate(frame, [30, 50], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'});
const barra = interpolate(frame, [20, 60], [0, 640], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.inOut(Easing.cubic),
});
return (
<AbsoluteFill style={{background: '#0c0c10', justifyContent: 'center', paddingLeft: 200, fontFamily: 'system-ui, sans-serif'}}>
<h1 style={{color: '#f4f4f5', fontSize: 120, lineHeight: 1.1, margin: 0, opacity: entra, transform: `translateY(${(1 - entra) * 60}px)`}}>
Vídeo é React
</h1>
<div style={{width: barra, height: 6, background: '#e9b949', margin: '36px 0'}} />
<p style={{color: '#a1a1aa', fontSize: 48, margin: 0, opacity: apoio}}>Um componente, um quadro, um MP4.</p>
</AbsoluteFill>
);
};
const Raiz: React.FC = () => (
<Composition id="Abertura" component={Abertura} durationInFrames={150} fps={30} width={1920} height={1080} />
);
registerRoot(Raiz);{
"name": "abertura-remotion",
"private": true,
"description": "Ensaio do curso Motion por código com agente (INEMA.CLUB): uma composição Remotion mínima.",
"scripts": {
"render": "remotion render src/index.tsx Abertura out/abertura.mp4"
},
"dependencies": {
"@remotion/cli": "4.0.537",
"react": "19.3.1",
"react-dom": "19.3.1",
"remotion": "4.0.537"
}
}K=~/projetos/formacao-cinema-ia-motion-v72/kit
E=${TMPDIR:-/tmp}/ensaio-motion/a5; rm -rf "$E"; mkdir -p "$E/remotion"
cp -r "$K"/remotion/{package.json,package-lock.json,src} "$E/remotion/"; cd "$E/remotion"
npm ci --no-audit --no-fund > npm.log 2>&1; echo "npm: $?"
npx remotion render src/index.tsx Abertura out/abertura.mp4 --concurrency=2 > render.log 2>&1
echo "render: $?"
python3 "$K/conferir.py" out/abertura.mp4 5 1920x1080; echo "saída: $?"npm: 0 render: 0 OK: 5,0 s · 1920×1080 · 30 fps · sem áudio saída: 0
npm ci installs 245 MB into node_modules (measured on this machine). The first render may download the browser that Remotion uses.
Ask for the render with the id in lowercase.
cd ${TMPDIR:-/tmp}/ensaio-motion/a5/remotion
npx remotion render src/index.tsx abertura out/errado.mp4 2>&1 | grep -m1 -o "Could not find composition.*"
echo "render: ${PIPESTATUS[0]}"npx skills add remotion-dev/skills
Could not find composition with ID abertura. Available compositions: Abertura render: 1
The id is case-sensitive. The message lists the ids that exist: copy from there.
What to read before using it in a company project.
Remotion's LICENSE.md allows free use for individuals, nonprofit organizations, and companies of up to 3 people; above that, the company needs the paid license.
HyperFrames is Apache-2.0: free to use, including commercially.
The Remotion 5.0 migration page mentions a license change (contractors would start counting toward the team). On 10/19/2026, 5.0 hadn't been released and the rule wasn't confirmed: reread LICENSE.md when you update.
A team that already writes React, components reused from an app, data passed through props, video generated inside a React product.
A piece that an agent writes from scratch, with no build; a check with built-in layout and contrast; frame snapshots; local TTS and transcription in the same CLI (lesson 7).
The rehearsal, the error, and a change of your own.
Run the "install, render, and check" block.
Done when: npm: 0, render: 0, and OK: 5,0 s · 1920×1080 · 30 fps · sem áudio with saída: 0.
Run the wrong-id block.
Done when: Could not find composition with ID abertura. Available compositions: Abertura and render: 1.
In /tmp/ensaio-motion/a5/remotion/src/index.tsx, change durationInFrames to 180 and the bar's easing to Easing.out(Easing.exp). Render again with the same command from the block.
Done when: python3 ~/projetos/formacao-cinema-ia-motion-v72/kit/conferir.py out/abertura.mp4 6 1920x1080; echo "saída: $?" → OK: 6,0 s · 1920×1080 · 30 fps · sem áudio and saída: 0.
The explanation appears after you answer.
Question 1 of 4
durationInFrames={150} at fps={30} gives how many seconds?
Question 2 of 4
What is extrapolateRight: 'clamp' in interpolate for?
Question 3 of 4
A 12-person company wants to use Remotion 4 in client videos. What should it do first?
Question 4 of 4
When is Remotion usually worth more than HyperFrames?
Pick one option. The explanation appears after your attempt.