Créer des vidéo avec React & Remotion

Disponible dans 16 heures

Devenir premium
Résumé Support

Remotion permet de créer des vidéos avec du code en s'appuyant sur React. L'intérêt est double : on peut construire des animations de manière précise, mais aussi automatiser la génération de vidéos puisque tout repose sur des composants, des propriétés et des fichiers manipulables par un script ou un agent IA.

Sommaire

00:00 Introduction
00:39 Installation
02:38 Première animation
13:56 Les séquences
15:27 Remotion Studio
16:40 Séries & Transitions
20:16 Propriétés dynamiques
28:01 Sous-titres dynamiques
41:14 IA & skills
44:13 Conclusion

Installation

Pour démarrer, Remotion propose plusieurs templates que l'on peut installer via la commande :

npx create-video@latest

On vous proposera de choisir entre plusieurs templates (je vous conseille de commencer par blank), puis de choisir quelques éléments de configuration (dont la possibilité d'installer des skills pour les agents d'IA).

Une fois le projet créé, on installe les dépendances puis on lance le studio :

bun install bun run dev

La commande démarre Remotion Studio sur le port 3000 qui permet de prévisualiser les compositions, de se déplacer dans la timeline et de lancer un rendu.

Créer une première composition

Un projet Remotion repose sur des composants React. Le premier composant qui sera utilisé sera le composant Composition qui représente une vidéo et qui permet de définir les informations nécessaires au rendu : son composant React, sa durée, sa fréquence d'image et ses dimensions.

import { Composition, Folder } from "remotion"; import { Base } from "./comps/Base"; const FPS = 30; export const RemotionRoot = () => { return ( <Folder name="Tutoriel"> <Composition component={Base} durationInFrames={FPS * 10} fps={FPS} width={1920} height={1080} id="base" /> </Folder> ); };

La durée s'exprime toujours en frames. Pour une vidéo de 10 secondes à 30 images par seconde, on utilise donc FPS * 10. Le composant rendu peut être un composant React classique qui générera un rendu HTML. Lors du rendu, Remotion effectuera le rendu dans un navigateur, frame par frame, pour générer la vidéo finale.

import { AbsoluteFill, useCurrentFrame } from "remotion"; export function Base() { const frame = useCurrentFrame(); return ( <AbsoluteFill className="bg-black grid place-items-center"> <div className="text-5xl text-white font-bold">{frame}</div> </AbsoluteFill> ); }

AbsoluteFill est un composant pratique fourni par Remotion pour occuper toute la surface de la vidéo. Ensuite, le positionnement et le style peuvent être gérés comme dans une page web. Si vous avez installé Tailwind pendant le démarrage, vous pouvez utiliser les classes que vous avez l'habitude d'utiliser.

Animer avec les frames

Dans Remotion, on évite les animations CSS classiques, car le rendu doit pouvoir avancer précisément dans le temps et rejouer n'importe quelle frame. Les animations se basent donc sur la frame courante, récupérée avec useCurrentFrame().

Pour transformer une frame en valeur animable, on utilise interpolate() :

import { AbsoluteFill, Easing, interpolate, interpolateColors, useCurrentFrame, useVideoConfig, } from "remotion"; import { makeTransform, translateX } from "@remotion/animation-utils"; export function Interpolation() { const frame = useCurrentFrame(); const { fps, width } = useVideoConfig(); const x = interpolate( frame, [10, 10 + fps], [-0.75 * width * 0.5, 0.75 * width * 0.5], { easing: Easing.out(Easing.quad), extrapolateLeft: "clamp", extrapolateRight: "clamp", }, ); const color = interpolateColors( frame, [10, 10 + fps], ["#673333", "#598559"], ); return ( <AbsoluteFill className="bg-black grid place-items-center"> <div className="size-40 rounded-full" style={{ transform: makeTransform([translateX(x)]), background: color, }} /> </AbsoluteFill> ); }

On donne à interpolate() 4 paramètres :

  • La frame courante.
  • Les frames clés.
  • Les valeurs correspondant aux images clés.
  • Des options supplémentaires.

Côté options, on peut utiliser extrapolateLeft et extrapolateRight avec la valeur clamp pour éviter que la valeur continue d'évoluer en dehors des bornes. On peut aussi utiliser un easing pour définir la courbe d'animation.

Si besoin, le package @remotion/animation-utils ajoute des helpers comme makeTransform() et translateX(), qui rendent les transformations CSS plus composables. Pour les couleurs, interpolateColors() fonctionne sur le même principe.

Séquences et timeline

Le composant Sequence permet de décaler une partie de l'animation dans le temps. La frame retournée par useCurrentFrame() devient alors relative à la séquence parente. C'est pratique lorsque l'on veut caler une animation sur un son ou déplacer un élément dans la timeline depuis Remotion Studio.

import { Sequence } from "remotion"; export function Base() { return ( <Sequence name="Balle" from={19}> <BaseInner /> </Sequence> ); }

Dans cet exemple, la séquence commence à la frame globale 19, mais à l'intérieur de BaseInner, la première frame sera 0. Remotion Studio permet aussi de déplacer certaines séquences et de modifier des propriétés via l'inspecteur.

Enchaîner avec des transitions

Pour créer des transitions entre plusieurs séquences, Remotion propose le package @remotion/transitions. Le composant TransitionSeries permet d'enchaîner des séquences les unes après les autres et d'insérer une transition entre elles.

import { linearTiming, springTiming, TransitionSeries, } from "@remotion/transitions"; import { iris } from "@remotion/transitions/iris"; import { wipe } from "@remotion/transitions/wipe"; import { AbsoluteFill } from "remotion"; export function Transition() { return ( <TransitionSeries> <TransitionSeries.Sequence durationInFrames={60}> <AbsoluteFill className="bg-blue-500" /> </TransitionSeries.Sequence> <TransitionSeries.Transition timing={springTiming({ config: { damping: 200 } })} presentation={iris({ width: 1920, height: 1080 })} /> <TransitionSeries.Sequence durationInFrames={60}> <AbsoluteFill className="bg-pink-500" /> </TransitionSeries.Sequence> <TransitionSeries.Transition timing={linearTiming({ durationInFrames: 30 })} presentation={wipe()} /> <TransitionSeries.Sequence durationInFrames={60}> <AbsoluteFill className="bg-green-500" /> </TransitionSeries.Sequence> </TransitionSeries> ); }

Une séquence d'une série n'a pas besoin de from, car les séquences se suivent automatiquement. Le timing contrôle la durée et la courbe temporelle, tandis que la présentation contrôle l'effet visuel, comme fade, wipe ou iris.

Ajouter des propriétés dynamiques

Une composition peut recevoir des propriétés modifiables depuis Remotion Studio. Pour cela, on définit un schéma avec Zod.

import z from "zod"; export const propSchema = z.object({ text1: z.string(), text2: z.string(), }); export function LayerProps(props: z.infer<typeof propSchema>) { return ( <AbsoluteFill className="bg-black grid place-items-center"> <div className="text-white text-8xl font-bold flex flex-col gap-4"> <Sequence name="Titre1" className="contents" durationInFrames={296}> <SlidingTitle text={props.text1} ratio={-0.1} /> </Sequence> <Sequence name="Titre2" className="contents" from={5}> <SlidingTitle text={props.text2} ratio={0.1} /> </Sequence> </div> </AbsoluteFill> ); }

Puis on l'associe à la composition avec schema et defaultProps.

<Composition component={LayerProps} durationInFrames={FPS * 10} fps={FPS} width={1920} height={1080} id="props" schema={propSchema} defaultProps={{ text1: "Titre1", text2: "Titre2" }} />

On peut ensuite utiliser ces propriétés dans le composant et construire une animation qui s'adapte à la durée de la vidéo.

function SlidingTitle({ text, ratio }: { text: string; ratio: number }) { const frame = useCurrentFrame(); const { fps, width, durationInFrames } = useVideoConfig(); const time = [0, fps, durationInFrames - fps, durationInFrames]; const x = interpolate(frame, time, [ratio * width, 0, 0, -ratio * width], { easing: Easing.out(Easing.quad), extrapolateLeft: "clamp", extrapolateRight: "clamp", }); const opacity = interpolate(frame, time, [0, 1, 1, 0], { easing: Easing.out(Easing.quad), extrapolateLeft: "clamp", extrapolateRight: "clamp", }); return ( <div style={{ opacity, transform: makeTransform([translateX(x)]) }}> {text} </div> ); }

Créer des sous-titres dynamiques

Remotion peut aussi servir à générer des formats courts avec des sous-titres synchronisés et animés. Pour afficher une vidéo, on peut utiliser le composant Video du package @remotion/media (si le fichier se trouve dans le dossier public, on peut utiliser la fonction staticFile()).

import { Video } from "@remotion/media"; import { AbsoluteFill, staticFile } from "remotion"; export function Caption() { return ( <AbsoluteFill className="grid place-items-center bg-black text-white"> <Video src={staticFile("video.webm")} /> </AbsoluteFill> ); }

Pour les sous-titres, on peut les générer automatiquement en utilisant la transcription avec des packages comme @remotion/install-whisper-cpp. Ce package permet de gérer l'installation et l'utilisation du modèle. Par exemple :

import path from "path"; import { downloadWhisperModel, installWhisperCpp, transcribe, toCaptions, } from "@remotion/install-whisper-cpp"; import { execSync } from "child_process"; import fs from "fs"; const to = path.join(process.cwd(), "whisper.cpp"); const version = "1.5.5"; const model = "medium"; await installWhisperCpp({ to, version, }); await downloadWhisperModel({ model, folder: to, }); const videoPath = path.join(process.cwd(), "public", "video.webm"); const audioPath = path.join(process.cwd(), "public", "video.wav"); try { // Convert the video audio to a 16KHz wav file alongside the source video. execSync(`ffmpeg -i "${videoPath}" -ar 16000 "${audioPath}" -y`); const whisperCppOutput = await transcribe({ model, language: "fr", whisperPath: to, whisperCppVersion: version, inputPath: audioPath, tokenLevelTimestamps: true, }); // Optional: Apply our recommended postprocessing const { captions } = toCaptions({ whisperCppOutput, }); fs.writeFileSync("public/captions.json", JSON.stringify(captions, null, 2)); } finally { fs.rmSync(audioPath, { force: true }); }

Cela permettra de générer un fichier JSON qui contiendra l'ensemble des mots de la vidéo ainsi que leur timing. À partir de ce fichier, on pourra générer les sous-titres avec @remotion/captions.

import { createTikTokStyleCaptions, type TikTokPage } from "@remotion/captions"; import captions from "../../public/captions.json"; const SWITCH_CAPTIONS_EVERY_MS = 500; const { pages } = createTikTokStyleCaptions({ captions, combineTokensWithinMilliseconds: SWITCH_CAPTIONS_EVERY_MS, });

combineTokensWithinMilliseconds permet de regrouper les mots prononcés dans un même intervalle, ce qui permet ensuite de créer automatiquement des Sequence par "page" de sous-titre (il faudra convertir les valeurs de millisecondes en frames).

{ pages.map((page, index) => { const nextPage = pages[index + 1] ?? null; const startFrame = (page.startMs / 1000) * fps; const endFrame = Math.min( nextPage ? (nextPage.startMs / 1000) * fps : Infinity, startFrame + (SWITCH_CAPTIONS_EVERY_MS / 1000) * fps, ); return ( <Sequence key={index} from={startFrame} durationInFrames={endFrame - startFrame} > <CaptionPage page={page} /> </Sequence> ); }); }

Pour mettre en avant le mot prononcé, on compare le temps absolu courant avec les bornes fromMs et toMs de chaque token.

function CaptionPage({ page }: { page: TikTokPage }) { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const currentTimeMs = (frame / fps) * 1000; const absoluteTimeMs = page.startMs + currentTimeMs; return ( <div className="text-6xl font-bold text-center absolute left-0 right-0 top-[30vh]"> {page.tokens.map((token) => { const isActive = token.fromMs <= absoluteTimeMs && token.toMs > absoluteTimeMs; return ( <span key={token.fromMs} className={isActive ? "text-green-600" : "text-white"} > {token.text} </span> ); })} </div> ); }

La documentation fournit aussi des composants prêts à réutiliser pour créer des effets de sous-titres plus avancés.

IA et automatisation

Comme Remotion produit des vidéos à partir de code, l'outil se prête bien à l'automatisation. Un script ou un agent peut créer une composition, rendre une frame pour vérifier le résultat, puis ajuster le code. Les skills Remotion fournissent des bonnes pratiques et aident l'agent à utiliser les bons composants, comme les séquences, les transitions ou les interpolations.

Cette approche ne remplace pas forcément le travail manuel, mais elle donne une base modifiable. Contrairement à une vidéo générée uniquement par IA, on garde accès au code source et on peut affiner précisément l'animation en cas de besoin.

Rendu et cas d'usage

Remotion peut rendre une vidéo depuis le studio, mais aussi en ligne de commande ou depuis un serveur. Cela ouvre la porte à des pipelines automatisés : génération de sous-titres, création de shorts, vidéos personnalisées ou motion design piloté par des données.

L'outil demande un petit temps d'adaptation, notamment à cause de la logique par frames et des interpolations. Une fois ce modèle compris, il devient très pratique pour piloter des animations et réutiliser la même composition avec des paramètres différents.