Communitygithub.com

thativanng/reels-maker

Construye y renderiza una película, reel o animación como código. Una página HTML dibuja cualquier fotograma bajo demanda con window.seek(t), y un navegador sin cabeza renderiza cada fotograma a 2x en un MP4 con sonido.

¿Qué es reels-maker?

Esta skill convierte la creación de videos en un proceso determinístico basado en código. En lugar de usar generadores de video tradicionales, defines una película como una página HTML que responde a `window.seek(t)` para dibujar el fotograma exacto en cualquier momento `t`. Un renderizador sin cabeza (headless browser) captura cada fotograma a resolución 2x y lo combina con audio mediante ffmpeg, produciendo un archivo MP4 final con sonido normalizado a −14 LUFS. La skill soporta múltiples formatos (9:16, 4:5, 1:1, 16:9) y estilos visuales, y está diseñada para integrarse con flujos de trabajo de IA como Claude Code, Cursor o Codex. Incluye herramientas para previsualización, generación de portadas, verificación de colisiones y auditoría de calidad. Es ideal para desarrolladores que necesitan producir videos animados, reels o explicadores con control total sobre cada fotograma, sin depender de herramientas de edición visual.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/thativanng/reels-maker/tree/HEAD/skills/film-engine

Preguntar en tu IA favorita

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

Documentación

film-engine: películas dibujadas desde código, un fotograma a la vez

Dónde está el kit: ejecuta cada comando de esta skill desde la carpeta reels-maker (el clon de github.com/thativanng/reels-maker que contiene template/ y tools/). Si el proyecto no lo tiene, configúralo primero siguiendo el SETUP.md de ese repositorio.

La idea en un párrafo

Una película aquí es una página HTML con una regla: window.seek(t) debe dibujar exactamente el fotograma para el tiempo t, cada vez, a partir de nada más que t. Nada en la imagen puede leer un reloj real. Luego un renderizador avanza t a 30 fps, captura cada fotograma a 2x y lo envía a ffmpeg. Como la página es determinista, un re-renderizado coincide (en promedio con una fracción de nivel; el ruido de la GPU puede mover píxeles individuales hasta unos 60 niveles), un fotograma fijo en 17.3 s es el fotograma que mostrará el video, y cada verificación (film-audit) puede examinar cualquier momento sin reproducir la película.

La ruta (no te saltes ninguna estación)

  1. Brief y look primero. reel-intake termina en un brief firmado y un storyboard (formato, duración, sonido, qué nunca aparece); reel-style termina en un look elegido. No abras los pasos de construcción de esta skill sin ambos.
  2. Storyboard. reel-intake lo escribe (tiempo · palabras en pantalla · sonido y subtítulo · por qué; references/brief.md), y la persona lo firma antes de que construyas. Un estilo con banner añade una columna de banner.
  3. Tira de prueba. Construye unos primeros 10 s en el look elegido (una película de estilo caminata puede necesitar 15–30 s para mostrar un capítulo), toma fotogramas fijos, obtén un sí. Una construcción completa sobre un look no firmado es trabajo desperdiciado.
  4. Construye la película completa (el pack de estilo dice cómo construir escenas).
  5. Puertas (film-audit): la verificación de colisiones a 40 fps, las sondas de píxeles, luego un auditor independiente. Nada llega a la persona antes de esto.
  6. Renderiza: node tools/render.mjs --page reels/<name>/page.html --out reels/<name>/out/film-silent.mp4 (fotogramas a 2x, escalados con lanczos, sin etiquetas de color).
  7. Audio: python3 template/audio.py out/film.wav (una lista de señales en el mismo reloj que la película).
  8. Finaliza: zsh tools/mux.sh out/film-silent.mp4 out/film.wav out/film.mp4. Esto da −14 LUFS, un limitador, un archivo H.264 sin etiquetas y una vista previa para teléfono del mismo tamaño que la película (lado corto 720 px).
  9. Portada: node tools/cover.mjs --page reels/<name>/page.html --out reels/<name>/out/cover.jpg. Una página con modo portada (el ?cover del starter, desde config cover) dibuja el fotograma diseñado; cualquier otra página da el fotograma en T=. Se guarda en la forma de la película y se mantiene por debajo de 2 MB, más el recorte central 3:4 para una película 9:16 (la cuadrícula de Instagram, informada). LinkedIn toma una portada solo al publicar: después, solo se puede editar el texto de la publicación (Ayuda de LinkedIn, "Editar una publicación compartida", https://www.linkedin.com/help/linkedin/answer/a522811). Su especificación de miniatura, JPG o PNG en la forma del video y como máximo 2 MB, proviene de la ayuda de LinkedIn según lo citado por un resumen de búsqueda; la página en sí no cargaría, así que trátalo como no verificado.
  10. Envía primero la vista previa para teléfono, luego el archivo completo y la portada.

El contrato de la página

  • window.seek(T): dibuja el fotograma T. Es el único lugar que anima algo.
  • window.ready: una promesa que se resuelve después de fuentes, texturas y el primer fotograma.
  • window.DUR: la duración de la película en segundos.
  • window.FILM = { w, h, format, safe }: el tamaño del fotograma y la zona de seguridad en píxeles. Cada herramienta dimensiona su ventana a partir de esto, así que la página posee su formato.
  • Opcional: window.cover() dibuja la portada en lugar de un fotograma, y FILM.coverAt nombra el momento en el que se asienta (tools/cover.mjs usa ambos; sin ellos toma el fotograma en T=).
  • window.collide(T) y window._dbg: ganchos para film-audit (ver esa skill).
  • Las herramientas sirven la carpeta del kit sobre http por su cuenta (los módulos ES no cargan desde file://). Para ver una página en vivo en un navegador, npm run serve y abre http://127.0.0.1:8765/template/starter.html.

Formatos, el starter y otros idiomas

  • template/formats.json contiene los cuatro tamaños (9:16 1080×1920 · 4:5 1080×1350 · 1:1 1080×1080 · 16:9 1920×1080), el mapa plataforma → formato, y la zona de seguridad de cada plataforma con su fuente. La línea de formato del brief lo elige.
  • Cada reel vive en su propia carpeta, reels/<name>/ (ignorada por git): su brief, storyboard, page.html (una copia del starter o de film.html), su reel.json o career.json, y su out/. Las páginas cargan fuentes y formats.json desde la raíz del kit, por lo que una copia funciona desde allí. Apunta las herramientas hacia él: PAGE=reels/<name>/page.html.
  • template/starter.html + template/reel.json es el esqueleto para todo look excepto la caminata voxel. Sus palabras y tiempos viven en la configuración, se dimensiona desde ?fmt= o la configuración, y mantiene cada palabra dentro del núcleo de seguridad de la plataforma. ?guides muestra las zonas cubiertas en rojo, solo para verificar; nunca renderices con ello activado. Es un esqueleto, no un look: reel-style reescribe sus tokens y diseño.
  • template/film.html (la plantilla voxel) es solo 4:5, y opta por no usar zonas de seguridad (safe es 0): su banner y subtítulos están cerca de los bordes por diseño, por lo que la sonda safe siempre pasa allí y el ojo del auditor es la verificación. Sus cámaras están ajustadas para 4:5; otra relación significa reajustarlas y volver a ejecutar cada puerta.
  • Chino, japonés, coreano: el starter carga Noto Sans TC desde Google Fonts al renderizar (solo los caracteres que usa la configuración), y ejecuta texto CJK a aproximadamente 0.72× del tamaño latino (sus caracteres son un em completo). Escribe \n donde una línea debe romperse: el chino se rompe entre dos caracteres cualesquiera, por lo que una línea larga necesita una rotura que elijas. template/reel.zh.json es un ejemplo trabajado. Cualquier otro script necesita su propia fuente en la pila.

Relojes y temporización

  • Un reloj de película impulsa las escenas, la cámara, el banner, los subtítulos y el sonido. Cuando insertes un tiempo después sin re-temporizar todo, usa un re-mapeo: el reloj de la escena se pausa en el punto de inserción mientras las superposiciones mantienen el reloj real:
    const INS = [[24.0, 5.0, 'detail']];   // en tiempo de escena 24.0, reproduce un tiempo insertado de 5 s
    function remap(T) { let sh = 0; for (const [a, len, id] of INS) { if (T < a + sh) return { t: T - sh, ins: null }; if (T < a + sh + len) return { t: a, ins: id, u: T - sh - a, len }; sh += len; } return { t: T - sh, ins: null }; }
    
    El script de audio necesita la misma tabla: escribe un RT(t) que suma las longitudes insertadas a un tiempo de escena, y coloca cada señal en RT(t), o cada señal después del inserto se desvía. (La plantilla no tiene insertos, por lo que su audio.py no necesita uno.)
  • Suavizado: eio para movimientos de cámara, eo para cosas que llegan, trap (velocidad trapezoidal) para caminar y conducir, para que un objeto en movimiento no "chasquee" en los extremos.
  • Fotos de cámara son absolutas A(pos, target) o de seguimiento F(offset, targetOffset) desde el punto de tierra del héroe. Mezcla entre fotos con eio. Mantén un momento que importe; un plano que se mueve todo el tiempo se lee como nervioso.

Las superposiciones (en DOM, sobre el canvas)

  • Banner: año, luego empresa, luego la píldora del rol, más una línea de tiempo cuyo punto respira sobre el año actual. El banner cambia cuando la nueva escena está EN PANTALLA (su conjunto ha subido, el atuendo ha cambiado, el héroe ha llegado), nunca cuando el código comienza a construirlo. Verifica esto en una hoja de fotogramas a ±1 s alrededor de cada cambio.
  • Subtítulos: una línea de historia por tiempo, con la frase clave en negrita y acento. Usa text-wrap: balance. Dale al lector aproximadamente 3 palabras por segundo, y desvanece un subtítulo antes de que un movimiento rápido de cámara descubra al sujeto detrás de él.
  • Un globo de diálogo que sigue a un personaje: proyecta el punto de la cabeza y fija el globo ≥24 px dentro del fotograma.
  • La tarjeta final: nombre, un eslogan, en qué son buenos, una cita. La escena se desenfoca detrás.

Audio

  • Las señales se escriben en el reloj de la película, junto al momento al que pertenecen (pasos por pierna de caminata, un timbre cuando algo aparece, un golpe cuando aterriza).
  • Sin sonidos de "whoosh" en las transiciones; se leen como baratos. Usa timbres, tics e impactos suaves.
  • La base por defecto en template/audio.py se genera a partir de ondas senoidales, por lo que es gratis de usar en cualquier lugar. Si alguien trae una pista generada por IA, verifica su licencia: algunos pesos de modelos solo permiten uso no comercial.
  • Volumen: normalización de dos pasos loudnorm a −14 LUFS (medir, luego aplicar linealmente), luego un limitador. Un solo paso se excede.

Hechos de renderizado que nos costaron días (más en references/gotchas.md)

  • Renderiza a 2x y escala hacia abajo. EffectComposer elimina el anti-aliasing del canvas, por lo que un renderizado a 1x tiene bordes dentados por todas partes.
  • Sin etiquetas de color en el MP4. Una tubería JPEG lo etiqueta como rango completo, y algunos reproductores desplazan entonces los colores. setparams=...=unknown en el filtro.
  • Dos caras en un mismo plano parpadean (z-fighting). Dale a la frontal un desplazamiento de profundidad.
  • Las fuentes deben cargarse (document.fonts.load) antes de dibujar cualquier texto en el canvas, o se recurre silenciosamente a una alternativa.
  • GPU activada en Chromium sin cabeza (--use-angle=metal en macOS). Sin ella, un renderizado es aproximadamente 10x más lento.

Trabajar sin un prompt en cada paso

La persona dijo sí a las reglas de permiso del kit una vez (SETUP.md paso 4); mantente dentro de ellas, o se les preguntará una y otra vez:

  • Escribe y cambia archivos con las herramientas de escritura y edición: páginas, reel.json, brief, storyboard, AUDIT.md. No los generes con un script de Python o shell, un heredoc o sed -i; cada uno de esos es un prompt de aprobación.
  • Mide con las herramientas del kit (stills, probe, sheet, collide, render, cover), no con fragmentos de python3 -c.
  • No se necesita mkdir, bucles ni cadenas: cada herramienta crea su propia carpeta de salida, y toma una lista de tiempos en una sola llamada (stills.mjs … 1.5,6,12).
  • Los scripts propios de un reel (su script de sonido, un helper) viven en reels/<name>/ y se ejecutan como python3 reels/<name>/audio.py o node reels/<name>/x.mjs, lo cual las reglas cubren.

Comandos

Ejecuta cada herramienta como un solo comando simple (flags, sin VAR=… al inicio, sin bucles, sin cadenas &&). Así, las reglas de permiso de la persona (node tools/*) lo cubren y no se les pide aprobar cada paso. Las variables de entorno siguen funcionando para los scripts.

npm run doctor                                  # verifica esta máquina primero
npm run demo                                    # la prueba de 12 segundos: out/demo/
node tools/stills.mjs "2,8.5,15"                # fotogramas fijos a 2x → out/stills (la plantilla voxel)
node tools/stills.mjs --page template/starter.html --format 16:9 1.6        # el starter en otra relación
node tools/stills.mjs --page template/starter.html --cfg reel.zh.json 1.6   # otra configuración (aquí: chino)
node tools/stills.mjs --dsf 1 --out out/chk 2,8.5                           # fotogramas rápidos a 1x para una hoja
python3 tools/sheet.py out/chk/sheet.jpg 6 300 out/chk/t*.jpg
node tools/render.mjs --page reels/<name>/page.html --out reels/<name>/out/film-silent.mp4
python3 template/audio.py out/film.wav
zsh tools/mux.sh out/film-silent.mp4 out/film.wav out/film.mp4

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

thativanng/reels-maker

The gates every code-rendered film passes before anyone sees it. A 40 fps collision pass (no mover touches a solid or another mover), pixel probes (nothing sits over or behind a face, a head, a subtitle or the frame edge), then an independent auditor (a fresh agent) who reads frame sheets at ≥2 fps and signs off. The builder never signs off their own film. Use it before sending ANY render, after every fix round, and whenever someone says "check the film", "audit this", "is it ready", or "why does this frame look wrong".

thativanng/reels-maker

The interview before any video is built. It reads what the person already gave (source text, CV, links, brand files), then asks the rest in a few short rounds of numbered questions with a recommended answer marked, always including the format (platform, reel or video, ratio, layout, length, sound). It ends in a one-screen brief that splits "you said" from "I assumed", plus a storyboard, and builds nothing until the person says yes. Use it whenever someone asks for a reel, a video, an explainer, a career film, an animation or a "make a video about X", before film-engine or any style pack, and again when a request changes its purpose, platform or audience mid-way.

thativanng/reels-maker

Picks the look of a reel or film before it is built. It shows three genuinely different looks as stills of the same opening frame with the same words, so the person judges the look and not the content. It keeps a log of every look ever shown, so no look comes back unless asked for, and it locks one look on purpose for a series. Use it after reel-intake's brief is signed and before any test strip, whenever someone says "what should it look like", "give me options", "make it look different from last time", "it looks cheap", or starts a new reel or a new series.

thativanng/reels-maker

The voxel career-film style. A blocky 3D version of the person walks, without ever vanishing, through one scene per role. Their outfit changes as they walk, each scene rises out of the floor ahead of them, and a dark banner tracks year, company and role. Use it when someone wants their career, a journey or a "how I got here" told as a short 3D film for LinkedIn or a portfolio, says "make my career film", "turn my CV into a video", "voxel film", or wants a new chapter added to an existing one. It runs on film-engine (render, audio, finish) and must pass film-audit before anything is sent.

Skills relacionados