Spatial Film
Comprovado do início ao fim em um filme de produto de 60s (7 cenas, 5 workers em uma única onda, todas as emendas exatas no quadro, verificação limpa,
1080p60 em 4m19s). O film.json do template contém as barras de cena e a folha de pauta reais desse filme como exemplo trabalhado.
<skill> = a pasta desta skill (o diretório onde este SKILL.md está). Execute todos os scripts a partir da raiz do projeto de filme.
python abaixo = o comando Python salvo pela verificação de configuração do explainer-kit em ~/.talking-explainer/config.json
(py -3.12 no Windows, python3 no macOS).
O que você precisa
- Node 20+,
ffmpeg+ffprobeno PATH, Python 3.10+ comnumpyePillow. - As skills HyperFrames:
npx hyperframes skills update general-video hyperframes-animation media-use(construtor de pacotes, blueprints + mapa de animação, auxiliar de música). O CLI em si é fixado por projeto e executado vianpx. - Playwright para pré-renderização de superfícies:
npm i -D playwrightna pasta do filme (ou definaPLAYWRIGHT_PATH). - Opcional, para música com fonte: uma conta HeyGen – execute
npx hyperframes auth loginuma vez no seu terminal. Sem ela, traga sua própria faixa e comece a fase 4 emmusic-analyze.py. - Uma máquina com GPU para a renderização final (ela ainda renderiza pelo caminho de software, cerca de 15x mais lento).
Como usar esta skill sem desperdiçar contexto
- Carregue UMA referência de fase por vez, quando a fase começar. Nunca pré-carregue todas.
- Scripts são o código comprovado. Execute-os; nunca regenere a lógica deles inline.
- A sessão principal nunca lê um arquivo de cena inteiro (20-60KB). Avalie as cenas pelas folhas de contato; corrija com
grep -ne um patch exato. - Workers recebem CAMINHOS de arquivo, não conteúdo colado.
storyboard.html(60KB+) é vasculhado por ID de célula, nunca lido inteiro. - Leia uma folha de contato primeiro; abra PNGs individuais apenas para verificar um detalhe.
Fases (cada uma termina em um portão – não avance até que ele passe)
| # | Fase | Carregar | Portão |
|---|---|---|---|
| 0 | Scaffold: node <skill>/scripts/new-film.mjs <dir> --name <slug> | - | pasta tem film.json, frame.md, kit, docs do template |
| 1 | Intake, BRIEF, STORYBOARD, folha de esboço | references/storyboard.md | "vai" explícito do usuário registrado em ## Bloqueado |
| 2 | frame.md, kit, superfícies pré-renderizadas | references/surfaces.md (+ platform-canvases.md apenas se GHL/n8n/Zapier aparecerem) | sem {{ no frame.md; todos os PNGs de captura verificados |
| 3 | Teste de harness, pacotes, workers paralelos | references/workers.md | cada cena: lint 0 erros, folha de contato corresponde ao seu bloco |
| 4 | Música + SFX (roda enquanto os workers constroem) | references/audio.md | base no BPM nativo, barra 0 no tempo forte; pautas no film.json |
| 5 | Montar, verificar, emendas, renderizar, enviar | references/verify-ship.md | verificação passa, seam-check todos PASS, renderização com hardware-gpu verificada |
Roteie por /hyperframes -> /general-video para a intenção e as etapas de revisão de esboço (esta skill fornece
o formato do projeto, contrato, scripts e portões; general-video fornece o construtor de pacotes e a função de worker).
Arquivos
| Caminho | O quê |
|---|---|
template/ | Esqueleto do projeto: film.json (barras de cena, BPM, bgm, SFX, pautas – a ÚNICA fonte de temporização), frame.md (contrato de verdade do design com preenchimentos), BRIEF.md, STORYBOARD.md, assets/kit.css (kit base do filme), assets/fonts/ (Poppins 500/700 + OFL), references/worker-brief.md, references/scene-starter.html (esqueleto de cena limpo de lint), references/snippets.html, package.json (hyperframes 0.8.117 fixado). |
surfaces/ | Reconstruções HTML prontas dos canvases GHL, n8n e Zapier + uma longa página de marketing (dados fictícios da Northwind Dental), platform.css, sprite.svg, jobs.json. |
scripts/new-film.mjs | Scaffold de um projeto a partir do template. |
scripts/capture-surfaces.cjs | Renderiza src/captures/*.html -> assets/captures/*.png em 2x (jobs.json). |
scripts/measure-surface.cjs | Caixas 1x de elementos em uma superfície, para alvos exatos de anotação/corte. |
scripts/make-harness.py, scripts/test-scene.mjs | Projetos de teste isolados por cena; lint + snapshot de uma cena. |
scripts/music-search.mjs, sfx-search.mjs | Catálogo HeyGen via auxiliar REST do media-use (seguro para Windows). |
scripts/music-analyze.py, music-bars.py | Tempo apenas com numpy + energia por barra (sem necessidade de librosa). |
scripts/bgm-cut.py | Corta a base nas linhas de barra, emenda o próprio final da faixa, fades; --b repete para fazer loop de uma base sob um filme mais longo. |
scripts/assemble.py | film.json -> index.html (slots de cena na grade de barras, bgm, SFX com durações + pistas). |
scripts/seam-check.py | Comprova pixel a pixel cada emenda (antes / no / após o corte) e o loop (quadro 0 vs último). |
scripts/render-final.mjs | Renderização via CLI com --browser-gpu, registrada em arquivo, modo gpu + ffprobe verificados, depois flash-scan.py. |
scripts/flash-scan.py | Cada quadro de uma renderização: um quadro diferente de ambos os vizinhos (um elemento mostrado por 1/60s) = um bug. |
scripts/encode-web.mjs | Codificação web (H.264 CRF 21, faststart, AAC ou --silent) + quadro de poster. |
scripts/blink-fit.py, scripts/build-presenter.py | Filmes narrados: sobreposições de piscar para poses de folha (--band = faixa dos olhos); a sobreposição do apresentador a partir de sua faixa, com sincronia labial quando o mascote tem mouth-fit.json. |
scripts/el-tts.py, el-words.py, el-music.py | Filmes narrados via ElevenLabs (chave na env EL, nunca em disco): candidatos de voz a partir de SCRIPT.md, temporizações de palavras do Scribe, uma base gerada. |
scripts/seam-diff.py | Diff de emenda com a caixa do apresentador mascarada (filmes narrados). |
scripts/fromto-scan.py | Pré-renderização: sinaliza propriedades fromTo ONLY-FROM que workers de renderização (buscas no meio da cena) nunca aplicam. 0 acertos antes de cada renderização. |
scripts/patch_exact.py | Substituição exata, segura para CRLF, para correções de cena roteirizadas (afirma uma correspondência cada). |
scripts/selftest.mjs | Prova de ponta a ponta de que a skill funciona nesta máquina. Deve imprimir PASS. |
Filmes narrados (voz over + apresentador na tela)
Comprovado em um explainer narrado "API vs Webhook" (83s, vertical e paisagem, voz clonada, mascote apresentador, 10 cenas por 5 workers). As regras de filme musical acima ainda valem, exceto onde estas as substituem:
- Candidatos de voz primeiro.
EL=<key> python <skill>/scripts/el-tts.py . eric=<voice_id> michelle=<voice_id>lê as linhas indentadas deSCRIPT.mde escreve um MP3 por voz emsrc/audio/voice-candidates/(chave apenas da env, nunca em disco). Deixe o usuário escolher de ouvido, depois transcreva apenas a escolha. - Narração é o relógio. Tempos das palavras:
EL=<key> python <skill>/scripts/el-words.py <voice.mp3> src/timing/voice-words.json(ElevenLabs Scribe – sem modelo local, precisa de chave), ou faster-whisper comword_timestamps(idioma definido, VAD ativado;transcribe.pydo explainer-kit, gratuito e local). Mantenha o JSON de palavras emsrc/timing/. Emfilm.jsondefina"bpm": 60, "beats_per_bar": 1para que uma "barra" seja 1s e os comprimentos das cenas sejam segundos simples cortados nas pausas das frases. Os blocos do storyboard fornecem tempos de palavras locais às cenas; as chegadas começam ~0,1s antes e pousam na palavra. Dê a um título pelo menos ~1s na tela antes do próximo corte (verifique o corte em relação à ÚLTIMA palavra da linha que ele mostra). - Extras do
film.json(assemble.py + make-harness.py os leem):"audio": [{id, src, dur, volume}]para a voz,"overlays": [{id, track, z}]para uma camada de filme inteiro (o apresentador) que fica acima de cada cena, para que nunca apareça em uma emenda e as cenas permaneçam livres de apresentador. - Apresentador a partir de PNGs de pose: um PNG transparente por pose (
prep-mascot.pydo explainer-kit corta, escala e escreve o manifesto), depoisscripts/blink-fit.py <mascot_dir>(opcional, precisa de torch; ajusta a faixa BLINK dos olhos em cada pose por NCC; poses abaixo de 0,70 não recebem piscada) escripts/build-presenter.py(escreve a sobreposição a partir desrc/presenter-track.json: poses em palavras de pauta, ancoradas na cabeça para que as trocas nunca saltem, balanço de envelope de voz, respiração, piscadas, fx). Nunca edite manualmente sua saída. - Áudio: loudnorm na voz para -16 LUFS / -1,5 dBTP (um ganho simples cortado em +4,6 dBTP), duck do lado da
base sob ela offline (~17 dB abaixo da voz), SFX baixo (0,1-0,45). Preencha a chave da voz ou o duck trunca a
base para o comprimento da voz (verifique se avisou "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>. Sem base de catálogo (pesquisa HeyGen 500s ou sem login):EL=<key> python <skill>/scripts/el-music.py .media/candidates/bed.mp3 <ms> "<prompt: instrumental, BPM, fica sob um narrador, sem quedas, termina em um acorde resolvido>"- uma faixa que termina sozinha no comprimento do filme não precisa de emenda. - Ruído na verificação de emenda: o apresentador se move através de cada emenda, então
seam-check.pyFALHA apenas por causa dele. Julgue cada par sinalizado comscripts/seam-diff.py a.png b.png out.png x0,y0,x1,y1(mascara a caixa do apresentador) - 0,00% fora dela é uma aprovação. - Lei de tamanho vertical (1080x1920): a faixa superior de cada cena carrega um título real (60-84px), texto do corpo >= 40px, objetos usam a largura total e a altura acima do apresentador. A primeira onda enviou texto de ~33px no topo 45% do quadro – coloque esses números no frame.md antes do despacho, não depois.
- Não é um loop: um filme narrado termina em um lockup mantido; ignore a linha de loop do seam-check.
Regras rígidas (as que o tornam premium e suave)
- Verdade da fonte. A UI do produto é portada do próprio HTML/CSS do produto; os canvases de terceiros são reconstruídos a partir das capturas reais do usuário, nunca da memória. Apenas dados fictícios. Apenas recursos que o produto realmente possui.
- Uma câmera. Perspectiva 1866px (35mm) em cada cena; uma camada de deriva sob a camada da câmera para que nada nunca fique parado; paralaxe + desfoque DOF em folhas distantes. Sem opacidade/filtro/overflow/clip-path em um parente 3D (achata).
- Nítido em 1080p+. Escale a UI com CSS
zoom(.kit-zoom), nuncatransform: scale(>1); crie cada peça em seu tamanho de tela mais próximo; capturas são PNGs 2x mostrados em ou abaixo do nativo. - Lei de suavidade de 60fps. Apenas transform/opacity; molas para aterrissagens, expo/power3 para chegadas, inOut para pernas de câmera; sem linear, steps, snap ou arredondamento; pernas de câmera se sobrepõem 0,15-0,3s e duram 0,6s+.
- Determinístico. Uma linha do tempo pausada por cena registrada por último em
window.__timelines[id],fromTocom inícios explícitos, sem random/clock/CSS transitions. - Emendas são quadros compartilhados. Cada emenda é uma imagem exata que ambos os vizinhos seguram ~0,1s, travada por uma placa 2D fora do rig. Sem corte seco, sem quadro vazio, sem fade para nada. Último quadro = primeiro quadro.
- Na grade. As cenas são barras inteiras/meio; cliques, ticks, carimbos e trocas caem nos tempos fortes; música em seu andamento nativo cortada nas linhas de barra.
- Correções de contrato, não correções de cena. Caminhos
assets/...relativos à raiz, IDs nunca começando com dígito, IDs e classes prefixados com o ID da cena. Uma regra que toda cena quebraria é corrigida uma vez no frame.md / worker-brief. - Proibições. Sem emoji, brilho neon, gradiente roxo-para-azul, cartões com borda esquerda colorida, spinners circulares (shimmer), pessoas de banco de imagens, "João da Silva", ritmo de slideshow ou movimento que não diz nada.
- Finais a partir do CLI com GPU de hardware. Nunca a Exportação do Studio (caminho de software, ~15x mais lento).
Quando algo dá errado
| Sintoma | Causa | Correção |
|---|---|---|
| Lint: erro de caminho de asset | ../assets/ em uma sub-composição | relativo à raiz assets/... (corrigir no contrato) |
| Lint: ID de composição inválido | ID começa com dígito | s01-click, nunca 01-click (film.py recusa) |
| Construtor de pacotes lança / pacote > 48KB | dois IDs de blueprint, ou muitas rules: | um id (Adapt); 2-3 regras por bloco |
| UI parece suave | escalado por transform ou empurrado em direção à câmera além do tamanho de criação | .kit-zoom, criar no tamanho mais próximo |
| 3D de repente plano | opacidade/filtro/overflow em um ancestral 3D | mover para a folha |
| Emenda aparece | um lado não segurando o quadro compartilhado | seam-check.py, corrigir a placa/pose do lado que deriva |
| Seam FAIL alguns % mas os quadros parecem idênticos | uma cópia do rig sob a placa dobra sua sombra | apenas a placa aparece enquanto segura (emendas do frame.md) |
| UI do produto deriva do aplicativo real | reconstruída da memória ou CSS parcial | capturar o aplicativo real em 2x (primeira seção do surfaces.md) |
800+ avisos duplicate_audio_track | SFX sem data-duration | executar assemble.py novamente |
music-search diz sem credencial | não logado | usuário executa npx hyperframes auth login no terminal deles |
| Renderização rasteja ~1 quadro/s | GPU de software (exportação do Studio) | render-final.mjs (CLI, --browser-gpu --workers 6) |
| Renderização continua rodando após parada | parar o shell não para o node | matar a árvore de processos (verify-ship.md) |
| Nenhum progresso de renderização visível | saída pipeada através de ` | tail` |
Autoteste
node <skill>/scripts/selftest.mjs
Cria um filme de 2 cenas em uma pasta temporária, então executa o harness, assemble, check, seam-check, uma renderização
com GPU de hardware (1920x1080 a 60 fps) e a codificação web. Deve imprimir PASS – execute-o em uma máquina nova e após qualquer edição.