Video de producto como código
Crea videos de producto como código: una presentación HTML controlada por una línea de tiempo determinista, una locución generada por IA, grabaciones reales de pantalla del producto y una masterización de audio con ffmpeg. Todo es reproducible, versionable (diffable) y re-renderizable, por lo que las revisiones cuestan minutos en lugar de una reconstrucción completa.
Esto no es un editor de video. Es un pipeline. El resultado es un MP4.
La idea clave
Primero el audio, luego el ritmo, luego la imagen.
Genera la narración antes de decidir cuánto tiempo estará cada elemento en pantalla. Mide cada clip de audio, deriva las duraciones de las escenas a partir de esas mediciones, y solo entonces renderiza los fotogramas. Las alternativas — adivinar duraciones o escribir la narración para que encaje en un guion gráfico fijo— producen imágenes que se desincronizan con la voz y obligan a volver a renderizar cada vez que se modifica una línea.
script.json ──► TTS ──► medir ──► timings.json ──► render ──► composición ──► multiplexar ──► MP4
▲ ▲ ▲
assemble.py render.py composite.py
Para un video sin narración (un título animado, un bucle silencioso, un clip mudo para redes), omite la etapa 1 y escribe timings.json manualmente. Todo lo que sigue aguas abajo permanece igual.
Pipeline
| Etapa | Script | Produce |
|---|---|---|
| 1. Narración | tu herramienta TTS preferida | audio/vo_NN.mp3, uno por escena |
| 2. Ritmo + pista de voz | scripts/assemble.py | timings.json, out/voice.wav |
| 3. Imagen | scripts/render.py | out/silent.mp4 |
| 4. Grabación de pantalla (opcional) | scripts/capture/tour.py | capture/clips/*.mp4 |
| 5. Composición (opcional) | scripts/composite.py | out/composited.mp4 |
| 6. Masterización de audio | scripts/mux.py | MP4 final |
Lee references/PIPELINE.md para una guía paso a paso, references/AUDIO.md para voz y música, references/SCREEN-CAPTURE.md para grabaciones del producto y references/PITFALLS.md antes de depurar cualquier cosa: allí se enumeran los fallos que este pipeline ha encontrado realmente.
Tipos de video
El mismo pipeline para todos; lo que cambia es la estructura, el ritmo y cuánto ocupa el producto real en el encuadre. Decide cuál de estos estás creando antes de escribir una línea, porque determina todo lo demás.
| Tipo | Duración típica | Estructura | Imágenes del producto |
|---|---|---|---|
| Video explicativo de producto | 1-3 min | problema → giro → cómo funciona → prueba → cierre | poca, como prueba |
| Lanzamiento de funcionalidad | 30-90s | qué cambió → por qué importa → vívelo → cómo obtenerlo | media |
| Demo / recorrido | 2-5 min | una tarea real, de principio a fin | dominante |
| Tutorial | 3-10 min | paso a paso, por capítulos, pausas para seguir | dominante |
| Notas de versión | 30-90s | un elemento por compás, denso | capturas o clips cortos |
| Onboarding | 1-3 min | el camino del primer uso, tal como lo ve el usuario | dominante |
| Título animado / bucle | 5-20s | una idea, a menudo silencioso | ninguna |
| Clip para redes | menos de 60s | primero el gancho, derivado de un corte más largo | corto, contundente |
Reglas prácticas según el tipo:
- Las demos y tutoriales priorizan la imagen. La presentación es un marco alrededor del producto: tarjetas de título, anotaciones, separadores de capítulos. No narres lo que la pantalla ya dice.
- Los videos explicativos y lanzamientos priorizan la narración. Las imágenes aparecen como evidencia de una afirmación que acabas de hacer; esa cercanía es lo que hace que impacte.
- Los tutoriales necesitan aire. Deja una pausa después de cada paso. Usa
--tailgenerosamente; el espectador está siguiendo, no solo mirando. - Las notas de versión son densas. El texto en pantalla carga el mensaje, la narración resume. Un elemento por compás, sin alargar.
- Los clips para redes se derivan, nunca se escriben dos veces. Constrúyelos como un subconjunto de escenas del corte largo para que no se desvíen (consulta PIPELINE.md).
Inicio rápido
Configuración, una vez: pip install -r requirements.txt && playwright install chromium. ffmpeg y ffprobe también deben estar en PATH.
Silencioso primero, para comprobar que la presentación se renderiza:
cp templates/deck.html .
python3 scripts/render.py --preview 2 8 18 # imágenes fijas
python3 scripts/render.py # -> out/silent.mp4
Luego el pipeline completo una vez que exista la narración:
cp templates/script.example.json script.json
# genera audio/vo_01.mp3 ... uno por escena, luego:
python3 scripts/assemble.py # timings.json + out/voice.wav
python3 scripts/render.py # out/silent.mp4
python3 scripts/mux.py --final out/final.mp4 # añade voz (+ música si existe audio/bed.mp3)
assemble.py es la única etapa que necesita audio; sin ella, render.py usa las duraciones declaradas en la presentación.
Reglas que mantienen esto funcionando
Nunca guíes la línea de tiempo con el tiempo real de pared. El renderizador avanza a un t explícito y captura la pantalla. render.py llama a window.__seek(t) para cada fotograma, por lo que una máquina lenta produce un archivo idéntico al de una rápida. Cualquier cosa que use requestAnimationFrame o animaciones CSS se romperá o perderá fotogramas bajo un bucle de captura.
Ancla cada animación a un compás de la narración. Si la voz dice "tres cosas" en el segundo 4.2, la tercera tarjeta aparece en el segundo 4.2. Después de cualquier reescritura, vuelve a comprobar los compases: una línea que vuelve 3 segundos más corta dejará sus imágenes visuales huérfanas. Esta es la causa principal de que el resultado parezca dirigido o genérico.
Renderiza en fragmentos reanudables. render.py escribe out/parts/part_NNN.mp4 y omite los fragmentos completados al re-ejecutar. Una captura estancada cuesta un fragmento, no todo el pase. Nunca ejecutes dos renderizados contra el mismo directorio de partes.
Mide, no supongas. Verifica con números, no con sensaciones:
- detección de fotogramas en blanco: cuenta píxeles oscuros (
(pixels < 200).mean()), no la desviación estándar — una página de interfaz legítimamente blanca también tiene baja desviación - pronunciación: pasa el audio generado por un reconocimiento de voz y léelo
- niveles:
ffmpeg -af volumedetecten el archivo final, cada vez - geometría: lee
getBoundingClientRect()del DOM en lugar de hardcodear
Un origen, múltiples cortes. Selecciona subconjuntos de escenas de una sola presentación con un parámetro de consulta (deck.html?cut=short) para que una demo larga y un clip de 40 segundos para redes no puedan desviarse. Consulta references/PIPELINE.md.
Adáptate a la marca del producto desde su código
Si el producto tiene un repositorio, extrae la marca de él en lugar de pedir códigos hex:
python3 scripts/detect_brand.py ../su-producto --css
Lee archivos de tokens de diseño, configuraciones de Tailwind, propiedades personalizadas CSS, variables SCSS y Less, objetos de tema JS/TS y el manifiesto web, e informa las fuentes, la librería de iconos y los archivos de logo que ha encontrado. Pega el bloque :root que imprime en deck.html.
La salida de compilación se omite, ya que una carpeta dist/ está llena de CSS de librerías de componentes empaquetadas que no son la marca. La paleta también se verifica por coherencia en lugar de confiar solo en la coincidencia de nombres: un repositorio que define --surface para su tema claro y --bg para el oscuro daría una tarjeta blanca sobre un marco negro. Los rechazos se imprimen, nunca se aplican en silencio.
Muestra al usuario lo que se detectó y lo que se rechazó antes de renderizar.
Guía de diseño
La presentación es HTML/CSS, por lo que el diseño es completamente tuyo. Vale la pena saber dos cosas:
Los contextos sociales y de reproducción automática se ejecutan silenciados. El titular en pantalla debe comunicar cada compás por sí solo, y el gancho tiene que impactar en el primer segundo o dos. Trata la locución como un refuerzo, no como el mecanismo de entrega.
Si te piden algo visualmente distintivo, enraíza en el propio mundo del tema — sus artefactos, instrumentos y lenguaje vernáculo — en lugar de recurrir a un fondo oscuro con un color de acento brillante, que es donde se agrupa el resultado genérico. La habilidad frontend-design de Anthropic cubre esto bien y se compone limpiamente con esta.