Você dá um assunto. A skill escreve o roteiro, grava a locução, anima as cenas e renderiza os MP4 em 16:9 e 9:16 — tudo na sua máquina.

Não é um gerador de vídeo por IA. É um pipeline determinístico: HTML animado + Chrome headless + FFmpeg, orquestrado pelo HyperFrames, com narração TTS local. O resultado é sempre PT-BR, dark premium âmbar, e termina na CTA do INEMA.CLUB.
Chrome headless + FFmpeg renderizam o vídeo; a narração sai do inemavox (voz bella) com Kokoro pf_dora como fallback. Nada sai da máquina.
O gerador lê o array SCENES[]: uma entrada por beat do roteiro. Range saudável 4–12 cenas de conteúdo — o assunto decide, não um template travado.
O mesmo gerador escreve os dois formatos (--vertical para Shorts/Reels), com safe zones da UI dos apps e título persistente de 2 linhas no vertical.
O timing é fonte única: cada cena declara a duração REAL do seu WAV (medida com ffprobe), e o gerador deriva dali tanto os data-start/duration quanto os tempos dos tweens — áudio e animação nunca desencontram.
Roteiro com gancho na cena 1 e revisão em duas formas por frase: tela (PT-BR acentuado, inglês na grafia original) e fala (siglas expandidas, inglês fonético — deploy → "deplói").
Projeto criado em ~/projetos/output/<nome>/, fontes baixadas como .woff2 local, e um WAV por cena gerado pelo narration.sh.
Composição a partir do vocabulário de movimento M.*, lint + inspect de layout, render em draft para conferir e high para entregar.
Tudo roda local. Se algo falhar, npx hyperframes doctor aponta o que está faltando.
FFmpeg em C:\ffmpeg\bin. No git-bash use sempre -nostdin, senão o comando sai com exit 0 sem gerar arquivo.
# confira a versão node -v ffmpeg -nostdin -version
O HyperFrames renderiza o HTML num Chrome headless próprio — baixe-o uma vez.
# baixa o browser do HyperFrames npx hyperframes browser ensure # diagnóstico geral npx hyperframes doctor
Default da casa — pede GPU. python3 do sistema roda o tts_direct.py; o env conda chatterbox é chamado internamente para a transferência de timbre.
# fallback opcional (Kokoro pf_dora) pip install kokoro-onnx soundfile
O render (Chrome headless + FFmpeg) nunca precisou de GPU. Só duas peças do fluxo default puxam hardware extra — e as duas têm substituto direto.
Sintetiza na nuvem da Microsoft, sem chave e sem modelo local. Liste e escute as vozes antes de gerar o vídeo inteiro — trocar de voz no meio é retrabalho.
# instalar e ver as vozes PT-BR pip install edge-tts edge-tts --list-voices | grep pt-BR # amostra de 1 frase, para escutar edge-tts --voice pt-BR-FranciscaNeural \ --text "Teste de voz." --write-media amostra.mp3
Modelo ONNX pequeno, roda em CPU, sem internet. Vozes PT: pf_dora (F), pm_alex e pm_santa (M). Mesma regra: gere uma frase com cada candidata e escolha ouvindo.
pip install kokoro-onnx soundfile # o timing do vídeo vem do áudio real ffprobe -v error -show_entries format=duration \ -of csv=p=0 assets/audio/s1.wav
No lugar do flux2-klein (difusão local, GPU): o CLI imagens-agnes chama a API da Agnes — US$ 0, sem créditos, nada roda na sua máquina. Prompt em inglês (PT apanha do filtro), no máx. 2 referências, e baixe na hora (a URL expira).
cd ~/projetos/imagens-agnes python3 gerar.py "dark premium abstract \ data landscape, amber accent" \ --ratio 16:9 --size 2K -o s3.png
Sem Agnes e sem GPU, o fallback SVG continua valendo: o house-style já é vetorial/CSS, imagem é reforço e não requisito. Detalhes completos em references/sem-gpu.md.
Os comandos abaixo são os reais da skill. Todo o conteúdo — projeto, assets, áudios, index.html e os MP4 finais — vive numa pasta só: ~/projetos/output/<nome>/.
Um beat por cena, 1–3 frases curtas (~8–15s de voz cada). A cena 1 abre direto no gancho — pergunta afiada, número que choca, promessa concreta ou erro comum. Sem logo, sem "olá pessoal": os ~3s iniciais decidem a retenção. Default de duração quando ninguém pede nada: ~1:40–2:00.
# arco de referência (funda ou desdobre beats conforme o tema) # hook → primeiro princípio → mecânica → conceito-chave → # aplicação → avançado → exemplo real → fecho → CTA INEMA.CLUB
Cada frase tem duas formas. Tela (caption + literais em html(p)): PT-BR com acentuação varrida palavra a palavra, termos em inglês na grafia original. Fala (txt/sN.txt): siglas e URLs expandidas, inglês reescrito foneticamente — o TTS fonemiza pela grafia escrita.
# tela → Toda skill começa com o SKILL.md # fala → Toda skiu começa com o SKILL ponto M D # léxico: deploy→"deplói" · design→"dizáin" · framework→"frêimuork"
Init do HyperFrames com o exemplo blank (a skill traz o house style próprio). Copie o design.md da referência de house style para a raiz do projeto.
cd ~/projetos/output npx hyperframes init <nome> --example blank --non-interactive
Sora, Inter e JetBrains Mono como .woff2 (subset latin) + fonts.css. Nunca use Google Fonts via CDN: some no render.
# copie scripts/fetch-fonts.mjs para o projeto e rode node fetch-fonts.mjs # → assets/fonts/*.woff2 + fonts.css
Copie scripts/narration-template.sh como assets/narration.sh, escreva os txt/sN.txt na forma-fala e rode. Ele itera sobre todos os sN.txt que existirem, tenta a bella via inemavox e cai no Kokoro por cena se falhar.
bash assets/narration.sh # → assets/audio/sN.wav # meça a duração REAL de cada WAV (vai para o campo `audio` da cena) ffprobe -v error -show_entries format=duration \ -of default=noprint_wrappers=1:nokey=1 assets/audio/s1.wav
Copie scripts/composition-template.mjs como build-index.mjs e edite o array SCENES[] — uma entrada por cena, cada uma { audio, caption, html(p), anim(at,p) }. A CTA do INEMA.CLUB já vem anexada como última cena. No 9:16, defina TITLE com persona + gancho.
const TITLE = { l1: "PROFISSIONAL <b>LIBERAL</b>", l2: "por que ainda faz tudo <b>sozinho?</b>" }; // a CTA é sempre a última — não remover const ALL = [...SCENES, CTA];
O lint precisa dar 0 erros e o inspect 0 problemas de layout. Não deixe dois index*.html na raiz — o lint acusa multiple_root_compositions.
npx hyperframes lint # 0 erros npx hyperframes inspect --samples 16 # 0 problemas de layout
Renderize logo após gerar cada modo — o gerador sempre escreve index.html. Um arquivo por formato, e só: o render já é a entrega, sem cópia comprimida ao lado. Confira frames em draft antes do high.
node build-index.mjs && npx hyperframes render --quality high --output <nome>-16x9.mp4 node build-index.mjs --vertical && npx hyperframes render --quality high --output <nome>-9x16.mp4 # extrair um frame para conferência ffmpeg -nostdin -y -ss 12 -i <nome>-16x9.mp4 -vframes 1 -update 1 frame.png
Casos reais que validaram cada recurso do pipeline — e o curso publicado sobre a própria skill.
O exemplo que acompanha o narration-template.sh: 8 cenas + CTA explicando o que são Skills, do SKILL.md à divulgação progressiva. O curso completo sobre esta skill está publicado em skill-video-explicativo.
Caso que validou o título persistente de 2 linhas no 9:16 (v1.10.3): l1 = "PROFISSIONAL LIBERAL" (persona), l2 = "por que ainda faz tudo sozinho?" (gancho). Fixo no topo o vídeo todo, sumindo na CTA.
Origem das safe zones do 9:16 (v1.5.0/1.5.1): mensagem no meio, mídia como faixa de topo entrando da direita, caption oculta no vertical — base livre para a UI do app.
Variação é outro ângulo do mesmo assunto — didático vs. caso real vs. contrarian vs. lista — com roteiro, gancho e narração próprios. Cada uma gera os dois formatos: <nome>-v1-16x9.mp4, <nome>-v2-16x9.mp4…
Versionamento v1.yy.xxx — yy = recurso, xxx = correção. Histórico completo no CHANGELOG.md.
SCENES[]), CTA anexada automaticamente e câmera Ken Burns em toda cena — fim do slideshow.M.* (reveal/sweep/type/float/pulse/glow/countUp) no lugar de tweens improvisados, com deslocamentos menores no 9:16.TRANS em GSAP: fade (default), push, slideUp, zoom, wipe, fadeBlack — especiais só em 2–3 momentos-chave.-FINAL.MUSIC_VOL ~0.14), scrim/blur/painel sob texto, e o que é uma variação.chatterbox-vc); Kokoro pf_dora vira fallback automático por cena.TITLE = { l1, l2 } — persona + curiosidade, fixo no topo do vertical e sumindo na CTA, fechando o loop de retenção.edge-tts ou Kokoro (com validação de voz obrigatória) e imagens pela Agnes AI (US$ 0) no lugar do flux2-klein.