HyperFrames
L'HTML est la source de vérité pour la vidéo. Une composition est un fichier HTML avec des attributs data-* pour le timing, une timeline GSAP pour l'animation et du CSS pour l'apparence. Le framework gère la visibilité des clips, la lecture des médias et la synchronisation de la timeline.
Approche
Découverte (requêtes exploratoires uniquement)
Pour les demandes ouvertes (« fais-moi une vidéo de lancement de produit », « crée quelque chose pour notre marque ») où l'utilisateur ne s'est pas encore engagé dans une direction, comprenez l'intention avant de choisir les couleurs :
- Audience — qui regarde cela ? Développeurs ? Dirigeants ? Consommateurs généraux ?
- Plateforme — où est-ce diffusé ? Réseaux sociaux (15s), page d'accueil du site, démo produit, interne ?
- Priorité — qu'est-ce qui compte le plus ? Qualité du mouvement ? Exactitude du contenu ? Fidélité à la marque ? Rapidité ?
- Variations — l'utilisateur veut-il des options, ou une seule meilleure tentative ?
Pour les demandes spécifiques (« ajoute une carte-titre », « corrige le timing de la scène 3 »), sautez la phase de découverte.
Pour les requêtes exploratoires, envisagez de proposer 2 à 3 variations qui diffèrent significativement — pas seulement des changements de couleurs, mais des rythmes, niveaux d'énergie ou approches structurelles différents. Une variante sûre/attendue, une ambitieuse. Ne rendez pas cela obligatoire — c'est un outil disponible quand c'est approprié.
Étape 1 : Système de design
Si le fichier design.md ou DESIGN.md existe dans le projet, lisez-le d'abord (vérifiez les deux casses — ce sont des fichiers différents sous Linux). C'est la source de vérité pour les couleurs de la marque, les polices et les contraintes. Utilisez ses valeurs exactes — n'inventez pas de couleurs et ne remplacez pas les polices. N'importe quel format fonctionne (YAML front-matter, prose, tableaux — extrayez simplement les valeurs).
S'il nomme des polices que vous ne trouvez pas localement (pas de répertoire fonts/ avec des fichiers .woff2, pas une police intégrée), avertissez l'utilisateur avant d'écrire le HTML : « design.md spécifie la police [nom de la police] mais aucun fichier de police trouvé. Veuillez ajouter des fichiers .woff2 dans fonts/ ou je vais utiliser un substitut [alternative intégrée la plus proche]. »
Si aucun design.md n'existe, proposez un choix à l'utilisateur :
- L'utilisateur a nommé un style ou une ambiance ? → Lisez visual-styles.md pour les 8 presets nommés. Choisissez le plus proche.
- Souhaite parcourir les options visuellement ? → Lancez le sélecteur de design : lisez references/design-picker.md pour le workflow complet. Cela sert une page de sélection visuelle. L'utilisateur configure l'ambiance, la palette, la typographie et le mouvement dans le navigateur, puis copie le
design.mdgénéré et le colle dans la conversation. - Veut aller vite et sauter cette étape ? → Demandez : ambiance, clair ou foncé, des couleurs/polices de marque ? Choisissez ensuite une palette dans house-style.md.
design.md définit la marque. Il ne définit pas les règles de composition vidéo. Celles-ci viennent de references/video-composition.md et de house-style.md. Utilisez les couleurs de la marque à une échelle adaptée à la vidéo — pas à l'opacité d'une interface web.
Étape 2 : Expansion du prompt
Toujours exécutée sur chaque composition (sauf les pièces à scène unique et les modifications triviales). Cette étape ancre l'intention de l'utilisateur par rapport à design.md et house-style.md et produit un intermédiaire cohérent que tout agent aval lis de la même manière.
Lisez references/prompt-expansion.md pour le processus complet et le format de sortie.
Étape 3 : Planification
Avant d'écrire le HTML, réfléchissez à un haut niveau :
- Quoi — que doit expérimenter le spectateur ? Identifiez l'arc narratif, les moments clés et les battements émotionnels.
- Structure — combien de compositions, lesquelles sont des sous-compositions vs en ligne, quelles pistes transportent quoi (vidéo, audio, incrustations, légendes).
- Rythme — déclarez votre rythme de scène avant d'implémenter. Quelles scènes sont des impacts rapides, lesquelles sont des tenues, où les shaders atterrissent, où l'énergie atteint son pic. Nommez le modèle : rapide-rapide-LENT-rapide-SHADER-tenue. Lisez references/beat-direction.md pour les modèles de rythme.
- Timing — quels clips déterminent la durée, où se situent les transitions, quel est le rythme.
- Mise en page — construisez d'abord l'état final. Voir « Mise en page avant animation » ci-dessous.
- Animer — ajoutez ensuite le mouvement en utilisant les règles ci-dessous.
Construisez ce qui a été demandé. Une demande pour « une carte-titre » n'est pas une demande pour « une carte-titre + 3 scènes de soutien + musique d'ambiance + légendes. » Chaque scène, chaque élément, chaque tween doit gagner sa place. Si des scènes ou éléments supplémentaires amélioreraient réellement la pièce, proposez-les — ne les ajoutez pas.
Pour les petites modifications (corriger une couleur, ajuster le timing, ajouter un élément), passez directement aux règles.
Mise en page avant animation
Positionnez chaque élément là où il doit être à son moment le plus visible — l'image où il est complètement entré, correctement placé, et pas encore en sortie. Écrivez d'abord ceci en HTML+CSS statique. Pas encore de GSAP.
Pourquoi c'est important : Si vous positionnez les éléments à leur état de départ animé (hors écran, échelle 0, opacité 0) et que vous les animez vers où vous pensez qu'ils devraient atterrir, vous devinez la mise en page finale. Les chevauchements sont invisibles jusqu'à ce que la vidéo soit rendue. En construisant l'état final d'abord, vous pouvez voir et corriger les problèmes de mise en page avant d'ajouter du mouvement.
Le processus
- Identifiez l'image héroïque de chaque scène — le moment où le plus d'éléments sont simultanément visibles. C'est la mise en page que vous construisez.
- Écrivez le CSS statique pour cette image. Le conteneur
.scene-contentDOIT remplir toute la scène avecwidth: 100%; height: 100%; padding: Npx;avecdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Utilisez le padding pour pousser le contenu vers l'intérieur — JAMAISposition: absolute; top: Npxsur un conteneur de contenu. Les conteneurs de contenu en position absolue débordent lorsque le contenu est plus haut que l'espace restant. Réservezposition: absoluteuniquement pour les éléments décoratifs. - Ajoutez les entrées avec
gsap.from()— animez DEPUIS hors écran/invisible VERS la position CSS. La position CSS est la vérité de base ; le tween décrit le voyage pour y arriver. (Dans les sous-compositions chargées viadata-composition-src, préférezgsap.fromTo()— voir les règles GSAP porteuses dans references/motion-principles.md.) - Ajoutez les sorties avec
gsap.to()— animez VERS hors écran/invisible DEPUIS la position CSS.
Exemple
/* scene-content remplit la scène, le padding positionne le contenu */
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
gap: 24px;
box-sizing: border-box;
}
.title {
font-size: 120px;
}
.subtitle {
font-size: 42px;
}
/* Le conteneur remplit toute taille de scène (1920x1080, 1080x1920, etc).
Le padding positionne le contenu. Flex + gap gère l'espacement. */
MAUVAIS — dimensions codées en dur et positionnement absolu :
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Étape 3 : Animer VERS ces positions
tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
// Étape 4 : Animer DEPUIS ces positions
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
Quand les éléments partagent de l'espace dans le temps
Si l'élément A sort avant que l'élément B n'entre dans la même zone, les deux doivent avoir des positions CSS correctes pour leurs images héroïques respectives. L'ordre de la timeline garantit qu'ils ne coexistent jamais visuellement — mais si vous sautez l'étape de mise en page, vous ne détecterez pas le cas où ils se chevauchent accidentellement en raison d'une erreur de timing.
Ce qui est considéré comme un chevauchement intentionnel
Les effets superposés (lueur derrière le texte, éléments d'ombre, motifs d'arrière-plan) et les conceptions empilées en z (piles de cartes, couches de profondeur) sont intentionnels. L'étape de mise en page sert à détecter les chevauchements non intentionnels — deux titres atterrissant l'un sur l'autre, une statistique couvrant une étiquette, un contenu débordant du cadre.
Attributs de données
Tous les clips
| Attribut | Requis | Valeurs |
|---|---|---|
id | Oui | Identifiant unique |
data-start | Oui | Secondes ou référence d'ID de clip ("el-1", "intro + 2") |
data-duration | Requis pour img/div/compositions | Secondes. La vidéo/audio utilise par défaut la durée du média. |
data-track-index | Oui | Entier. Les clips de même piste ne peuvent pas se chevaucher. |
data-media-start | Non | Décalage de coupe dans la source (secondes) |
data-volume | Non | 0-1 (défaut 1) |
data-track-index n'affecte pas la superposition visuelle — utilisez le CSS z-index.
Clips de composition
| Attribut | Requis | Valeurs |
|---|---|---|
data-composition-id | Oui | Identifiant unique de composition |
data-start | Oui | Temps de début (composition racine : utilisez "0") |
data-duration | Oui | Prédomine sur la durée de la timeline GSAP |
data-width / data-height | Oui | Dimensions en pixels (1920x1080 ou 1080x1920) |
data-composition-src | Non | Chemin vers un fichier HTML externe |
data-variable-values | Non | Objet JSON des remplacements de variables par instance sur un hôte de sous-comp |
Sur l'élément racine <html> :
| Attribut | Requis | Valeurs |
|---|---|---|
data-composition-variables | Non | Tableau JSON des variables déclarées (id/type/label/default) — pilote l'interface d'édition Studio et fournit les valeurs par défaut pour getVariables() |
Structure dune composition
Les sous-compositions chargées via data-composition-src utilisent une enveloppe <template>. Les compositions autonomes (le main index.html) n'utilisent PAS <template> — elles placent le div data-composition-id directement dans <body>. Utiliser <template> sur un fichier autonome cache tout contenu du navigateur et casse le rendu.
Structure dune sous-composition :
<template id="mon-comp-template">
<div data-composition-id="mon-comp" data-width="1920" data-height="1080">
<!-- contenu -->
<style>
[data-composition-id="mon-comp"] {
/* styles scopés */
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// tweens...
window.__timelines["mon-comp"] = tl;
</script>
</div>
</template>
Chargement dans la racine : <div id="el-1" data-composition-id="mon-comp" data-composition-src="compositions/mon-comp.html" data-start="0" data-duration="10" data-track-index="1"></div>
Variables (Compositions paramétrées)
Rendez la même composition avec un contenu différent — titre, couleur de thème, prix, légendes — sans modifier le code HTML source.
Modèle en trois étapes :
- Déclarez les variables sur la racine
<html>de la composition avecdata-composition-variables. Chaque entrée nécessiteid,type(parmistring,number,color,boolean,enum),labeletdefault. Les entrées Enum nécessitent égalementoptions: [{value, label}, ...]. - Lisez les valeurs résolues dans le script de la composition avec
window.__hyperframes.getVariables(). Retourne le résultat fusionné des valeurs par défaut déclarées + des remplacements par instance + des remplacements CLI. - Remplacez au moment du rendu avec
npx hyperframes render --variables '{...}'(au niveau supérieur) ou avecdata-variable-values='{...}'sur l'élément hôte (par instance pour les sous-compositions).
<!doctype html>
<html
data-composition-variables='[
{"id":"titre","type":"string","label":"Titre","default":"Bonjour"},
{"id":"theme","type":"enum","label":"Thème","default":"clair","options":[
{"value":"clair","label":"Clair"},
{"value":"sombre","label":"Sombre"}
]}
]'
>
<body>
<div data-composition-id="root" data-width="1920" data-height="1080">
<h1 id="hero" class="clip" data-start="0" data-duration="3"></h1>
<script>
const { titre, theme } = window.__hyperframes.getVariables();
document.getElementById("hero").textContent = titre;
document.body.dataset.theme = theme;
</script>
</div>
</body>
</html>
# L'aperçu de développement utilise les valeurs par défaut déclarées
npx hyperframes preview
# Rendu avec des remplacements
npx hyperframes render --variables '{"titre":"Rapport Q4","theme":"sombre"}' --output q4.mp4
# Ou à partir d'un fichier JSON
npx hyperframes render --variables-file ./vars.json
Valeurs par instance dune sous-composition : la même fonction getVariables() fonctionne à l'intérieur des sous-compositions chargées via data-composition-src. Chaque élément hôte transmet ses propres valeurs :
<div
data-composition-id="carte-pro"
data-composition-src="compositions/carte.html"
data-variable-values='{"titre":"Pro","prix":"$29"}'
></div>
<div
data-composition-id="carte-entreprise"
data-composition-src="compositions/carte.html"
…
[tronqué pour la traduction]