Spatial Film
Probado de principio a fin en un vídeo de producto de 60 s (7 escenas, 5 workers en una sola oleada, cada costura exacta al fotograma, comprobación limpia,
1080p60 en 4m19s). La plantilla film.json contiene las barras de escena reales y la hoja de referencia de ese vídeo como ejemplo práctico.
<skill> = la carpeta de este skill (el directorio donde está este SKILL.md). Ejecuta cada script desde la raíz del proyecto de vídeo.
python abajo = el comando Python guardado por la comprobación de configuración de explainer-kit en ~/.talking-explainer/config.json
(py -3.12 en Windows, python3 en macOS).
Lo que necesitas
- Node 20+,
ffmpeg+ffprobeen PATH, Python 3.10+ connumpyyPillow. - Los skills de HyperFrames:
npx hyperframes skills update general-video hyperframes-animation media-use(constructor de paquetes, planos + mapa de animación, ayudante de música). El CLI en sí está fijado por proyecto y se ejecuta a través denpx. - Playwright para prerenderizar superficies:
npm i -D playwrighten la carpeta del vídeo (o configuraPLAYWRIGHT_PATH). - Opcional, para música con fuente: una cuenta de HeyGen; ejecuta
npx hyperframes auth loginuna vez en tu terminal. Sin ella, usa tu propia pista y comienza la fase 4 enmusic-analyze.py. - Una máquina con GPU para el renderizado final (aún renderiza por la ruta de software, unas 15 veces más lento).
Cómo usar este skill sin desperdiciar contexto
- Carga UNA referencia de fase a la vez, cuando esa fase comience. Nunca las precargues todas.
- Los scripts son el código probado. Ejecútalos; nunca regeneres su lógica en línea.
- La sesión principal nunca lee un archivo de escena completo (20-60KB). Evalúa las escenas por hojas de contacto; corrige con
grep -nmás un parche exacto. - Los workers reciben RUTAS de archivo, no contenido pegado.
storyboard.html(60KB+) se busca por id de celda, nunca se lee completo. - Lee primero una hoja de contacto; abre solo los PNG de instantánea individuales para comprobar un detalle.
Fases (cada una termina en una puerta - no avances hasta que pase)
| # | Fase | Cargar | Puerta |
|---|---|---|---|
| 0 | Andamiaje: node <skill>/scripts/new-film.mjs <dir> --name <slug> | - | la carpeta tiene film.json, frame.md, kit, documentos de plantilla |
| 1 | Recepción, BRIEF, STORYBOARD, hoja de bocetos | references/storyboard.md | "ok" explícito del usuario registrado bajo ## Bloqueado |
| 2 | frame.md, kit, superficies prerenderizadas | references/surfaces.md (+ platform-canvases.md solo si aparecen GHL/n8n/Zapier) | sin {{ en frame.md; cada PNG de captura revisado |
| 3 | Prueba de humo del andamiaje, paquetes, workers paralelos | references/workers.md | cada escena: lint 0 errores, la hoja de contacto coincide con su bloque |
| 4 | Música + SFX (ejecutar mientras los workers construyen) | references/audio.md | base musical a BPM nativo, compás 0 en un tiempo fuerte; claves en film.json |
| 5 | Montaje, comprobación, costuras, renderizado, envío | references/verify-ship.md | comprobación pasa, verificación de costuras todo OK, renderizado con gpu de hardware verificado |
Ruta a través de /hyperframes -> /general-video para los pasos de intención y revisión de bocetos (este skill proporciona
la forma del proyecto, el contrato, los scripts y las puertas; general-video proporciona el constructor de paquetes y el rol de worker).
Archivos
| Ruta | Qué |
|---|---|
template/ | Esqueleto del proyecto: film.json (barras de escena, BPM, bgm, SFX, claves - la ÚNICA fuente de tiempo), frame.md (contrato de verdad de diseño con rellenos), BRIEF.md, STORYBOARD.md, assets/kit.css (kit base del vídeo), assets/fonts/ (Poppins 500/700 + OFL), references/worker-brief.md, references/scene-starter.html (esqueleto de escena limpio de lint), references/snippets.html, package.json (hyperframes 0.8.117 fijado). |
surfaces/ | Reconstrucciones HTML listas de los lienzos de GHL, n8n y Zapier + una página de marketing larga (datos ficticios de Northwind Dental), platform.css, sprite.svg, jobs.json. |
scripts/new-film.mjs | Andamiaje de un proyecto a partir de la plantilla. |
scripts/capture-surfaces.cjs | Renderiza src/captures/*.html -> assets/captures/*.png a 2x (jobs.json). |
scripts/measure-surface.cjs | Cajas 1x de elementos en una superficie, para objetivos exactos de anotación/recorte. |
scripts/make-harness.py, scripts/test-scene.mjs | Proyectos de prueba aislados por escena; lint + instantánea de una escena. |
scripts/music-search.mjs, sfx-search.mjs | Catálogo HeyGen mediante el helper REST de media-use (seguro en Windows). |
scripts/music-analyze.py, music-bars.py | Tempo solo con numpy + energía por compás (no necesita librosa). |
scripts/bgm-cut.py | Corta la base musical en líneas de compás, empalma el propio outro de la pista, fundidos; --b repite para hacer un bucle de una base debajo de un vídeo más largo. |
scripts/assemble.py | film.json -> index.html (ranuras de escena en la cuadrícula de compases, bgm, SFX con duraciones + carriles). |
scripts/seam-check.py | Comprueba cada costura (antes / en / después del corte) y el bucle (fotograma 0 vs último) a nivel de píxel. |
scripts/render-final.mjs | Renderizado CLI con --browser-gpu, registrado en un archivo, modo gpu + ffprobe verificado, luego flash-scan.py. |
scripts/flash-scan.py | Cada fotograma de un renderizado: un fotograma distinto a ambos vecinos (un elemento mostrado durante 1/60 s) = un error. |
scripts/encode-web.mjs | Codificación web (H.264 CRF 21, faststart, AAC o --silent) + fotograma de póster. |
scripts/blink-fit.py, scripts/build-presenter.py | Vídeos narrados: superposiciones de parpadeo para poses de hoja (--band = banda de ojo); la superposición del presentador desde su pista, con sincronía de labios cuando la mascota tiene mouth-fit.json. |
scripts/el-tts.py, el-words.py, el-music.py | Vídeos narrados mediante ElevenLabs (clave en env EL, nunca en disco): candidatos de voz desde SCRIPT.md, tiempos de palabra de Scribe, una base generada. |
scripts/seam-diff.py | Diferencia de costura con la caja del presentador enmascarada (vídeos narrados). |
scripts/fromto-scan.py | Pre-renderizado: marca props fromTo solo FROM que los workers de renderizado (búsquedas en mitad de escena) nunca aplican. 0 aciertos antes de cada renderizado. |
scripts/patch_exact.py | Buscar/reemplazar exacto, seguro con CRLF para correcciones de escena con script (afirma una coincidencia cada una). |
scripts/selftest.mjs | Prueba de extremo a extremo de que el skill funciona en esta máquina. Debe imprimir PASS. |
Vídeos narrados (una locución + un presentador en pantalla)
Probado en un explicador narrado "API vs Webhook" (83 s, vertical y apaisado, voz clonada, presentador mascota, 10 escenas por 5 workers). Las reglas del vídeo musical anteriores siguen vigentes excepto donde estas las reemplazan:
- Candidatos de voz primero.
EL=<key> python <skill>/scripts/el-tts.py . eric=<voice_id> michelle=<voice_id>lee las líneas sangradas deSCRIPT.mdy escribe un MP3 por voz ensrc/audio/voice-candidates/(clave solo de env, nunca en disco). Deja que el usuario elija de oído, luego transcribe solo la elección. - La narración es el reloj. Tiempos de palabra:
EL=<key> python <skill>/scripts/el-words.py <voice.mp3> src/timing/voice-words.json(ElevenLabs Scribe - sin modelo local, necesita una clave), o faster-whisper conword_timestamps(idioma configurado, VAD activado;transcribe.pyde explainer-kit, gratis y local). Mantén el JSON de palabras ensrc/timing/. Enfilm.jsonconfigura"bpm": 60, "beats_per_bar": 1para que un "compás" sea 1 s y las duraciones de escena sean segundos simples cortados en las pausas de las frases. Los bloques del storyboard dan tiempos de palabra locales a la escena; las llegadas comienzan ~0.1 s antes y aterrizan en la palabra. Dale a un título al menos ~1 s en pantalla antes del siguiente corte (comprueba el corte contra la ÚLTIMA palabra de la línea que muestra). - Extras de
film.json(assemble.py + make-harness.py los leen):"audio": [{id, src, dur, volume}]para la voz,"overlays": [{id, track, z}]para una capa de película completa (el presentador) que se sienta sobre cada escena, por lo que nunca aparece en una costura y las escenas permanecen libres de presentador. - Presentador desde PNGs de pose: un PNG transparente por pose (
prep-mascot.pyde explainer-kit recorta, escala y escribe el manifiesto), luegoscripts/blink-fit.py <mascot_dir>(opcional, necesita torch; ajusta la banda de ojo BLINK en cada pose mediante NCC; las poses por debajo de 0.70 no reciben parpadeo) yscripts/build-presenter.py(escribe la superposición desdesrc/presenter-track.json: poses en palabras clave, anclado a la cabeza para que los intercambios nunca salten, balanceo por envolvente de voz, respiración, parpadeos, fx). Nunca edites a mano su salida. - Audio: loudnorm a la voz a -16 LUFS / -1.5 dBTP (una ganancia simple limitada a +4.6 dBTP), sidechain-duck de la
base debajo de ella fuera de línea (~17 dB por debajo de la voz), SFX bajo (0.1-0.45). Rellena la clave de voz o el duck trunca
la base a la longitud de la voz (comprueba la advertencia "bgm 110.25s vs slot 113s"):
[1:a]apad=whole_dur=<TOTAL>[k];[0:a][k]sidechaincompress=threshold=0.03:ratio=6:attack=25:release=450,atrim=end=<TOTAL>. Sin base de catálogo (HeyGen busca 500 s o sin inicio de sesión):EL=<key> python <skill>/scripts/el-music.py .media/candidates/bed.mp3 <ms> "<prompt: instrumental, BPM, se sienta debajo de un narrador, sin caídas, termina en un acorde resuelto>"- una pista que se desvanece por sí sola en la duración del vídeo no necesita empalme. - Ruido de verificación de costura: el presentador se mueve a través de cada costura, por lo que
seam-check.pyFALLA solo por eso. Evalúa cada par marcado conscripts/seam-diff.py a.png b.png out.png x0,y0,x1,y1(enmascara la caja del presentador) - 0.00% fuera de ella es un pase. - Ley de tamaño vertical (1080x1920): la banda superior de cada escena lleva un titular real (60-84 px), texto del cuerpo >= 40 px, los objetos usan el ancho completo y la altura por encima del presentador. La primera oleada envió ~33 px de texto en el 45% superior del fotograma; pon estos números en frame.md antes del envío, no después.
- No es un bucle: un vídeo narrado termina en un bloqueo mantenido; ignora la línea de bucle de seam-check.
Reglas estrictas (las que lo hacen premium y suave)
- Verdad desde la fuente. La interfaz de producto se porta del HTML/CSS del propio producto; los lienzos de terceros se reconstruyen a partir de las capturas reales del usuario, nunca de memoria. Solo datos ficticios. Solo funciones que el producto realmente tiene.
- Una cámara. Perspectiva 1866 mm (35 mm) en cada escena; una capa de deriva debajo de la capa de cámara para que nada esté nunca quieto; paralaje + desenfoque DOF en hojas lejanas. Sin opacidad/filtro/desbordamiento/clip-path en un padre 3D (lo aplana).
- Nítido a 1080p+. Escala UI con CSS
zoom(.kit-zoom), nuncatransform: scale(>1); crea cada pieza a su tamaño en pantalla más cercano; las capturas son PNG 2x mostrados a resolución nativa o inferior. - Ley de suavidad a 60 fps. Solo transform/opacidad; resortes para aterrizajes, expo/power3 para llegadas, inOut para tramos de cámara; sin lineal, pasos, snap ni redondeo; los tramos de cámara se superponen 0.15-0.3 s y duran 0.6 s+.
- Determinista. Una línea de tiempo pausada por escena registrada por última vez en
window.__timelines[id],fromTocon inicios explícitos, sin random/reloj/transiciones CSS. - Las costuras son fotogramas compartidos. Cada costura es una imagen exacta que ambos vecinos mantienen ~0.1 s, bloqueada por una placa 2D fuera del soporte. Sin corte duro, sin fotograma vacío, sin fundido a nada. Último fotograma = primer fotograma.
- En la cuadrícula. Las escenas son compases enteros/medios; clics, ticks, sellos e intercambios caen en tiempos; música a su tempo nativo cortada en líneas de compás.
- Correcciones de contrato, no de escena. Rutas
assets/...relativas a la raíz, ids que nunca comienzan con un dígito, ids y clases prefijados con el id de la escena. Una regla que toda escena rompería se corrige una vez en frame.md / worker-brief. - Prohibiciones. Sin emoji, resplandor neón, degradado púrpura a azul, tarjetas con borde izquierdo coloreado, spinners circulares (brillo), personas stock, "Juan Pérez", ritmo de presentación de diapositivas o movimiento que no dice nada.
- Finales desde el CLI con GPU de hardware. Nunca Exportación de Studio (ruta de software, ~15 veces más lento).
Cuando algo sale mal
| Síntoma | Causa | Corrección |
|---|---|---|
| Lint: error de ruta de asset | ../assets/ en una subcomposición | ruta relativa a la raíz assets/... (corregir en el contrato) |
| Lint: id de composición inválido | id comienza con un dígito | s01-click, nunca 01-click (film.py lo rechaza) |
| El constructor de paquetes lanza / paquete > 48 KB | dos id de plano, o demasiados rules: | un id (Adapt); 2-3 reglas por bloque |
| UI se ve suave | escalado con transform o empujado hacia la cámara más allá del tamaño creado | .kit-zoom, crear al tamaño más cercano |
| 3D de repente plano | opacidad/filtro/desbordamiento en un ancestro 3D | moverlo a la hoja |
| Costura salta | un lado no mantiene el fotograma compartido | seam-check.py, corregir la placa/pose del lado que se desvía |
| Costura FALLA un pequeño % pero los fotogramas se ven idénticos | una copia del soporte debajo de la placa duplica su sombra | solo la placa se muestra mientras la sostiene (seam en frame.md) |
| UI del producto se desvía de la aplicación real | reconstruida de memoria o CSS parcial | capturar la aplicación real a 2x (primera sección de surfaces.md) |
800+ advertencias duplicate_audio_track | SFX sin data-duration | volver a ejecutar assemble.py |
music-search dice sin credencial | no has iniciado sesión | el usuario ejecuta npx hyperframes auth login en su terminal |
| El renderizado va a ~1 fotograma/s | GPU de software (exportación Studio) | render-final.mjs (CLI, --browser-gpu --workers 6) |
| El renderizado sigue ejecutándose después de parar | detener el shell no detiene node | matar el árbol de procesos (verify-ship.md) |
| Sin progreso de renderizado visible | salida canalizada a través de ` | tail` |
Autocomprobación
node <skill>/scripts/selftest.mjs
Crea un vídeo de 2 escenas en una carpeta temporal, luego ejecuta el andamiaje, montaje, check, verificación de costura, un renderizado con GPU de hardware
(1920x1080 a 60 fps) y la codificación web. Debe imprimir PASS; ejecútalo en una máquina nueva y después de cualquier edición.