Guide Maître Remotion — Création Vidéo Programmatique avec React
Remotion permet de créer des vidéos cinématographiques avec du code React pur. Chaque frame vidéo est traitée comme un état déterministe (frame) => UI, produisant des visuels parfaits à 60 FPS sans saccade ni perte d'image.
1. Architecture de Composition & Configuration Racine
A. Point d'Entrée Racine (Root.tsx)
import { Composition } from 'remotion';
import { MainExplainer } from './compositions/MainExplainer';
import { VerticalShort } from './compositions/VerticalShort';
export const RemotionRoot: React.FC = () => {
return (
<>
{/* Vidéo explicative paysage 16:9 */}
<Composition
id="MainExplainer-1080p"
component={MainExplainer}
durationInFrames={30 * 32} // 32 secondes @ 30 FPS = 960 frames
fps={30}
width={1920}
height={1080}
defaultProps={{
title: 'Le Mystère de la Fonte des Glaces Polaires',
author: 'NARA Studio',
themeColor: '#f97316',
}}
/>
{/* Format vertical 9:16 (TikTok / Reels / Shorts) */}
<Composition
id="VerticalShort-60fps"
component={VerticalShort}
durationInFrames={60 * 15} // 15 secondes @ 60 FPS = 900 frames
fps={60}
width={1080}
height={1920}
/>
</>
);
};
B. Règles de Durée & FPS
- YouTube / Explicatives 16:9 : Recommandation
30 fps(équilibre entre vitesse de rendu et fluidité) ou60 fpspour une typographie cinétique ultra-nette. - Durée en Frames : Calculez toujours
durationInFrames = Math.round(secondes * fps). - Zéro Saccade : N'utilisez pas
setIntervalousetTimeout. Utilisez toujoursuseCurrentFrame()etuseVideoConfig().
2. Primitives de Disposition & Chronologie
AbsoluteFill
Base de toute la couche de la scène. position: absolute; inset: 0; width: 100%; height: 100%; automatiquement :
import { AbsoluteFill } from 'remotion';
export const SceneBackground: React.FC = () => {
return (
<AbsoluteFill style={{ backgroundColor: '#090812', overflow: 'hidden' }}>
{/* Contenu visuel */}
</AbsoluteFill>
);
};
Sequence vs Series
Sequence: Place un élément à un moment absolu (frometdurationInFrames). Idéal pour l'audio de fond, les filigranes, les superpositions HUD, ou les éléments qui se chevauchent.Series: Ordonne les scènes séquentiellement automatiquement sans avoir à calculer manuellement le décalage des frames.
import { Series, Sequence, Audio, staticFile } from 'remotion';
export const VideoStory: React.FC = () => {
return (
<AbsoluteFill>
{/* Musique de fond tout au long de la vidéo */}
<Audio src={staticFile('audio/ambient_score.mp3')} volume={0.35} />
{/* Séquence automatique des scènes */}
<Series>
<Series.Sequence durationInFrames={120}>
<HookScene />
</Series.Sequence>
<Series.Sequence durationInFrames={180}>
<ProblemScene />
</Series.Sequence>
<Series.Sequence durationInFrames={240}>
<SolutionScene />
</Series.Sequence>
<Series.Sequence durationInFrames={150}>
<CallToActionScene />
</Series.Sequence>
</Series>
</AbsoluteFill>
);
};
3. Physique d'Animation : spring() & interpolate()
A. Formule de Ressort Naturel (spring())
Utilisez spring() pour générer un mouvement physique réaliste (élimine l'impression rigide "PowerPoint") :
import { spring, useCurrentFrame, useVideoConfig } from 'remotion';
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// 1. Apparition Snappy (Texte/Badge surgissant)
const popScale = spring({
frame,
fps,
config: { damping: 10, mass: 0.4, stiffness: 180 },
});
// 2. Glissement Doux (Caméra qui se déplace en douceur)
const smoothSlide = spring({
frame,
fps,
config: { damping: 20, mass: 1.2, stiffness: 60 },
});
// 3. Rebond (Icône/particule qui tombe et rebondit)
const bounce = spring({
frame: frame - 10, // retard de 10 frames
fps,
config: { damping: 8, mass: 0.6, stiffness: 140 },
});
B. Mappage de Valeurs Progressif (interpolate())
import { interpolate, Easing } from 'remotion';
// Transition Alpha & Rotation avec Easing Bezier
const opacity = interpolate(frame, [0, 20], [0, 1], {
extrapolateRight: 'clamp',
});
const rotateY = interpolate(frame, [0, 60], [-45, 0], {
easing: Easing.bezier(0.16, 1, 0.3, 1),
extrapolateRight: 'clamp',
});
4. Typographie Cinétique & Synchronisation Voice-Over
A. Dévoilement Échelonné des Mots (Texte apparaissant mot par mot)
import React from 'react';
import { useCurrentFrame, useVideoConfig, spring, interpolate } from 'remotion';
interface KineticTextProps {
sentence: string;
startFrame?: number;
wordDelay?: number;
}
export const KineticText: React.FC<KineticTextProps> = ({
sentence,
startFrame = 0,
wordDelay = 4, // 4 frames d'intervalle entre les mots
}) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const words = sentence.split(' ');
return (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px', justifyContent: 'center' }}>
{words.map((word, idx) => {
const wordStart = startFrame + idx * wordDelay;
const progress = spring({
frame: frame - wordStart,
fps,
config: { damping: 12, mass: 0.4, stiffness: 160 },
});
const translateY = interpolate(progress, [0, 1], [40, 0]);
const opacity = interpolate(progress, [0, 1], [0, 1]);
return (
<span
key={idx}
style={{
display: 'inline-block',
transform: `translateY(${translateY}px)`,
opacity,
fontSize: '48px',
fontWeight: 800,
color: idx === 0 ? '#f97316' : '#ffffff',
letterSpacing: '-1px',
fontFamily: "'JetBrains Mono', sans-serif",
}}
>
{word}
</span>
);
})}
</div>
);
};
B. Synchronisation Voice-Over & Effets Sonores
- Placez les fichiers audio dans le dossier
public/audio/. - Utilisez
staticFile('audio/file.mp3'). - Créez des transitions audio fondu/enchaîné en utilisant la propriété
volume:
<Audio
src={staticFile('audio/naskah_vo.mp3')}
volume={(f) =>
interpolate(f, [0, 15, 880, 900], [0, 1, 1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
})
}
/>
5. Ressources Asynchrones & Préchargement (delayRender)
Lorsqu'une vidéo charge des polices personnalisées, des images externes ou des données d'API, Remotion DOIT en être informé pour éviter de rendre des frames vides :
import { delayRender, continueRender } from 'remotion';
import { useEffect, useState } from 'react';
export const DynamicScene: React.FC = () => {
const [handle] = useState(() => delayRender('Chargement des polices & ressources'));
useEffect(() => {
document.fonts.ready.then(() => {
continueRender(handle);
});
}, [handle]);
return <AbsoluteFill>...</AbsoluteFill>;
};
6. Template & Recettes Motion Graphics NARA
Formule en 8 Étapes Cinématographiques (Anti-PPT)
- Scène 1 : Accroche Initiale (00-03s) $ ightarrow$ Visuel mystérieux + grande typographie cinétique.
- Scène 2 : Énoncé du Problème (04-07s) $ ightarrow$ Visuel de glace qui s'effondre / graphique rouge incandescent.
- Scène 3 : Analyse Approfondie des Données (08-12s) $
ightarrow$ Chiffres statistiques qui augmentent (
Math.floor). - Scène 4 : Changement d'Échelle Dynamique (13-16s) $ ightarrow$ Transition d'échelle macro à micro.
- Scène 5 : Impact Humain (17-20s) $ ightarrow$ Personnage en dessin animé réagissant sur la toile.
- Scène 6 : Révélation de la Solution (21-25s) $ ightarrow$ Visuel passant aux couleurs vert/orange vif.
- Scène 7 : Étapes d'Action (26-29s) $ ightarrow$ 3 points avec cartes flottantes 3D.
- Scène 8 : Apogée & Appel à l'Action (30-32s) $ ightarrow$ Logo du studio qui pulse + effet sonore whoosh.
7. Exécution du Rendu CLI & Accélération Matérielle
A. Rendu Vidéo Unique (1080p 60fps)
npx remotion render src/remotion/index.ts MainExplainer-1080p out/output.mp4 --gl=angle
B. Rendu Rapide avec Haute Concurrence
npx remotion render src/remotion/index.ts MainExplainer-1080p out/output.mp4 --concurrency=8 --gl=angle --jpeg-quality=90
C. Flags de Rendu Importants
--gl=angle: Utilise l'accélération GPU DirectX sous Windows (beaucoup plus rapide que le rasteriseur logiciel).--gl=swiftshader: Solution de repli logicielle si le GPU n'est pas disponible dans un conteneur/CI.--concurrency=N: Nombre de cœurs CPU utilisés pour le rendu parallèle.--muted: Rend sans flux audio si seul le visuel est nécessaire.--props='{"title":"Nouveau"}': Passe des paramètres dynamiques via JSON en ligne de commande.
8. Guide de Dépannage
- Écran Noir / Image qui n'apparaît pas : Assurez-vous que la ressource se trouve dans le dossier
public/et qu'elle est appelée avecstaticFile('nom_fichier'). - Vidéo qui se bloque pendant le rendu : Vérifiez s'il y a une boucle infinie dans
useMemoou un composant React qui ne ferme pasdelayRender. - Audio Désynchronisé : Assurez-vous que la valeur
fpsde la composition correspond exactement au calcul des secondes du voice-over (secondes = frames / fps).