Communitygithub.com

lladbet/video-explainer-skill

Crea videos explicativos en MP4 escribiendo escenas 2D animadas en HTML/SVG/CSS con GSAP y renderizándolas fotograma a fotograma con Chromium headless + ffmpeg.

¿Qué es video-explainer-skill?

Esta habilidad permite crear videos explicativos animados de alta calidad, como lo haría un motion designer, pero escribiendo código. Cada escena es un pequeño módulo JavaScript que construye elementos DOM/SVG y define animaciones en una línea de tiempo de GSAP pausada. El renderizador avanza esa línea de tiempo fotograma a fotograma, captura cada uno con Chromium headless y los envía a ffmpeg para generar un video MP4 determinista y reproducible. Soporta narración mediante servicios externos (OpenRouter, ElevenLabs, Gemini) y ofrece un flujo de trabajo completo: storyboard, desarrollo de escenas, verificación con lint, vista previa con contact-sheet y renderizado final. Es ideal para creadores de contenido educativo, documentación técnica, demostraciones de productos o cualquier situación que requiera un video animado a partir de código. Funciona con cualquier agente de IA o herramienta de línea de comandos que pueda ejecutar scripts Node.js y Chromium.

Compatible con~Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/lleadbet/video-explainer-skill/tree/HEAD

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

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

  1. 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 tiene narration (lo que dice la locución) y visual (lo que se ve en pantalla). Lee reference/storyboard.md. No escribas el código de la escena hasta que el storyboard tenga sentido por sí mismo.
  2. Andamio: node SKILL_DIR/scripts/new.mjs <directorio-del-proyecto> --title "...", luego edita video.json y escribe un módulo por escena en scenes/<id>.js. Usa reference/animation-patterns.md para recetas y reference/design.md para diseño, tipografía y color. Con locución, establece las duraciones de escena en "auto" y temporiza las animaciones con cue('frase'). Ambos ejemplos son videos completos con locución: examples/kubernetes-overview/ (ElevenLabs) y examples/how-dns-works/ (Gemini).
  3. Locución (si se desea): sigue reference/voiceover.md. En resumen: agrega un bloque "voice" a video.json, ejecuta node SKILL_DIR/scripts/narration.mjs status <directorio-del-proyecto>, llama a la herramienta generate-speech del servidor OpenRouter MCP una vez por escena listada con exactamente los argumentos que imprime, y registra cada archivo guardado con narration.mjs add. Hazlo antes de la verificación (paso 4), porque el audio real cambia la duración de las escenas y los tiempos de cue().
  4. 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 de tl, elementos fuera del marco, texto recortado y locución demasiado larga para su escena. Corrige cada advertencia a menos que sea intencional.
  5. 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.
  6. 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 con node SKILL_DIR/scripts/serve.mjs <directorio-del-proyecto> (barra de avance, reproducir/pausa, reproducción de locución, c alterna subtítulos, m silencia).

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 en tl (o en otra línea de tiempo agregada a ella). Nunca uses gsap.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 dice frase (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 cada at sincronizado con la locución. voice proporciona { start, duration, end, estimated } para el habla en esta escena (por ejemplo, para mantener un efecto de bucle hasta voice.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" en video.json agrega 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, ... }, donde onFrame((t) => ...) recibe el tiempo global y scenes lista el start/end de cada escena.
  • random() está inicializado por escena, y Math.random también lo está, por lo que los diseños aleatorios son reproducibles.
  • La función puede ser async (por ejemplo, para fetch('/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 --font y --mono. Sobrescríbelos en video.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.

Skills relacionados