Communitygithub.com

learnermaker/project-demo-video-skill

Produce un video demo narrado y subtitulado en 1080p (3 minutos o menos) para cualquier proyecto de software, entrada de hackathon o producto, como código, usando Playwright y ffmpeg-static.

¿Qué es project-demo-video-skill?

Esta habilidad permite crear videos demo profesionales y verificables de proyectos de software, todo como código reproducible. Está diseñada para agentes de IA como Claude Code, Cursor o Codex, y se enfoca en grabar la aplicación real, el resultado real de terminal y el código fuente real, evitando maquetas o simulaciones. El proceso incluye fases que van desde la verificación de requisitos, prueba del proyecto, creación de storyboard y narración, adaptación de plantillas, captura de pruebas de terminal, generación de voz (usando la voz integrada de Windows o ElevenLabs opcional), grabación con Playwright mediante screencast de Chromium, y ensamblaje final con ffmpeg. El resultado son videos MP4 de 1080p con audio y subtítulos sincronizados, archivos SRT, una miniatura y documentación como listas de cambios y reportes. La habilidad impone reglas estrictas: trabajar siempre en una copia del repositorio, ser honesto en pantalla etiquetando cualquier simulación, no mostrar secretos ni mencionar herramientas de IA, y no desplegar ni gastar dinero. Todo el pipeline está documentado en el SKILL.md, explicando cómo el director, el grabador y el audio se sincronizan para evitar desvíos entre imagen y voz.

Compatible con✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/learnermaker/project-demo-video-skill/tree/HEAD/skills/project-demo-video

Preguntar en tu IA favorita

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

Documentación

project-demo-video

Construye el video demo como código para que pueda re-renderizarse en minutos tras cualquier cambio. Todo lo que aparece en pantalla es real: la aplicación en ejecución, el resultado real de comandos, los archivos fuente reales. Nada es una maqueta.

Esta habilidad está escrita para que un agente de codificación menos capaz pueda seguirla paso a paso. Sigue las fases en orden. Cada fase tiene una línea Listo cuando: no avances hasta que sea verdad. Copia los archivos de templates/ en lugar de volver a escribirlos.

Instalación: copia toda esta carpeta (SKILL.md y templates/) a la carpeta de habilidades del agente, por ejemplo ~/.claude/skills/project-demo-video/, o simplemente apunta al agente hacia SKILL.md.

Lo que produces

SalidaNotas
<nombre>-voiced.mp41920x1080, 30 fps, H.264 + AAC, subtítulos incrustados, con voz
<nombre>-captions-only.mp4misma imagen, pista de audio silenciosa (el usuario puede elegir esto o grabar su propia voz)
<nombre>.srtsubtítulos como archivo, mismo texto que la narración
thumbnail.png1280x720
narration.md, shotlist.mdguión con marcas de tiempo reales; cada escena con su fuente
frames/los fotogramas de QA que realmente revisaste
repo/archivos del proyecto cambiados o nuevos, reflejados en sus rutas de repositorio
COPY_LIST.md, README-snippet.md, REPORT.mdqué copiar y dónde, una sección "Verlo funcionar" del README, qué está hecho y qué es riesgoso

0. Reglas que no debes romper

  1. Fuente de solo lectura. Nunca escribas, hagas commit ni subas cambios desde los repositorios reales del usuario. Trabaja en una copia (robocopy/cp, excluyendo node_modules, .git, salidas de compilación), luego ejecuta npm ci allí. Mantén los cambios que valga la pena conservar dentro de la carpeta de salida, reflejando las rutas del repositorio, más COPY_LIST.md. Otra herramienta o persona hará el trabajo de git.
  2. Sé honesto en pantalla. Cualquier cosa simulada (un dispositivo, un conector, un pago, un modelo) está etiquetada como simulada en el video. Nunca impliques hardware, despliegues o usuarios que no existan. Copia la propia tabla "real vs simulado" del proyecto; si el README está mal, dilo en el informe y no repitas la afirmación.
  3. Sin secretos en pantalla ni en los archivos que entregues: tokens, claves de API, contenido de .env, claves privadas, correos reales. Los tokens de desarrollo que el proyecto documenta (por ejemplo Bearer dev-alice) están bien. Cuando muestres código, elige rangos de líneas que excluyan secretos.
  4. No menciones la herramienta de IA que escribió el código, ni su proveedor, en fotogramas, narración, subtítulos o documentos entregados, a menos que el usuario diga lo contrario. Un ID de modelo en tiempo de ejecución que forma parte de la configuración del producto puede aparecer como nombre de modelo. Verifica con grep -ri antes de terminar.
  5. Respeta las reglas del concurso. Lee el límite de duración (generalmente "menos de 3:00"; apunta a 2:50) y la regla de tecnología requerida. El video debe mostrar la tecnología requerida realmente funcionando, no solo mencionarla.
  6. Nivel gratuito, sin despliegues, sin compras. No despliegues, crees recursos en la nube ni gastes dinero. Si un paso necesita una credencial o una grabación del usuario, pregunta una vez, claramente, y continúa con todo lo demás.
  7. Pregunta al usuario solo por credenciales, sus propias grabaciones, o una elección que realmente sea suya (por ejemplo qué voz). Decide todo lo demás tú mismo y di lo que decidiste.

1. Prerrequisitos (verifica, no asumas)

node --version          # 22+ (24 recomendado; los archivos .mjs deben ser JavaScript simple, sin conversiones de TypeScript)
  • En la copia del proyecto: @playwright/test (con Chromium instalado) y ffmpeg-static deben existir. Verifica: node -e "import('@playwright/test').then(async m=>{const b=await m.chromium.launch();console.log(b.version());await b.close()})" y node -e "import('ffmpeg-static').then(m=>console.log(m.default))". Si faltan y el proyecto prohíbe nuevas dependencias, colócalos en una carpeta de herramientas separada y apunta hacia ella.
  • Voz: Windows PowerShell con System.Speech (incorporado). En macOS usa say, en Linux espeak-ng; luego debes adaptar templates/lib/tts.mjs (solo synthAll y tts.ps1 son específicos del SO).
  • No ffprobe viene con ffmpeg-static: mide duraciones desde las cabeceras WAV (wavSeconds en templates/lib/tts.mjs) y desde la temporización del propio grabador.
  • Notas para shell de Windows: prefiere las herramientas Write y Edit para cualquier archivo que contenga barras invertidas, expresiones regulares o $. La reescritura en línea con python -c/heredoc de JS corrompe silenciosamente los escapes. El sleep en primer plano está bloqueado en algunos agentes: espera con until grep -q "done" log; do sleep 10; done.

2. Fases

Fase A: Requisitos y mapa de pruebas

Lee la página de reglas, el README, los documentos propios del proyecto y cualquier nota de la historia. Escribe una tabla en tus notas:

El juez debe estar convencido de queDonde el video lo pruebaEscena
la tecnología requerida se llama en el códigollamada real visible: registro de solicitudes, terminal, extracto de código...
la experiencia funcionaaplicación ejecutándose en un navegador...
qué es real vs simuladola propia tabla del proyecto...

Listo cuando cada fila tenga una escena.

Fase B: Espacio de trabajo

  1. Copia el repositorio a una carpeta temporal, luego instala: npm ci.
  2. Copia templates/ dentro de la copia como video/ (mantén la estructura: video/lib, video/pages, video/scenes, video/final.mjs, video/narration.mjs, ...). Agrega a package.json los scripts: "video:final": "node video/final.mjs". No agregues dependencias.
  3. Agrega video/out/ a .gitignore en la copia.

Fase C: Ejecución en seco de la aplicación real y caza de errores (haz esto antes de grabar nada)

Inicia la aplicación como lo haría un juez (npm run demo o el comando del README) en su puerto predeterminado documentado, para que cada comando mostrado en pantalla sea literal. Condúcela con un pequeño script de Playwright, toma capturas de pantalla a 1920x1080 después de cada paso de la historia, y míralas. Verifica:

  • inicio de sesión, paneles vacíos, banners de error rojos, errores de consola;
  • cada estado que la historia necesita realmente se renderiza (la primera ejecución del proyecto de referencia mostró un inicio de sesión fallido, una bandeja de entrada vacía y una vista clave faltante, todo oculto por un borrador antiguo del video);
  • texto duplicado o cortado.

Corrige errores reales en la copia, de manera mínima, ejecuta las comprobaciones propias del proyecto (npm run verify, pruebas, typecheck) y registra cada corrección para COPY_LIST.md. Si la aplicación no tiene forma de mostrar el tráfico de red, agrega un pequeño gancho opcional (un registro de solicitudes) en lugar de simular uno. Listo cuando toda la historia se ejecute limpia dos veces seguidas y las comprobaciones del proyecto pasen.

Fase D: Storyboard y narración

  1. Elige escenas. Historia de referencia (aproximadamente 2:50):
EscenaDuraciónMuestra
gancho12 sun cambio se propaga a través de varios compromisos (tarjeta animada)
arranque6 sel único comando que lo inicia todo, resultado real
sim (la app)60 s8-10 momentos; registro de solicitudes abierto en 2 momentos
terminal(es) de prueba17-30 scliente oficial o CLI contra el mismo servidor; comprobaciones de autenticación
recorrido de código16 s3-4 extractos reales que prueban la tecnología requerida
arquitectura, pila en la nube, real vs simulado30 sdiagrama, fuente de la pila, tabla de honestidad, tarjeta de código abierto
cierre12 seslogan, enlaces al repositorio, créditos de construcción, pie de página "X es simulado"
  1. Escribe video/narration.mjs: scenes = [{ id, title, segs: [{ id, text }] }]. Cada seg es una línea hablada y un subtítulo (text se muestra tal cual). Reglas: aproximadamente 2.4 palabras por segundo; total de palabras habladas 330-380; una idea por línea; no más de aproximadamente 12 palabras en cualquier tarjeta en pantalla; mayúscula inicial; sin exageraciones.
  2. Agrega pistas de pronunciación en la tabla say solo para el motor de voz (siglas deletreadas, "Alexa+" como "Alexa plus"); nunca cambies el texto del subtítulo. Listo cuando la narración se lea como una historia que un extraño entiende y cada fila del mapa de pruebas sea hablada o mostrada.

Fase E: Adapta las plantillas

Edita solo lo específico del proyecto:

ArchivoEdición
video/narration.mjstus escenas y líneas
video/scenes/sim.mjslos pasos de tu aplicación: qué escribir o hacer clic, selectores, los zooms de cámara (lens(...), zoomWire)
video/scenes/static.mjscomandos de terminal (CMD), codeBlocks (archivo + regex de la primera línea + recuento de líneas + resaltar regexes), llamadas a páginas de gancho/cierre
video/proofs.mjs, video/proof-auth.mjslos comandos reales que prueban tus afirmaciones; imprimen solo códigos de estado y nombres de error
video/pages/*.htmltexto, colores, mosaicos, cuadros de diagrama, filas de tabla, enlaces
video/lib/overlay.mjsselectores SIM_CSS para el diseño de tu aplicación (ver reglas de diseño), la fuente de datos del ticker
video/final.mjsla constante LIMIT y el orden de las escenas (el orden proviene de narration.mjs)
video/thumbnail.mjs, video/docs.mjstítulos y la tabla SHOT

No edites lib/recorder.mjs, lib/director.mjs, lib/tts.* a menos que algo esté roto.

Fase F: Captura la salida real de las pruebas

node video/proofs.mjs inicia un servidor nuevo, ejecuta los comandos reales (inspector oficial o CLI de cliente, comprobaciones de autenticación, salida de instalación/arranque) y guarda lo que imprimieron en video/out/proofs.json. Las escenas de terminal reproducen esta salida capturada en una terminal HTML a un tamaño legible. Reglas: muestra el texto real del comando (acorta solo la ruta del paquete); trunca la salida muy larga visiblemente (una línea tenue "... N líneas ...") y pon "Ejecución grabada" en la etiqueta; nunca imprimas tokens; formatea JSON de forma bonita y dilo si lo haces.

Fase G: Audio de narración

Predeterminado (sin cuenta necesaria): node video/final.mjs --rate 2 hace un WAV por línea con la voz incorporada de Windows (Microsoft Zira) y mide cada duración. La tasa 1 es aproximadamente 150 s de voz para el guión de referencia, la tasa 2 aproximadamente 139 s, la tasa 3 es demasiado rápida. Elige la tasa más lenta que mantenga todo el video por debajo del límite. Voz opcional con sonido humano: ver Apéndice A. Todo lo siguiente funciona igual con cualquiera.

Fase H: Graba y ensambla

node video/final.mjs --rate 2                   # todo: tts, pruebas, grabar todas las escenas, ensamblar ambos cortes
node video/final.mjs --rate 2 --reuse-proofs    # omitir volver a ejecutar pruebas
node video/final.mjs --scenes sim,close --reuse-tts --reuse-proofs   # volver a grabar solo algunas escenas
node video/final.mjs --assemble-only            # re-multiplexar (subtítulos, audio) sin volver a grabar
node video/final.mjs --tts-dir video/out/tts-human --name mi_video --single    # construir desde otra carpeta de audio

Una ejecución completa tarda de 6 a 10 minutos; ejecútala en segundo plano y consulta el registro. El script falla si el total es 180 s o más. Apunta a 170 s. Importante: si cambias el texto de la narración o la voz, las duraciones cambian, por lo que cada escena debe volver a grabarse (las marcas provienen de las duraciones). Cambiar solo los elementos visuales de una escena significa --scenes <id> --reuse-tts.

Fase I: QA (nunca lo saltes, nunca confíes en el registro)

  1. Extrae fotogramas al final de cada línea de narración del MP4 final: ffmpeg -ss <markEnd-0.3> -i final.mp4 -frames:v 1 frame.png (la línea de tiempo está en video/out/timeline.json). Obsérvalos. Verifica legibilidad a 1080p, espacio vacío, texto recortado, posición y tamaño de los subtítulos, sincronización (el evento del subtítulo está en pantalla), sin secretos, sin mención del proveedor, etiquetas de honestidad presentes.
  2. ffmpeg -i final.mp4 -af volumedetect -f null -: media alrededor de -22 dB, máxima por debajo de 0 dB.
  3. Duración por debajo del límite en ambos cortes; ambos archivos tienen una pista de video y una de audio.
  4. grep -ril "<palabras del proveedor>" video/pages video/narration.mjs <documentos de salida> no devuelve nada excepto un ID de modelo permitido.
  5. Ejecuta las comprobaciones del proyecto nuevamente (npm run verify; e2e si la interfaz de usuario cambió).
  6. Corrige y vuelve a renderizar hasta que todo pase.

Fase J: Entregables

Genera narration.md y shotlist.md con node video/docs.mjs <dir_salida> (marcas de tiempo reales). Copia los MP4, SRT, miniatura (node video/thumbnail.mjs <png>), los fotogramas de QA y los archivos del repositorio cambiados (todos los archivos que difieren entre el original y tu copia, en sus rutas de repositorio). Escribe COPY_LIST.md (correcciones de errores primero, luego archivos nuevos, mensajes de commit sugeridos, "ejecutar compilación y verificar después de copiar"), README-snippet.md (comando de ejecución, archivo de punto de entrada:línea, archivo de cliente:línea, archivo de habilidad, marcador de posición de enlace de video) y REPORT.md (hecho, qué debe hacer el usuario, riesgos). Di claramente lo que no pudiste verificar (por ejemplo, no pudiste escuchar la voz).

3. Cómo funciona el pipeline (léelo antes de cambiarlo)

Grabador (lib/recorder.mjs). El recordVideo propio de Playwright limita la tasa de bits y desenfoca el texto pequeño, por lo que esto usa screencast de Chromium: los fotogramas JPEG (calidad 92) se sellan cuando llegan, se escriben en disco y se codifican a 30 fps constantes con libx264 crf 17. Las pantallas estáticas producen pocos fotogramas; el último fotograma se mantiene. Las marcas de tiempo provienen de un reloj (performance.timeOrigin + performance.now()), el mismo que usa el director.

Director (lib/director.mjs). Cada línea de narración es un segmento. d.seg(id, actions, {tail}) registra la hora de inicio, ejecuta las acciones en pantalla, luego espera hasta que la línea haya terminado de reproducirse más tail * 0.45 segundos (las colas se recortan globalmente para ajustarse al límite). Las marcas (id, inicio, fin, texto) se guardan en video/out/<escena>.marks.json. Debido a que el audio se coloca más tarde en las marcas grabadas, la imagen y la voz no pueden desviarse. Regla para un momento: haz la acción dentro del segmento y asegúrate de que la acción termine antes de que termine la narración; si la línea es más corta que la acción, divídela en dos líneas (esta es la razón por la que un momento de la historia se convirtió en sim9 y sim10).

Audio (final.mjs). Cada WAV se retrasa hasta su inicio global con adelay, se mezcla con amix=normalize=0, se limita con alimiter, y se rellena con apad=whole_dur=<total> (sin el relleno, -shortest corta el final del video).

Subtítulos. Las pistas son el texto de la narración dividido en finales de oración (decimales como "2.0" están protegidos) y en comas cuando supera los 82 caracteres, con el tiempo dividido en proporción a la longitud. El .srt se entrega. La versión incrustada usa un archivo ASS generado con PlayResX: 1920, PlayResY: 1080 y tamaño de fuente 36. No incrustes un SRT con force_style: libass lo escala contra un lienzo de 288 píxeles de alto y el texto sale aproximadamente cuatro veces demasiado grande.

Superposición (lib/overlay.mjs), inyectada solo en la página de la aplicación grabada (la propia aplicación no se edita en absoluto; el grabador captura el HTML renderizado, no la fuente).

Apéndice A: Voz opcional de ElevenLabs (con sonido humano)

  1. Obtén una clave API de https://elevenlabs.io (el nivel gratuito da 10 000 caracteres/mes, suficiente para ~6 videos).
  2. Agrega ELEVENLABS_API_KEY a un archivo .env o pásalo como variable de entorno.
  3. Ejecuta npm install elevenlabs-node en la copia del proyecto (no en el proyecto original).
  4. En video/lib/tts.mjs, descomenta el bloque "ElevenLabs" y comenta el bloque "PowerShell".
  5. Ejecuta como de costumbre: node video/final.mjs --rate 2.

Nota: ElevenLabs es más lento (~2 s por oración) y requiere conexión a internet. El grabador espera a que el TTS termine antes de grabar, por lo que una ejecución completa puede durar el doble.

Apéndice B: Solución de problemas comunes

ProblemaCausa probableSolución
El video final está en negroEl grabador no pudo conectar con el screencast de ChromiumVerifica que @playwright/test y Chromium estén instalados; ejecuta npx playwright install chromium
Los subtítulos son demasiado grandesSe usó force_style en SRT en lugar del archivo ASSUsa el generador ASS incorporado en final.mjs
El audio está fuera de sincroníaLas marcas de duración cambiaron sin volver a grabar las escenasVuelve a ejecutar con --scenes all --reuse-proofs (no --reuse-tts si la voz también cambió)
ffmpeg-static no se encuentraLa instalación de npm falló o la ruta es incorrectaVerifica con `node -e "import('ffmpeg-static').then(m=>console.log(m.default))"; reinstala si es necesario
El video excede el límite de tiempoDemasiadas palabras o la tasa de voz es demasiado lentaAcorta la narración (apunta a 350 palabras) o aumenta la tasa a 3
Error "No se puede encontrar el módulo"Falta una dependencia en la copiaEjecuta npm ci en la carpeta de la copia, no en el repositorio original
Los comandos de Windows PowerShell fallanLa política de ejecución bloquea los scriptsEjecuta Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass antes del comando
La imagen de la terminal es demasiado pequeñaEl tamaño del viewport no coincide con el diseño de 1920x1080Ajusta viewport: { width: 1920, height: 1080 } en la configuración de Playwright
La superposición no apareceEl URL de la página no coincide con el patrón de inyecciónVerifica el selector SIM_CSS en overlay.mjs contra la URL real de la aplicación
El video tiene fotogramas perdidosEl sistema es demasiado lento para mantener 30 fpsReduce la calidad JPEG a 85 o baja la tasa de fotogramas a 24 (cambia en recorder.mjs)

Skills relacionados