Création de programmes vos
Vous générez des fichiers VosConfigJson — des configurations d'animation déclaratives compilées en animations Three.js + dialecte GSAP sur le moteur vos (github.com/vosjs/vos, MIT). L'aperçu est le rendu : une configuration produit les mêmes images sur toutes les machines.
Flux de travail
- Analyser la demande : identifier le concept d'animation, l'ambiance, les couleurs et toute image référencée.
- Concevoir l'approche : choisir le préréglage de caméra, les matériaux, les effets et la stratégie d'animation (voir le guide de catégories ci-dessous).
- Générer la configuration : écrire un VosConfigJson complet et valide dans
<kebab-case-name>.jsonlà où le projet stocke ses configurations. - Valider localement (voir ci-dessous), puis effectuer le rendu ou l'aperçu.
Si l'utilisateur fournit une image, analysez son style visuel (couleurs, ambiance, composition, éclairage) et traduisez ces qualités dans la scène.
Valider et effectuer le rendu localement
npm i @vosjs/core # compilateur + lints
node -e "import('@vosjs/core/compiler').then(async m => { \
const cfg = JSON.parse(require('fs').readFileSync('my-config.json','utf8')); \
m.compileVosConfig(cfg); console.error('compile propre') })"
compileVosConfig(config) ne doit pas lever d'exception — c'est le même compilateur que
chaque lecteur et serveur de rendu exécute. Pour les vérifications visuelles, l'interface CLI
(npm i -D @vosjs/cli) :
vos render my-config.json out.webm # rendu vidéo déterministe
vos still my-config.json out.webp --time 2.5
vos preview my-config.json # page de lecture locale
vos info my-config.json
Partager pour un aperçu (sans compte nécessaire)
Le rendu local est la valeur par défaut — rien ne quitte la machine sauf si l'utilisateur demande un partage ou un hébergement. S'il souhaite le voir hébergé (lien jouable, réglage fin dans le studio vos.so) et qu'aucune clé API n'est configurée, utilisez la poussée réclamable :
vos push my-config.json --claimable --title "…"
# → claim: https://vos.so/claim/… expiration : <72h à partir de maintenant>
# Pas de CLI, ou une version ancienne ? La même chose via HTTP simple :
curl -s -X POST https://vos.so/api/claim \
-H 'content-type: application/json' \
-d '{"title": "…", "config": <le VosConfigJson>}'
# → { "claimUrl": "https://vos.so/claim/…", "expiresAt": "…" }
Transmettez claimUrl à l'utilisateur et nulle part ailleurs — c'est la seule référence et
la seule information d'identification. Elle dure 72 heures ; le travail non réclamé est supprimé après
cette période (repousser si elle expire). Réclamer déplace le vos dans la bibliothèque
utilisateur. Avec une clé configurée (VOS_API_KEY ou vos login), préférez
vos push — les poussées avec clé n'ont pas d'expiration. Programmes uniquement, config ≤200 Ko,
5 poussées par jour par réseau.
Structure de VosConfigJson
{
"version": 2,
"duration": 8,
"scene": { "background": "#0a0a1a", "fog": { "type": "exp2", "color": 0, "density": 0.02 } },
"camera": { "preset": "perspective", "fov": 60, "position": [0, 2, 8] },
"postprocessing": [{ "type": "bloom", "strength": 0.8 }, { "type": "output" }],
"elements": [{ "type": "text", "id": "title", "content": "Hello", "position": "center", "font": { "size": 64, "color": "#fff" } }],
"setup": "async (ctx) => { ... return data }",
"createContent": "(ctx, setupData?) => { ... return { objects, refs, dispose } }",
"createTimeline": "(ctx, content, duration) => { ... return tl }"
}
Référence complète des champs : references/schema-reference.md. Exemples
fonctionnels de chaque type principal : references/examples.md.
Règles critiques
À FAIRE
- Les fonctions doivent être des chaînes JavaScript valides — PAS de TypeScript (pas de
as, pas d'annotations de type) - Accédez à Three.js via
ctx.THREE, au dialecte GSAP viactx.gsap, à la scène viactx.scene createContentDOIT retourner{ objects: [...], refs: {...}, dispose: () => {...} }createTimelineDOIT retournerctx.gsap.timeline({ paused: true })- Incluez toujours
dispose()qui nettoie les géométries, matériaux, textures - Utilisez
ctx.resolution.drawingBufferWidth/drawingBufferHeightpour leiResolutiondu shader - Définissez
mesh.frustumCulled = falsepour les quads de shader plein écran - Définissez
depthWrite: false, depthTest: falsesur les ShaderMaterial plein écran - Incluez toujours
{ "type": "output" }comme dernière entrée lors de l'utilisation du post-traitement - Ajoutez tous les objets de la scène à la fois à
sceneet au tableauobjectsretourné - Animez les uniforms du shader directement :
tl.to(uniforms.iTime, { value: duration, duration, ease: 'none' }) - Pour la logique par trame (mises à jour InstancedMesh, mathématiques de caméra), utilisez des rappels
onUpdatesur les interpolations
À NE PAS FAIRE
- PAS de littéraux de gabarit
${}dans les chaînes de fonction — ils sont évalués au moment de la compilation. Utilisez la concaténation de chaînes. - PAS d'échappement de backtick — écrivez les shaders sous forme de chaînes inline, ou utilisez la concaténation sur une seule ligne
- JAMAIS
repeat: -1sur des interpolations individuelles — la boucle est gérée par le lecteur ; cela casse le suivi de la durée - PAS d'objets
stylede type CSS ni detextContentsur les éléments — les éléments sont des plans WebGL - PAS d'imports externes — tout est sur
ctx(THREE, gsap, chargeurs, utilitaires) - PAS de
requestAnimationFramenisetTimeout— utilisez des rappelsonUpdate - PAS de
Date.now()/Math.random()dans les chemins d'image — le déterminisme est le contrat ; dérivez tout du temps de la timeline et dectx.data - PAS de
onFramepour les nouvelles configurations — mettez toute la logique par trame dansonUpdate
Paramètres et Looks (rendre le programme remixable)
Déclarez 2 à 4 paramètres via config.params — les éditeurs les affichent comme des contrôles
en direct, et d'autres agents peuvent ajuster le programme sans toucher au code. Chaque paramètre
documente une clé ctx.data que le programme lit :
"params": [{ "key": "hue", "label": "Teinte", "kind": "number",
"min": 0, "max": 1, "default": 0.6,
"hint": "Décale chaque couleur autour de la roue",
"unit": "°", "group": "Couleur", "order": 1 }]
- kinds :
number(min/max/step) |color|select(options) |toggle hint: UNE phrase sur ce qui change visiblement — écrivez-la- le programme la lit là où il anime :
const d = ctx.data || {}; if (typeof d.hue === 'number') u.uHue.value = d.hue - Nommez les paramètres par l'INTENTION (
mood,drama,pace), jamais par l'implémentation (blurRadius). Chaque paramètre doit changer visiblement la sortie — un paramètre mort est pire que pas de paramètre.
Livrez des Looks via config.presets — ensembles nommés de valeurs de paramètres (2 à 3 si le
programme a 4 paramètres ou plus) :
"presets": [{ "name": "Vif", "values": { "hue": 0.9, "speed": 2 } },
{ "name": "Calme", "values": { "hue": 0.3, "speed": 0.6 } }]
Les valeurs doivent référencer des clés de paramètres déclarés avec des types correspondants.
Mise en garde pour le rendu serveur
Les configurations poussées vers vos.so sont rendues en prévisualisation sur une flotte logicielle-GL : pas
de THREE.DoubleSide sur les matériaux de transmission (blocage dur), pas de dispersion
(trop lent), et toute ressource récupérée doit être accessible et ouverte CORS. Ceux-ci sont
corrects pour des rendus purement locaux sur un vrai GPU.
Principes de conception
- Impact visuel : bloom pour la lueur (même 0.3–0.5 élève); matériaux émissifs + bloom = émetteurs de lumière; brouillard exp2 (0.01–0.05) pour la profondeur; arrière-plans sombres font ressortir les couleurs; verre (transmission + environnement HDR) donne un aspect premium; un léger mouvement de caméra apporte de la vie.
- Qualité d'animation : boucles de 4 à 10 secondes, récits de 10 à 30 secondes;
ease: 'none'pour un mouvement constant;power2.inOut/sine.inOutpour les transitions; effet cascade pour des révélations séquentielles; caler les interpolations à différents moments de début. - Couleur : 2 à 3 couleurs complémentaires maximum; clé chaude + rim froide; tons
sombres (
0x0a0a1a,0x1a1a2e) pour les arrière-plans. - Performance :
InstancedMeshpour 10+ objets identiques; 5k–20k Points, 100–500 InstancedMesh; disposez tout.
Guide de sélection des catégories
| Concept | Caméra | Technique |
|---|---|---|
| Art de shader / fractales | fullscreen | ShaderMaterial + interpolations d'uniforms |
| Objets 3D | perspective | Mesh + Material + lumières |
| Isométrique/plat | orthographic | InstancedMesh + positions calculées |
| Présentation de texte | perspective ou fullscreen | TextGeometry (3D) ou elements (2D) |
| Verre/réfraction | perspective | MeshPhysicalMaterial + carte d'environnement HDR |
| Particules | perspective | Points + shader ou InstancedMesh |
| Média mixte | perspective | Scène 3D + tableau elements |