Film Génératif
Produit une vidéo MP4 de 45 à 120 s, 1920×1080, 30 ips dans laquelle chaque image est dessinée en code (pycairo + Pillow) et chaque note est synthétisée (numpy/scipy). Il n'y a pas d'images d'archive, pas d'images IA et pas de musique sous licence. Le film de référence est SŪTRA (civilisation indienne, 87 s). Il est stylisé comme une épreuve d'impression risographie, et un fil rouge traverse dix chapitres sur une grille rythmique à 96 BPM.
La qualité provient de trois éléments. Respectez-les tous :
- Une idée par chapitre, pas une image par chapitre. Chaque scène montre un mécanisme (un quadrillage urbain parcouru, une tresse de récitation, une fractale qui grandit, une trajectoire tracée). N'illustrez pas un nom.
- Une seule grille pour l'image et le son. Tout est chronométré en temps. La musique est écrite en premier, et chaque coupe, pop et flash tombe sur un événement musical.
- Une direction artistique forte et spécifique qui n'est pas un « clip corporate » : du chrome d'épreuve d'impression, des encres mal registrées, des écritures et chiffres natifs, des légendes en machine à écrire et des fondus en escalier.
0 · Configuration (une fois par session)
pip install --break-system-packages pycairo fonttools # numpy, scipy, Pillow sont généralement présents
which ffmpeg && python3 -c "from PIL import features; print('raqm', features.check('raqm'))"
Raqm doit être True, car il fournit le modelage des écritures complexes pour le devanagari, l'arabe, le tamoul et les écritures similaires. Créez un répertoire de travail. Si le dossier de cette compétence contient scripts/, copiez engine.py et audio_kit.py dans le répertoire de travail. Sinon, écrivez-les à partir de l'Annexe A et B textuellement. Ils sont testés, donc ne les réécrivez pas. Les polices sont téléchargées lors de la première utilisation depuis le dépôt GitHub google/fonts, ce que le sandbox autorise.
Configurez une liste de tâches : concept → musique → scènes → images de test → rendu → QA → livraison.
1 · Concept (avant tout code)
Recherche. Effectuez une recherche Web pour chaque date, nombre, nom et affirmation qui apparaîtra à l'écran. Préfixez les dates incertaines par « c. ». Étiquetez les diagrammes « schématique » ou « pas à l'échelle ». Gardez les faits peu nombreux et précis, avec des légendes de 12 mots ou moins.
Objet fil rouge. Trouvez un dispositif visuel qui apparaît dans chaque chapitre et donne son titre au film. Dans SŪTRA, c'était un fil rouge qui parcourt les rues, devient une onde sonore, dessine le kolam, puis trace la trajectoire lunaire. Autres exemples :
- Histoire océanique : une seule crête de vague.
- Café : une goutte.
- Informatique : un seul bit.
- Une entreprise : le geste central de son produit.
Titre. Choisissez un mot, de préférence dans la langue du sujet, et donnez-lui une définition de dictionnaire à l'écran : sūtra (n.) 1. un fil 2. une règle 3. ce qui maintient les choses ensemble.
Chapitres. Utilisez 6 à 10, chacun construit autour d'une idée visuelle distincte. Puisez dans ce répertoire :
- Un diagramme qui se construit lui-même : un quadrillage urbain, une carte d'itinéraire, une orbite, ou un arbre généalogique d'écritures.
- Les nombres comme art : un triangle de Pascal/Sierpiński, π qui spiralise, des compteurs binaires, ou des objets en série de doublement.
- Récursion ou fractales : un temple de temples, ou un arbre d'arbres.
- Un oscilloscope de la bande-son réelle (
wave_at(f)), idéal pour les chapitres sur la parole ou la musique. - Un dessin en un trait continu : un kolam ou une courbe miroir, un portrait au trait continu, ou une signature.
- Un collage de mots cinétique, comme des emprunts, des slogans, ou des noms de lieux tamponnés sur le rythme.
- Un compte à rebours en chiffres natifs, puis un lancement ou événement, puis un impact.
- Une boucle : l'outro enfile tous les chapitres comme des perles sur le fil rouge, puis revient à l'image d'ouverture.
Testez chaque chapitre : s'il pouvait être remplacé par une photo d'archive, repensez-le.
Palette. Utilisez une couleur de papier, une couleur de nuit et 5 encres nommées d'après les matériaux propres à la culture. SŪTRA utilisait indigo, sindoor, haldi, rani et mor (paon). Autres exemples :
- Japon : sumi, vermillon shu, indigo ai, or kincha, matcha.
- Mexique : cochenille, maïs, bleu talavera, jade, souci.
- Espace : vantablack, orange sodium, rouge hydrogène-alpha, teal oxygène.
Alternez les chapitres clairs et sombres, et faites en sorte que le chapitre du drop change de couleur à chaque mesure.
Système typographique. Utilisez 4 à 5 familles. Choisissez une police de caractères condensée pour les mots héros, la ou les écritures natives pour le sujet, une police à chasse fixe pour les légendes et étiquettes, et une police italique douce et empattée pour les lignes poétiques. Utilisez les chiffres natifs pour l'année courante ou le compteur. Chemins google/fonts utiles (get_font(name, path, axes)) :
| rôle | chemin | axes |
|---|---|---|
| affichage | ofl/anton/Anton-Regular.ttf | – |
| chasse fixe | ofl/spacemono/SpaceMono-Regular.ttf, …-Bold.ttf | – |
| italique empattée | ofl/fraunces/Fraunces-Italic[SOFT,WONK,opsz,wght].ttf | [144,380,100,1] |
| empattée grasse | ofl/fraunces/Fraunces[SOFT,WONK,opsz,wght].ttf | [144,900,100,1] |
| Devanagari | ofl/tirodevanagarisanskrit/TiroDevanagariSanskrit-Regular.ttf | – |
| Arabe | ofl/notonaskharabic/NotoNaskhArabic[wght].ttf | [700] |
| Tamoul / Bengali / Thaï | ofl/notosanstamil/NotoSansTamil[wdth,wght].ttf (même motif) | [700,100] (ordre wght, wdth = de la police) |
| CJK | ofl/notoserifjp/NotoSerifJP[wght].ttf | [700] |
| Brahmi | ofl/notosansbrahmi/NotoSansBrahmi-Regular.ttf | – |
Vérifiez l'ordre des axes avec font_axes(name). Si un chemin renvoie 404, parcourez https://github.com/google/fonts/tree/main/ofl/<famille> avec WebFetch.
Dispositifs non conventionnels. Choisissez-en plusieurs :
- HUD d'épreuve d'impression : repères de coupe, échantillons d'encre, une étiquette « PLANCHE 03 / 08 · NOM », un numéro de chapitre dans un système de numération ancien, une année courante en chiffres natifs, et un fil de progression avec des perles de chapitre.
- Mauvais enregistrement Riso sur les caractères héros (
riso()). - Remplissages en demi-teintes.
- Mots géants verticaux.
- Texte disposé en cercle (un tampon ou sceau).
- Légendes en machine à écrire.
- Fondus en escalier ou en iris avec deux encres.
- Bégaiements (glitch) aux jointures, assortis de bégaiements audio.
- Un flash blanc sur le drop.
- Grain de pellicule.
Jamais :
- Un carton de titre centré qui disparaît en fondu.
- Des dégradés, des lueurs ou des flares d'objectif comme décoration.
- Des émojis ou du clipart.
- Des dessins de personnages copyrightés, de logos ou de visages de personnes réelles.
- Des drapeaux nationaux utilisés comme décoration.
- Des images stéréotypées.
2 · Chronologie en temps
Utilisez 90 à 110 BPM. Un chapitre fait 8 à 16 temps. 96 BPM × 136 temps ≈ 87 s. Écrivez d'abord un tableau, comme celui de SŪTRA :
| temps | chapitre | image | musique |
|---|---|---|---|
| 0–12 | intro | un point pulse, s'étire en ligne, lettres du titre apparaissent une par temps | bourdon qui enfle, cloche sur chaque lettre, montée (riser) |
| 12–28 | 1 | le quadrillage urbain s'assemble ; le fil parcourt les rues | groove de percussion manuelle + mélodie pincée |
| 28–40 | 2 (respiration) | oscilloscope de l'audio + tresse de mots | bourdon, chant, clairsemé |
| 40–52 | 3 | roue qui tourne, lettres tamponnées, arbre de lignée | batterie plus chargée, montée, bégaiement à 51.5 |
| 52–64 | 4 | traînées d'étoiles accélèrent, chiffres en spirale | nappe + arpège de cloches, grande montée, flash |
| 64–80 | DROP | glyphe héros géant, fractale croît de 4 rangées/temps, palette change par mesure | kick, clap, sub bass, accroche, boom |
| 80–92 | 5 | structure récursive croît par profondeur | demi-temps ; tihai tombe sur 92 = coupure |
| 92–108 | 6 | dessin en un trait, puis collage de mots | groove doux, puis groove complet |
| 108–120 | finale | compte à rebours en chiffres natifs → trajectoire → impact d'atterrissage | cloches, grondement, montée, boom |
| 120–136 | outro | perles sur le fil, le titre revient, point | chant + nappe + mélodie, disparition progressive |
La courbe d'énergie va : intro calme → construction → respiration → construction → drop sur l'idée la plus surprenante → demi-temps → impact final → boucle calme.
3 · Musique d'abord — audio.py
from audio_kit import *
T = Track(bpm=96, beats=136, root=138.59, scale="raga_bhairavi") # root Hz ; SCALES contient plusieurs cultures
T.drone_bed(0, 132, level=lambda b: 0.16 if 12 <= b < 120 else 0.3)
T.hand_drum(12, 28) # motif par défaut de type keherwa
T.melody(12, [(2, 4, 1), (3, 5, 1.2, 6), (4, 7, 2.2)]) # (beat, degree, dur, glide-to degree)
T.riser_into(64, 4); T.at("fx", boom(), 64, 0.55) # vers le drop
T.groove(64, 80); T.stutter(51.5)
T.tihai(92) # phrase 3× tombant exactement sur le temps 92
T.mixdown("music.wav", fade_beats=(133, 136)) # écrit aussi env.npy / wave.npy
- Les bus sont : drone, perc, kick, bass, mel, fx et pad. Utilisez
T.at(bus, signal, beat, gain, pan)pour tout coup unique. - Instruments :
drone, pluck (buzz=0 pour propre), bell, voice, pad, sub, kick, clap, hat, drum_tone, drum_bass, drum_slap, drum_muted, riser, boom, rumble. - Faites correspondre la gamme et les instruments à la culture. Par exemple : hijaz + cadre de tambour + pincement oud-ish (
buzz=0.8) ; in_sen + pincement koto-ish (buzz=0) + cloches ; dorien + bourdon + pincements propres. - Chaque événement visuel a un son. Les fondus ont des montées, les drops ont un boom plus flash, les coupures tombent sur tihai ou coups de kick, et les lettres qui apparaissent ont des cloches.
- Vérifiez le résultat :
- Loudness :
ffmpeg -i music.wav -af ebur128=framelog=quiet -f null -devrait indiquer environ −16 à −14 LUFS. - Spectrogramme :
ffmpeg -i music.wav -lavfi showspectrumpic=s=1600x500:scale=log:fscale=log spec.png, puis lisez l'image et vérifiez les formes des sections.
- Loudness :
4 · Scènes — film.py
from engine import *
PAPER, NIGHT, INK = hx("F1E6CF"), hx("0A0D26"), hx("14121C")
INK1, INK2, INK3 = hx("1B2A6B"), hx("EE3B1E"), hx("FFB21A")
get_font("anton", "ofl/anton/Anton-Regular.ttf")
get_font("mono", "ofl/spacemono/SpaceMono-Regular.ttf")
# prégéométrie au niveau du module avec un générateur aléatoire ensemencé (déterministe entre les processus workers)
def s_intro(ctx, b, f): # b = beat (float), f = frame
bg(ctx, NIGHT)
riso(ctx, lambda c: text(ctx, "TITRE", "anton", 400, c, W/2, 700, ax=.5, sc=back(ph(b, 4, .5))),
PAPER, INK2, dark=True)
return PAPER # couleur de premier plan pour le HUD
def hud(ctx, b, fg): reg_marks(ctx, fg, (INK1, INK2, INK3))
SCENES = [(0, s_intro), (12, s_ch1)] # (beat de début, fonction)
run(SCENES, 136, bpm=96, name="monfilm", hud=hud,
wipes=[(12, INK2, INK3)], # (beat, c1, c2[, iris_wipe])
glitches=[(51.5, 52.15)], flashes=[(64, WHITE, .5)], fade_out=134.6)
Règles pour les scènes :
- Sans état. Chaque valeur est une fonction de
b, donc les images peuvent être rendues dans n'importe quel ordre entre les processus. - Entrées utilisez
back(ph(b, start, 0.4))pour les pops,eo()pour les glissements eteio()pour les mouvements de caméra et les tracés de lignes. Espacez les éléments de 0,2 à 1 temps. - Vie du rythme provient de
pulse(b)(1→0 chaque temps) pour le rebond et la taille des anneaux,env_at(f)pour la loudness etwave_at(f)pour l'oscilloscope. - Dessinez les lignes en direct. Utilisez
path_partial(ctx, pts, cumlen(pts), frac)et placez une perle lumineuse à la pointe retournée. - Composition.
- Gardez un élément héros, un diagramme de soutien et une légende par scène.
- Gardez des marges de 110 px. Les coins du HUD et la bande inférieure (y ≈ 960–1040) sont réservés. Les légendes vont à y ≈ 975 à partir de x ≈ 560, ou dans la zone vide de la scène.
- Les grands caractères sont grands : glyphes héros de 300–800 px. Les légendes sont en chasse fixe de 18–22 px.
- Contraste. Alternez les scènes de papier et de nuit. Utilisez
riso(..., dark=True)sur les arrière-plans sombres. - Utilisez
fit_size()pour les mots longs dans les collages, ettext_circle()pour les sceaux. - Le HUD peut afficher le nom du chapitre, le numéro de planche, les chiffres natifs via une carte de chiffres, et un fil de progression. Lorsque le dossier de la compétence contient
examples/sutra/film.py, sahud()et ses scènes sont la référence complète ; sonexamples/sutra/contact_sheet.pngmontre le rendu final.
5 · Images de test → planche contact → correction (à ne pas sauter)
python3 film.py test 2 6 10 17 25 34 45 58 66 75 86 96 104 110 114 118 125 134
Lisez sheet.png et vérifiez :
- Texte qui se chevauche ou qui est rogné.
- Boîtes tofu ou glyphes manquants. Exécutez
missing_glyphs(font, string)pour chaque police × chaîne. Par exemple, Fraunces manque → π ∞ ṅ, et Tiro Devanagari manque → ≈ π. - Légendes illisibles à la taille de la vignette.
- Images vides au début des chapitres.
- Faits erronés ou fautes de frappe.
- Temps où rien ne bouge.
Itérez jusqu'à ce que chaque image puisse être une affiche. Cela prend généralement 2 à 3 tours.
6 · Rendu, QA, livraison
nohup python3 film.py render > render.log 2>&1 &, puis interrogez avec des boucles Bashsleep(timeout ≤ 590 s chacun). Le rendu s'effectue en parallèle sur tous les cœurs. La vitesse approximative est de 2 à 10 ips au total selon la complexité de la scène ; SŪTRA a pris environ 20 minutes sur 2 cœurs.- Les sorties sont
name_full.mp4(CRF 18, pleine qualité) etname.mp4, un ré-encodage en deux passes maintenu sous 29 Mo pour tenir dans la limite de téléchargement de 30 Mo des conversations. - QA :
ffprobe -count_framespour confirmer le nombre d'images et la durée.ffmpeg -i name_full.mp4 -vf "fps=1/3.5,scale=384:216,tile=5x5" -frames:v 1 final.png, puis lisez-la.- Vérifiez 2 à 3 images en pleine taille, dont une après compression.
- Téléchargement : envoyez
name.mp4avec SendUserFile (display: "render") ; la carte permet à l'utilisateur de la lire et de la télécharger. Mentionnez également le fichier en pleine qualité. Si la session est liée à l'ordinateur de l'utilisateur, proposez de sauvegardername_full.mp4dans l'un de ses dossiers viadevice_commit_files, ou envoyez-le s'il fait moins de 30 Mo. - Pour ne rendre qu'une section corrigée, exécutez
python3 film.py chunk <f0> <f1> part.mp4pour les images concernées, puis concaténez et multiplexez les morceaux avec ffmpeg comme le faitrun().
Message final à l'utilisateur
Donnez le titre du film et une ligne sur son concept, les chapitres sous forme de liste compacte, de quoi est faite la musique, et toute approximation (dates « c. », schémas). Ne récapitulez pas les étapes.
Annexe A — engine.py
"""engine.py — générateur de film style épreuve d'impression (pycairo + Pillow/raqm + ffmpeg).
Un script de film fait : from engine import *
puis définit des scènes comme fonctions scene(ctx, b, f) -> fg_color (b = temps, f = image)
et appelle run(SCENES, TOTAL_BEATS, bpm=..., wipes=..., hud=...) à la fin.
CLI du script de film :
python film.py test 4 12.5 30 # rend les PNG à ces temps + planche contact (sheet.png)
python film.py render # rendu parallèle complet -> film_full.mp4 + film.mp4 (<29 Mo)
"""
import math, os, sys, subprocess, shutil
import numpy
…
[tronqué pour la traduction]