Video explainer
Creas videos como lo haría un diseñador de motion graphics, pero en código: cada escena es un pequeño módulo JS que construye el DOM/SVG y agrega animaciones a una línea de tiempo de GSAP pausada. El renderizador avanza esa línea de tiempo fotograma a fotograma (t = frame / fps), captura cada fotograma y los envía a ffmpeg. La salida es determinista: el mismo proyecto siempre produce los mismos fotogramas.
SKILL_DIR a continuación se refiere al directorio base de esta habilidad (la carpeta que contiene este archivo).
Configuración (una vez por máquina)
cd SKILL_DIR/scripts && npm install && npx playwright install chromium
También necesitas ffmpeg en el PATH. Cada script imprime este comando si faltan las dependencias.
Flujo de trabajo
- Storyboard primero. Acuerda el tema, la audiencia, la duración objetivo y si debe tener locución, luego escribe la lista de escenas en
video.json. Cada escena tienenarration(lo que dice la locución) yvisual(lo que se ve en pantalla). Leereference/storyboard.md. No escribas el código de la escena hasta que el storyboard tenga sentido por sí mismo. - Andamio:
node SKILL_DIR/scripts/new.mjs <directorio-del-proyecto> --title "...", luego editavideo.jsony escribe un módulo por escena enscenes/<id>.js. Usareference/animation-patterns.mdpara recetas yreference/design.mdpara diseño, tipografía y color. Con locución, establece las duraciones de escena en"auto"y temporiza las animaciones concue('frase'). Ambos ejemplos son videos completos con locución:examples/kubernetes-overview/(ElevenLabs) yexamples/how-dns-works/(Gemini). - Locución (si se desea): sigue
reference/voiceover.md. En resumen: agrega un bloque"voice"avideo.json, ejecutanode SKILL_DIR/scripts/narration.mjs status <directorio-del-proyecto>, llama a la herramientagenerate-speechdel servidor OpenRouter MCP una vez por escena listada con exactamente los argumentos que imprime, y registra cada archivo guardado connarration.mjs add. Hazlo antes de la verificación (paso 4), porque el audio real cambia la duración de las escenas y los tiempos decue(). - Verificación:
node SKILL_DIR/scripts/lint.mjs <directorio-del-proyecto>. Carga cada escena en Chromium e informa errores de carga, animaciones más largas que su escena, animaciones creadas fuera detl, elementos fuera del marco, texto recortado y locución demasiado larga para su escena. Corrige cada advertencia a menos que sea intencional. - Míralo:
node SKILL_DIR/scripts/render.mjs <directorio-del-proyecto> --contact-sheet, luego Lee el PNG (<directorio-del-proyecto>/out/contact-sheet.png). Verifica jerarquía, superposición, legibilidad, fotogramas vacíos, temporización. Para más detalle, renderiza momentos específicos:--stills 12.5,30. Itera hasta que se vea bien, luego haz un último pase con contact-sheet después de tu edición final. - Renderizado:
node SKILL_DIR/scripts/render.mjs <directorio-del-proyecto>→<directorio-del-proyecto>/out/video.mp4. Los clips de locución se mezclan automáticamente. Dale al usuario la ruta, la duración y la lista de escenas. Pueden previsualizar interactivamente connode SKILL_DIR/scripts/serve.mjs <directorio-del-proyecto>(barra de avance, reproducir/pausa, reproducción de locución,calterna subtítulos,msilencia).
Banderas de renderizado útiles: --out archivo.mp4, --from 10 --to 20 (parcial), --scale 0.5 (borrador rápido), --workers N, --crf 18, --audio pista.mp3 (usa tu propia pista de audio en lugar de la locución), --no-voice (silencioso), --audio-codec aac (el predeterminado es MP3, que también se reproduce en VS Code).
Estructura del proyecto
mi-video/
video.json # título, ancho, alto, fps, semilla, transición, tema, escenas[]
scenes/<id>.js # un módulo por escena
assets/ # imágenes/SVGs, referenciados como '/assets/x.svg'
audio/ # clips de locución + manifest.json (de narration.mjs; consérvalos, cuestan dinero)
style.css # opcional, se carga automáticamente si existe
out/ # renderizados (contact-sheet.png, stills/, video.mp4)
Campos de escena en video.json: id (obligatorio, también el nombre de archivo predeterminado scenes/<id>.js), duration (segundos, o "auto": ajusta al audio de locución, o una estimación de velocidad de lectura antes de que exista el audio, y nunca más corto que las animaciones), narration, visual, file opcional, y transition opcional ("fade", el predeterminado, o "cut"). El bloque "voice" de nivel superior se describe en reference/voiceover.md.
Contrato de la escena
import { h, reveal, pop } from '/__runtime/helpers.js';
export default function ({ el, tl, gsap, theme, width, height, random, onFrame, cue, voice }) {
const title = h('div', { class: 'h1' }, 'Hola');
el.append(h('div', { class: 'center' }, title));
reveal(tl, title, { at: 0.3 }); // tiempo de tl 0 = inicio de esta escena
tl.to(title, { color: 'var(--accent)', duration: 0.5 }, cue('la idea clave')); // cuando se dice esa frase
}
el: contenedor de esta escena. Cubre todo el escenario 1920×1080 y se muestra y oculta automáticamente.tl: línea de tiempo de esta escena. Todo movimiento debe estar entl(o en otra línea de tiempo agregada a ella). Nunca usesgsap.to(...)directamente, animaciones/transiciones CSS,setTimeout,setInterval,requestAnimationFrame,<video>, o cualquier cosa que dependa del tiempo real. El renderizador solo mueve el cabezal de la línea de tiempo, por lo que cualquier otra cosa termina congelada o desincronizada en el MP4.cue(frase, desplazamiento = -0.15)devuelve el tiempo de la escena cuando se dicefrase(sin distinción entre mayúsculas/minúsculas, debe aparecer en la locución de esta escena). Una vez que existe el audio, se alinea con las pausas medidas del clip, por lo que las frases que inician una cláusula coinciden con la palabra hablada. Lanza un error si no se encuentra la frase. Úsalo para cadaatsincronizado con la locución.voiceproporciona{ start, duration, end, estimated }para el habla en esta escena (por ejemplo, para mantener un efecto de bucle hastavoice.end).- Para dibujo por fotograma (canvas, movimiento procedural), usa
onFrame((tiempoEscena) => dibujar(tiempoEscena)). Se llama en cada fotograma mientras la escena es visible. - Superposición (opcional):
"overlay": "scenes/_overlay.js"envideo.jsonagrega un módulo dibujado sobre cada escena durante todo el video, para un código de tiempo, barra de progreso, logotipo o borde de fotograma. Recibe{ el, tl, onFrame, scenes, duration, ... }, dondeonFrame((t) => ...)recibe el tiempo global ysceneslista elstart/endde cada escena. random()está inicializado por escena, yMath.randomtambién lo está, por lo que los diseños aleatorios son reproducibles.- La función puede ser
async(por ejemplo, parafetch('/assets/data.json')). Las fuentes se cargan antes de que se ejecuten las escenas, por lo que medir texto funciona. - Los colores del tema son variables CSS:
var(--bg) --surface --surface2 --fg --muted --accent --accent2 --accent3 --accent4 --danger, más--fonty--mono. Sobrescríbelos envideo.json→"theme": { "accent": "#ff3366" }. - Los plugins de GSAP están registrados globalmente: DrawSVG, MotionPath, MorphSVG, SplitText, Text, ScrambleText, CustomEase.
Barra de calidad
- Una idea por escena. Algo en pantalla debe moverse o cambiar al menos cada 2–3 segundos, y cada animación debe coincidir con la línea de locución que ilustra (usa
cue(); la locución va a ~2.5 palabras/segundo). - El texto es grande: cuerpo ≥ 40px y etiquetas ≥ 26px en el escenario 1920×1080. Mantenlo dentro del área segura (
.safe, márgenes de 96/128px). - Construye los diagramas pieza por pieza en lugar de mostrar un diagrama terminado.
- Deja una breve pausa al final de cada escena antes del fundido, para que se pueda leer el estado final.
Locución
El audio proviene del servidor OpenRouter MCP (generate-speech); esta habilidad solo lo planifica y registra. Si esas herramientas no están disponibles, dile al usuario que la locución necesita el servidor OpenRouter MCP conectado, y renderiza sin voz mientras tanto. reference/voiceover.md tiene el procedimiento completo, la elección de modelo y voz, y los costes.