Générateur VidTSX 2D
Génère des fichiers vidéo TSX 2D prêts pour la production pour VidTSX à partir d’une description de plan ou de vidéo. Les fichiers sont des compositions Remotion que VidTSX rend image par image, donc la correction est plus importante que l’ingéniosité : une plage interpolate non monotone ou un useState intempestif plantera le rendu.
Cette compétence couvre les graphiques animés 2D. Le travail 3D (@remotion/three / React Three Fiber) est hors de portée.
Comment utiliser cette compétence
- Plan unique clair → générez le fichier
.tsxdirectement. - Demande complexe, multi‑plans ou visuellement ambiguë → confirmez d’abord le découpage ou la direction visuelle en un court message, puis générez. N’en demandez pas trop ; une confirmation d’une ligne suffit.
- Livrez sous forme de fichier
.tsxréel, pas de code en ligne. Ces composants TSX dépassent toujours 20 lignes et l’utilisateur édite ces fichiers directement. Par défaut, un plan par fichier (atomique, facile à éditer) sauf si l’utilisateur demande un livrable unique combiné. - Le fichier contient uniquement le code du composant — les commentaires de section structurelle ci-dessous sont les bienvenus, mais pas de bloc markdown, pas d’explications en prose à l’intérieur du fichier.
- Effectuez le rendu et regardez avant de déclarer terminé — voir Vérifier le rendu. Un plan que vous avez seulement raisonné n’est pas terminé ; un arrêt sur image (et, pour les révélations/défilements, des images à chaque repère) est la barre.
Spécification de sortie
Préréglages de dimensions
| Format | Largeur | Hauteur | Cas d’utilisation |
|---|---|---|---|
| horizontal | 1920 | 1080 | YouTube, présentations |
| vertical | 1080 | 1920 | TikTok, Reels, Shorts |
| carré | 1080 | 1080 | Fil Instagram |
Valeurs par défaut
- Format: horizontal (1920×1080)
- Durée: 5 secondes
- FPS: 30
- Style: minimaliste (voir
references/style-presets.md)
Structure de fichier obligatoire
Chaque fichier généré suit ce squelette. Les commentaires de section étiquetés sont intentionnels — ils gardent les gros fichiers navigables.
import React from 'react';
import {
useCurrentFrame,
useVideoConfig,
interpolate,
Easing,
AbsoluteFill,
Sequence,
} from 'remotion';
// =============================================================================
// CONFIGURATION DE LA COMPOSITION
// =============================================================================
export const compositionConfig = {
id: 'NomDuComposant', // PascalCase uniquement — PAS de tirets ni de underscores
durationInSeconds: 5,
fps: 30,
width: 1920,
height: 1080,
};
// =============================================================================
// CONSTANTES DE STYLE
// =============================================================================
const COLORS = {
primary: '#6366f1',
secondary: '#8b5cf6',
accent: '#06b6d4',
background: '#0f0f23',
text: '#ffffff',
} as const;
const TYPOGRAPHY = {
fontFamily: 'Inter, system-ui, sans-serif',
} as const;
const EASINGS = {
easeOut: Easing.bezier(0.33, 1, 0.68, 1),
easeIn: Easing.bezier(0.32, 0, 0.67, 0),
easeInOut: Easing.bezier(0.37, 0, 0.63, 1),
overshoot: Easing.bezier(0.34, 1.56, 0.64, 1),
} as const;
// =============================================================================
// DONNÉES PRÉ-GÉNÉRÉES (calculées une fois au niveau du module, PAS pendant le rendu)
// =============================================================================
const seededRandom = (seed: number): number => {
const x = Math.sin(seed * 9999) * 10000;
return x - Math.floor(x);
};
// =============================================================================
// COMPOSANT PRINCIPAL
// =============================================================================
const NomDuComposant: React.FC = () => {
const frame = useCurrentFrame();
const { fps, durationInFrames, width, height } = useVideoConfig();
return (
<AbsoluteFill style={{ backgroundColor: COLORS.background }}>
{/* Contenu */}
</AbsoluteFill>
);
};
export default NomDuComposant;
Remplacez NomDuComposant par un nom en PascalCase décrivant le plan, définissez le bloc COLORS à partir du préréglage de style choisi, et réglez width/height/durationInSeconds/fps selon la demande.
Règles de correction
Ce ne sont pas des préférences stylistiques — ce sont ce qui fait réussir un rendu. VidTSX (via Remotion) rend chaque image en appelant le composant à un numéro d’image fixe. Il n’y a pas de boucle d’événements, pas de persistance entre les images, ni de temps réel. Tout ce qui suppose ces éléments plantera.
L’animation est basée sur les images, toujours
- Conduisez tout mouvement à partir de
useCurrentFrame()+interpolate(). - N’utilisez jamais
useState,useEffect,setTimeout,setInterval, ou les animations/transitions CSS. Ils ne correspondent pas au modèle image par image et produisent une sortie cassée ou non déterministe. - Pour tout ce qui est aléatoire ou procédural, utilisez
seededRandom(ou une fonction équivalente ensemencée) afin que chaque rendu d’une image donnée soit identique. Pré‑gérez les tableaux de particules/aléatoires au niveau du module, jamais dans la fonction de rendu — les recalculer par image provoque du scintillement. - Échelonnez les animations pour que les éléments entrent en séquence plutôt que tous en même temps. Cela rend mieux et correspond à l’attendu.
interpolate — les plages d’entrée doivent être strictement croissantes
La plage d’entrée de interpolate doit être strictement monotone croissante. Des valeurs en double ou décroissantes lèvent une erreur à l’exécution.
// ✅ Correct
interpolate(frame, [0, 30, 60], [0, 1, 0]);
// ❌ Lève une erreur — la plage d’entrée décroît
interpolate(frame, [60, 30, 0], [0, 1, 0]);
Pour inverser un mappage, inversez la plage de sortie, jamais la plage d’entrée :
// ✅ Correct — mappe 0→100, 1→0
interpolate(value, [0, 1], [100, 0]);
// ❌ Faux
interpolate(value, [1, 0], [100, 0]);
Pour un timing basé sur l’index, assurez-vous que startFrame < endFrame et appliquez toujours une limite :
const startFrame = index * 30;
const endFrame = startFrame + 30;
interpolate(frame, [startFrame, endFrame], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
Passez toujours extrapolateLeft: 'clamp' et extrapolateRight: 'clamp' sauf si une valeur non bornée est vraiment souhaitée — sans eux, les valeurs dépassent leur plage avant/après les images clés.
Easing — utilisez Easing.bezier(), jamais la syntaxe d’enveloppe
Les formes d’enveloppe comme Easing.out(Easing.cubic) plantent. Définissez des bezier nommés une fois (voir le bloc EASINGS dans le squelette) et référencez‑les :
// ❌ Plante
Easing.out(Easing.cubic);
Easing.in(Easing.quad);
// ✅ Correct
interpolate(frame, [0, 30], [0, 1], {
easing: EASINGS.easeOut,
extrapolateRight: 'clamp',
});
chroma-js — import d’espace de noms uniquement
// ✅ Correct
import * as chroma from 'chroma-js';
const color = chroma('#00bfff').brighten(0.5).hex();
// ❌ Faux — "chroma n'est pas une fonction"
import chroma from 'chroma-js';
@remotion/paths — seules les fonctions réelles existent
Ces assistants n’existent pas et ne doivent jamais être utilisés :
makeCircle(), makeRect(), makeTriangle(), makeLine(), makePie(), makePolygon(), makeEllipse(), makeStar().
Seules ces importations sont valides :
import { evolvePath, getLength, getPointAtLength, getTangentAtLength } from '@remotion/paths';
Écrivez les chaînes de chemin SVG à la main et animez‑les avec evolvePath :
const circlePath = 'M 50 10 A 40 40 0 1 1 49.99 10 Z';
const rectPath = 'M 0 0 L 100 0 L 100 50 L 0 50 Z';
const linePath = 'M 0 0 L 100 100';
const { strokeDasharray, strokeDashoffset } = evolvePath(progress, rectPath);
ID de composition
compositionConfig.id est PascalCase uniquement — pas de tirets, pas de underscores (ex. ProductReveal, pas product-reveal ou product_reveal).
Disposition
Zones de sécurité
- Haut 10% : réserver pour l’interface de la plateforme.
- Bas 15% : réserver pour les légendes/boutons.
- Garder le contenu principal entre 25%–75% verticalement.
Aide au centrage
const centered: React.CSSProperties = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
};
Typographie
| Élément | Taille | Graisse |
|---|---|---|
| Titres | 72–120px | 700–900 |
| Sous‑titres | 36–48px | 500–700 |
| Corps | 28–36px | 400–500 |
Définissez toujours margin: 0 sur les éléments de texte — les valeurs par défaut du navigateur décalent les dispositions du centre.
Préréglages de style
Dans ce dépôt, la valeur par défaut est la marque maison, pas un préréglage. Les plans importent COLORS, GRADIENT, RADIUS, SHADOW, EASINGS depuis remotion/src/brand.ts et FONT_HEADLINE, FONT_DISPLAY, FONT_BODY, FONT_MONO depuis remotion/src/fonts.ts au lieu des blocs locaux COLORS/TYPOGRAPHY du squelette, et suivent brand.md. FONT_HEADLINE (Instrument Serif) a un seul poids : ne lui donnez jamais fontWeight. Les plans se reposent sur le fond jour sauf si leur compositionConfig dit ground: 'night' ; les composants du kit (lib/shorts.tsx, BrandBg) prennent leurs couleurs de là, donc lisez useGround() depuis brand.ts pour toute couleur que vous définissez vous‑même.
Lorsque l’utilisateur nomme un style, chargez sa palette et ses caractéristiques depuis references/style-presets.md et insérez l’objet COLORS dans le squelette. Les six préréglages sont : minimaliste, memphis, néo‑brutalisme, glassmorphisme, néon/cyberpunk, et corporate. Si aucun style n’est nommé, utilisez la marque maison ci‑dessus.
Vérifier le rendu (ne pas sauter)
Un plan n’est pas terminé tant que vous ne l’avez pas regardé. Après avoir écrit le .tsx, effectuez le rendu d’au moins un arrêt sur image et ouvrez‑le — ne remettez jamais un plan que vous avez seulement raisonné. Le rendu réussit et les nombres semblent corrects, pourtant le texte dépasse de sa carte, une image est recadrée au mauvais endroit, un élément est hors écran, ou deux choses se chevauchent. Seule une capture d’écran attrape ces problèmes.
- Effectuez le rendu d’un arrêt sur image (ex.
node scripts/render-all.mjs --still --scale=1 <ShotId>) et lisez le PNG. - Pour les révélations basées sur le temps (éléments entrants sur un repère, défilements, phases programmées), un seul arrêt sur image à 60% de la durée ne suffit pas — effectuez le rendu du clip complet et extrayez des images à chaque moment clé (
ffmpeg -ss <t> -i out/<id>.mp4 -frames:v 1 f.jpg), ou effectuez le rendu d’arrêts sur image à plusieurs décalages. Vérifiez l’état à chaque repère, pas seulement une image. - Vérifiez spécifiquement : les images tiennent dans leur emplacement (aucun contenu important recadré ; préférez
objectFit: containsur un fond correspondant pour des images réelles hétérogènes), le texte n’est pas coupé ou ne déborde pas, rien n’est en dehors de la zone de sécurité, et chaque élément animé est effectivement visible lorsqu’il devrait l’être. - S’il est composité sur d’autres séquences, vérifiez aussi un cadre du rendu final assemblé, pas seulement le plan isolé.
Corrigez ce que la capture d’écran révèle, puis refaites le rendu et regardez à nouveau. Considérez « J’ai effectué le rendu et il semble correct » — avec l’image montrée — comme la barre pour terminer.
Sortie finale
Générez le fichier .tsx complet et rien d’autre à l’intérieur — pas de markdown environnant, pas de commentaire avant ou après le code dans le fichier lui‑même. Un court message d’une ligne dans le chat lors de la remise du fichier est correct.