Film Generativo
Produce un MP4 de 45 a 120 segundos, 1920×1080, 30 fps en el que cada fotograma se dibuja en código (pycairo + Pillow) y cada nota se sintetiza (numpy/scipy). No hay metraje de archivo, imágenes generadas por IA ni música con licencia. La película de referencia es SŪTRA (civilización india, 87 s). Tiene un estilo de prueba de impresión risográfica, y un hilo rojo recorre diez capítulos sobre una rejilla de 96 BPM.
La calidad proviene de tres elementos. Sigue los tres:
- Una idea por capítulo, no una imagen por capítulo. Cada escena muestra un mecanismo (una cuadrícula urbana que se recorre, una trenza de recitación, un fractal que crece, una trayectoria que se vuela). No ilustres un sustantivo.
- Una sola rejilla para imagen y sonido. Todo está sincronizado en beats. La música se escribe primero, y cada corte, salto y destello cae sobre un evento musical.
- Una dirección artística fuerte y específica que no sea «explicación corporativa»: cromo de prueba de impresión, tintas desalineadas, escrituras y numerales nativos, leyendas de máquina de escribir y transiciones escalonadas.
0 · Configuración (una vez por sesión)
pip install --break-system-packages pycairo fonttools # numpy, scipy, Pillow suelen estar presentes
which ffmpeg && python3 -c "from PIL import features; print('raqm', features.check('raqm'))"
Raqm debe ser True, porque proporciona el moldeado de escrituras complejas para devanagari, árabe, tamil y escrituras similares. Crea un directorio de trabajo. Si la carpeta de esta skill tiene scripts/, copia engine.py y audio_kit.py al directorio de trabajo. De lo contrario, escríbelos a partir de Apéndice A y B textualmente. Están probados, así que no los reescribas. Las fuentes se descargan en el primer uso desde el repositorio de GitHub google/fonts, lo que el sandbox permite.
Establece una lista de tareas: concepto → música → escenas → fotogramas de prueba → renderizado → control de calidad → entrega.
1 · Concepto (antes de cualquier código)
Investigación. Busca en la web cada fecha, número, nombre y afirmación que aparecerán en pantalla. Prefija las fechas inciertas con «c.». Etiqueta los diagramas como «esquemático» o «no a escala». Mantén los hechos pocos y precisos, con leyendas de 12 palabras o menos.
Objeto conductor. Encuentra un dispositivo visual que aparezca en cada capítulo y dé título a la película. En SŪTRA era un hilo rojo que recorre las calles, se convierte en una onda sonora, dibuja el kolam, y luego vuela la trayectoria lunar. Otros ejemplos:
- Historia del océano: una sola cresta de ola.
- Café: una gota.
- Informática: un solo bit.
- Una empresa: el gesto central de su producto.
Título. Elige una palabra, idealmente en el idioma del tema, y dale una glosa estilo diccionario en pantalla: sūtra (n.) 1. un hilo 2. una regla 3. aquello que mantiene unidas las cosas.
Capítulos. Usa de 6 a 10, cada uno construido alrededor de una idea visual distinta. Recurre a este repertorio:
- Un diagrama que se construye a sí mismo: una cuadrícula urbana, un mapa de ruta, una órbita o un árbol genealógico de escrituras.
- Números como arte: un triángulo de Pascal/Sierpiński, π que se despliega en espiral, contadores binarios u objetos en series que se duplican.
- Recursión o fractales: un templo de templos, o un árbol de árboles.
- Un osciloscopio de la banda sonora real (
wave_at(f)), ideal para capítulos sobre habla o música. - Un dibujo de una sola línea ininterrumpida: un kolam o curva especular, un retrato de línea continua o una firma.
- Un collage de palabras cinético, como préstamos lingüísticos, consignas o nombres de lugares que aparecen al ritmo.
- Una cuenta regresiva en numerales nativos, luego un lanzamiento o evento, luego un impacto.
- Un cierre: el epílogo ensarta todos los capítulos como cuentas en el hilo conductor, y luego regresa a la imagen de apertura.
Prueba cada capítulo: si pudiera ser reemplazado por una foto de archivo, repiésalo.
Paleta. Usa un color de papel, un color de noche y 5 tintas nombradas según los materiales propios de la cultura. SŪTRA usó índigo, sindoor, haldi, rani y mor (pavo real). Otros ejemplos:
- Japón: sumi, shu bermellón, ai índigo, kincha oro, matcha.
- México: cochinilla, maíz, azul talavera, jade, cempasúchil.
- Espacio: vantablack, naranja sodio, rojo hidrógeno-alfa, verde oxígeno.
Alterna capítulos claros y oscuros, y haz que el capítulo de caída (drop) recoloquee en cada compás.
Sistema tipográfico. Usa 4–5 familias. Elige una cara condensada de exhibición para palabras principales, la(s) escritura(s) nativa(s) para el tema, una cara mono para leyendas y etiquetas, y una serifa itálica suave para líneas poéticas. Usa numerales nativos para el año o contador en ejecución. Rutas útiles de google/fonts (get_font(name, path, axes)):
| rol | ruta | ejes |
|---|---|---|
| exhibición | ofl/anton/Anton-Regular.ttf | – |
| mono | ofl/spacemono/SpaceMono-Regular.ttf, …-Bold.ttf | – |
| itálica serifa | ofl/fraunces/Fraunces-Italic[SOFT,WONK,opsz,wght].ttf | [144,380,100,1] |
| serifa pesada | ofl/fraunces/Fraunces[SOFT,WONK,opsz,wght].ttf | [144,900,100,1] |
| devanagari | ofl/tirodevanagarisanskrit/TiroDevanagariSanskrit-Regular.ttf | – |
| árabe | ofl/notonaskharabic/NotoNaskhArabic[wght].ttf | [700] |
| tamil / bengalí / tailandés | ofl/notosanstamil/NotoSansTamil[wdth,wght].ttf (mismo patrón) | [700,100] (orden wght, wdth = el de la fuente) |
| CJK | ofl/notoserifjp/NotoSerifJP[wght].ttf | [700] |
| brahmi | ofl/notosansbrahmi/NotoSansBrahmi-Regular.ttf | – |
Verifica el orden de los ejes con font_axes(name). Si una ruta da 404, navega https://github.com/google/fonts/tree/main/ofl/<familia> con WebFetch.
Dispositivos poco convencionales. Elige varios:
- HUD de prueba de impresión: marcas de registro, muestras de tinta, una etiqueta «PLACA 03 / 08 · NOMBRE», un número de capítulo en un sistema de numeración antiguo, un año en ejecución en numerales nativos y un hilo de progreso con cuentas de capítulo.
- Desalineación risográfica en el tipo principal (
riso()). - Rellenos de semitonos.
- Palabras verticales gigantes.
- Texto dispuesto en un círculo (un sello o estampilla).
- Leyendas de máquina de escribir.
- Transiciones escalonadas o de iris en dos tintas.
- Tartamudeos (glitches) en las costuras, acompañados de tartamudeos de audio.
- Un destello blanco en la caída (drop).
- Grano de película.
Nunca:
- Una tarjeta de título centrada que se desvanece.
- Degradados, brillos o destellos de lente como decoración.
- Emojis o clip-art.
- Dibujos de personajes con derechos de autor, logotipos o rostros de personas reales.
- Banderas nacionales usadas como decoración.
- Imágenes estereotipadas.
2 · Línea de tiempo en beats
Usa 90–110 BPM. Un capítulo dura de 8 a 16 beats. 96 BPM × 136 beats ≈ 87 s. Escribe primero una tabla, como la de SŪTRA:
| beats | capítulo | imagen | música |
|---|---|---|---|
| 0–12 | intro | un punto pulsa, se estira en una línea, las letras del título aparecen una por beat | zumbido de drone, campana en cada letra, subida (riser) |
| 12–28 | 1 | se ensambla la cuadrícula urbana; el hilo recorre las calles | ritmo de percusión manual + melodía pulsada |
| 28–40 | 2 (respiro) | osciloscopio del audio + trenza de palabras | drone, canto, escaso |
| 40–52 | 3 | rueda giratoria, las letras se estampan, árbol genealógico | percusión más activa, subida, tartamudeo en 51.5 |
| 52–64 | 4 | estelas de estrellas se aceleran, dígitos en espiral | pad + arpegio de campana, gran subida, destello |
| 64–80 | CAÍDA (DROP) | glifo héroe gigante, fractal crece 4 filas/beat, paleta cambia por compás | bombo, palmada, subgrave, estribillo, explosión |
| 80–92 | 5 | estructura recursiva crece por profundidad | medio tiempo; tihai cae en 92 = corte |
| 92–108 | 6 | dibujo de una línea, luego collage de palabras | ritmo suave, luego ritmo completo |
| 108–120 | final | cuenta regresiva en numerales nativos → trayectoria → impacto de aterrizaje | campanas, retumbo, subida, explosión |
| 120–136 | epílogo | cuentas en el hilo, el título regresa, punto final | canto + pad + melodía, fade out |
La curva de energía va: intro silenciosa → construcción → respiro → construcción → caída en la idea más sorprendente → medio tiempo → impacto final → cierre silencioso.
3 · Música primero — audio.py
from audio_kit import *
T = Track(bpm=96, beats=136, root=138.59, scale="raga_bhairavi") # root Hz; SCALES tiene muchas culturas
T.drone_bed(0, 132, level=lambda b: 0.16 if 12 <= b < 120 else 0.3)
T.hand_drum(12, 28) # patrón keherwa por defecto
T.melody(12, [(2, 4, 1), (3, 5, 1.2, 6), (4, 7, 2.2)]) # (beat, grado, dur, glissando a grado)
T.riser_into(64, 4); T.at("fx", boom(), 64, 0.55) # hacia la caída
T.groove(64, 80); T.stutter(51.5)
T.tihai(92) # frase 3 veces que cae exactamente en el beat 92
T.mixdown("music.wav", fade_beats=(133, 136)) # también escribe env.npy / wave.npy
- Los buses son drone, perc, kick, bass, mel, fx y pad. Usa
T.at(bus, signal, beat, gain, pan)para cualquier sonido único. - Instrumentos:
drone, pluck (buzz=0 para limpio), bell, voice, pad, sub, kick, clap, hat, drum_tone, drum_bass, drum_slap, drum_muted, riser, boom, rumble. - Empareja la escala y los instrumentos con la cultura. Por ejemplo: hijaz + tambor marco + pulso tipo oud (
buzz=0.8); in_sen + pulso tipo koto (buzz=0) + campanas; dorian + drone + pulsos limpios. - Cada evento visual tiene un sonido. Las transiciones tienen subidas (risers), las caídas tienen explosión (boom) más destello, los cortes caen en tihai o golpes de bombo, y las letras que aparecen tienen campanas.
- Verifica el resultado:
- Sonoridad:
ffmpeg -i music.wav -af ebur128=framelog=quiet -f null -debería dar aproximadamente −16 a −14 LUFS. - Espectrograma:
ffmpeg -i music.wav -lavfi showspectrumpic=s=1600x500:scale=log:fscale=log spec.png, luego Lee la imagen y comprueba las formas de las secciones.
- Sonoridad:
4 · Escenas — 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")
# precomputa geometría a nivel de módulo con un rng semilla (determinista entre procesos worker)
def s_intro(ctx, b, f): # b = beat (float), f = frame
bg(ctx, NIGHT)
riso(ctx, lambda c: text(ctx, "TITLE", "anton", 400, c, W/2, 700, ax=.5, sc=back(ph(b, 4, .5))),
PAPER, INK2, dark=True)
return PAPER # color de primer plano para el HUD
def hud(ctx, b, fg): reg_marks(ctx, fg, (INK1, INK2, INK3))
SCENES = [(0, s_intro), (12, s_ch1)] # (beat inicial, fn)
run(SCENES, 136, bpm=96, name="mypelicula", hud=hud,
wipes=[(12, INK2, INK3)], # (beat, c1, c2[, iris_wipe])
glitches=[(51.5, 52.15)], flashes=[(64, WHITE, .5)], fade_out=134.6)
Reglas de escena:
- Sin estado. Cada valor es una función de
b, por lo que los fotogramas se pueden renderizar en cualquier orden entre procesos. - Entradas usan
back(ph(b, start, 0.4))para apariciones,eo()para deslizamientos yeio()para movimientos de cámara y dibujos de líneas. Escalonar elementos por 0.2–1 beat. - Vida del beat proviene de
pulse(b)(1→0 cada beat) para rebote y tamaño de anillo,env_at(f)para sonoridad ywave_at(f)para el osciloscopio. - Dibuja las líneas en vivo. Usa
path_partial(ctx, pts, cumlen(pts), frac)y coloca una cuenta brillante en la punta devuelta. - Composición.
- Mantén un elemento héroe, un diagrama de apoyo y una leyenda por escena.
- Mantén márgenes de 110 px. Las esquinas del HUD y la banda inferior (y ≈ 960–1040) están reservadas. Las leyendas van en y ≈ 975 comenzando en x ≈ 560, o en la zona vacía de la escena.
- El tipo grande es grande: glifos héroe de 300–800 px. Las leyendas son mono de 18–22 px.
- Contraste. Alterna escenas de papel y noche. Usa
riso(..., dark=True)en fondos oscuros. - Usa
fit_size()para palabras largas en collages, ytext_circle()para sellos. - El HUD puede mostrar el nombre del capítulo, número de placa, numerales nativos a través de un mapa de dígitos y un hilo de progreso. Cuando la carpeta de la skill tiene
examples/sutra/film.py, sushud()y escenas son la referencia completa; suexamples/sutra/contact_sheet.pngmuestra el aspecto final.
5 · Fotogramas de prueba → hoja de contactos → corrección (no omitir)
python3 film.py test 2 6 10 17 25 34 45 58 66 75 86 96 104 110 114 118 125 134
Lee sheet.png y verifica:
- Texto superpuesto o recortado.
- Cajas tofu o glifos faltantes. Ejecuta
missing_glyphs(font, string)para cada fuente × cadena. Por ejemplo, Fraunces carece de → π ∞ ṅ, y Tiro Devanagari carece de → ≈ π. - Leyendas ilegibles en tamaño miniatura.
- Fotogramas en blanco al inicio de los capítulos.
- Hechos incorrectos o errores tipográficos.
- Beats donde nada se mueve.
Itera hasta que cada fotograma pueda ser un póster. Por lo general, toma 2–3 rondas.
6 · Renderizado, control de calidad, entrega
nohup python3 film.py render > render.log 2>&1 &, luego sondea con buclessleepde Bash (tiempo de espera ≤ 590 s cada uno). Renderiza en paralelo en todos los núcleos. La velocidad aproximada es de 2 a 10 fps total según la complejidad de la escena; SŪTRA tomó unos 20 min en 2 núcleos.- Las salidas son
name_full.mp4(CRF 18, calidad completa) yname.mp4, una recodificación de dos pasos que se mantiene por debajo de 29 MB para que quepa en el límite de carga de chat de 30 MB. - Control de calidad:
ffprobe -count_framespara confirmar el recuento de fotogramas y la duración.ffmpeg -i name_full.mp4 -vf "fps=1/3.5,scale=384:216,tile=5x5" -frames:v 1 final.png, luego Lee la imagen.- Verifica al azar 2–3 fotogramas a tamaño completo, incluido uno después de la compresión.
- Descarga: envía
name.mp4con SendUserFile (display: "render"); la tarjeta permite al usuario reproducirlo y descargarlo. Menciona también el archivo de calidad completa. Si la sesión está vinculada al ordenador del usuario, ofrecer guardarname_full.mp4en una de sus carpetas mediantedevice_commit_files, o enviarlo si está por debajo de 30 MB. - Para volver a renderizar solo una sección corregida, ejecuta
python3 film.py chunk <f0> <f1> part.mp4para los fotogramas afectados, luego concatena y multiplexa las piezas con ffmpeg como lo hacerun().
Mensaje final para el usuario
Da el título de la película y una línea sobre su concepto, los capítulos como una lista compacta, qué música tiene y cualquier aproximación (fechas «c.», esquemas). No recapitules los pasos.
Apéndice A — engine.py
"""engine.py — kit de herramientas para film generativo estilo impresión (pycairo + Pillow/raqm + ffmpeg).
Un script de película hace: from engine import *
luego define escenas como funciones scene(ctx, b, f) -> fg_color (b = beat, f = frame)
y llama a run(SCENES, TOTAL_BEATS, bpm=..., wipes=..., hud=...) al final.
CLI del script de película:
python film.py test 4 12.5 30 # renderiza PNGs en esos beats + hoja de contactos (sheet.png)
python film.py render # renderizado paralelo completo -> film_full.mp4 + film.mp4 (<29 MB)
"""
import math, os, sys, subprocess, shutil
import numpy
…
[truncado para traducción]