🧩 build-index.mjs — o gerador do projeto
Cada vídeo começa copiando scripts/composition-template.mjs como build-index.mjs na raiz do projeto. Esse arquivo é o gerador: roda uma vez, escreve index.html, e você renderiza.
O template já entrega CSS dark premium, background persistente (glow/grid/grain), caption layer, barra de progresso e overrides para 9:16. Você só precisa preencher 4 coisas: AUDIO[], CAPTIONS[], sceneN() e anim().
Rode node build-index.mjs para 16:9 e node build-index.mjs --vertical para 9:16. Os dois escrevem index.html — renderize logo após cada geração.
- ✓ Copie o template inteiro — não crie do zero
- ✓ Mantenha a cena 9 (CTA INEMA.CLUB) inalterada
- ✓ Renderize imediatamente após cada
node build-index.mjs - ✓ Use
writeFileSyncparaindex.html— o template já faz isso
- ✗ Não edite
index.htmldiretamente — ele será sobrescrito - ✗ Não use Google Fonts CDN — some no render headless
- ✗ Não remova a cena 9 de CTA — é a assinatura padrão
- ✗ Não misture
--verticale 16:9 sem re-rodar o gerador
--bg:#0D1321, variáveis de cor, fontes locais, overrides para 9:16.data-layout-ignore.🔢 AUDIO[] — durações REAIS medidas por ffprobe
O array AUDIO[] é a única entrada de dados de timing de todo o projeto. Cada elemento é a duração em segundos do WAV de narração da cena correspondente, incluindo a cena 9 de CTA.
A duração de um WAV gerado pelo Kokoro varia conforme o texto, a velocidade (--speed 0.98) e a fonética. Se você estimou errado, áudio e visual ficam dessincronizados para sempre. Meça sempre.
O comando ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 assets/audio/sN.wav imprime o número exato em segundos — cole direto no array.
Use for i in 1 2 3 4 5 6 7 8 9; do echo "s$i: $(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 assets/audio/s$i.wav)"; done para gerar a lista inteira de uma vez e colar no AUDIO[].
🎨 sceneN() — HTML de cada cena
Cada cena é uma função JavaScript que retorna uma string HTML. O conteúdo é inserido em <div class="scene-inner">, dentro de um <section class="scene clip"> com atributos de timing calculados a partir de AUDIO[].
A montagem transforma cada item de S (array derivado de AUDIO[]) em um <section> com três atributos críticos: data-start, data-duration e data-track-index. Tracks alternados (1/3) evitam sobreposição nas bordas de cena.
- ✓ Reutilize as classes CSS do template (
.kicker,.h2,.grid2) - ✓ Dê
idúnico a cada elemento animável (s1-word, etc.) - ✓ Decorativos off-canvas: use
data-layout-ignore - ✓ Animar o
.scene-inner, nunca o.clipwrapper
- ✗ IDs duplicados entre cenas — o GSAP vai animar a errada
- ✗ Animações no
.clip— o framework forçaopacity:1no clip ativo - ✗ Reordenar BODIES sem ajustar os IDs correspondentes
- ✗ Remover ou reposicionar
scene9(CTA fixa no fim)
✨ anim(i,t) — tweens GSAP por cena
A função anim(i, t) recebe o índice da cena e o instante de início absoluto. Ela empurra strings de código GSAP para um array local que depois é concatenado no <script> do HTML gerado.
O gerador roda em Node.js mas o GSAP executa no browser (Chrome headless). anim() constrói strings de código que serão embutidas no HTML — quando o browser carregar, essas strings já têm os tempos absolutos calculados a partir de AUDIO[].
A helper at(d) (atalho para round(t + d)) calcula offset relativo ao início da cena. Use ela em todos os tweens para manter o código legível.
1. Animar sempre o .scene-inner, nunca o .clip wrapper. 2. Usar tempos absolutos calculados por at(d) — jamais hardcodar números. 3. O FADE=0.45 é o mesmo para entrada e saída de todas as cenas, garantindo transição uniforme.
Cenas ímpares ficam em data-track-index="1" e pares em data-track-index="3". Captions seguem o mesmo padrão (2/4). Isso evita o "overlap de borda" onde dois frames de cenas adjacentes aparecem simultaneamente — erro clássico descrito em references/gotchas.md.
💬 CAPTIONS[] — uma legenda curta por cena
O array CAPTIONS[] tem exatamente o mesmo comprimento que AUDIO[]. Cada string é exibida na parte inferior da cena correspondente como uma legenda acessível, sincronizada via data-start/duration herdados do mesmo S[idx].
Uma boa caption captura a ideia central da cena em 5–10 palavras — não transcreve o áudio. O espectador que assiste sem som deve entender o ponto de cada cena só pela legenda + visual.
A caption roda em data-track-index="2" (cenas ímpares) ou "4" (pares) — tracks alternados para evitar sobreposição com a cena anterior na borda de transição.
- ✓ Frase curta que resume o conceito central
- ✓ Inclui termos técnicos exatos (nomes de arquivo, comandos)
- ✓ Legível para quem assiste sem áudio
- ✓ A caption da s9 menciona o site:
inema.club
- ✗ Transcrição literal do áudio (longo demais)
- ✗ Vagos: "uma ideia importante sobre o tema"
- ✗ Caps inteira — dificulta leitura rápida
- ✗ Comprimento diferente de AUDIO[] (vai causar erro)
No YouTube, as captions são indexadas pelo buscador. Captions precisas com termos técnicos corretos (ffprobe, GSAP, build-index.mjs) melhoram o ranqueamento do vídeo para buscas técnicas.
⏱️ Timing fonte-única — áudio e animação sempre batidos
O princípio mais importante do composition-template: AUDIO[] gera tudo — data-start, data-duration, os tempos dos tweens GSAP e os atributos do <audio>. Nenhum número é duplicado nem hardcoded em outro lugar.
As três constantes LEAD=0.5, TAIL=0.9 e FADE=0.45 combinadas com os valores em AUDIO[] determinam o tempo de início, duração e fade de cada cena — e portanto o tempo exato de todos os tweens GSAP. Alterar um valor em AUDIO[] propaga automaticamente para tudo.
Fórmulas: dur = LEAD + audioDur + TAIL · audioStart = start + LEAD · Tween de entrada = t · Tween de saída = end - FADE.
- ✓ Mude um WAV → rode o gerador → tudo sincroniza
- ✓ Adicione uma cena → adicione entry em AUDIO[], CAPTIONS[], sceneN(), anim() case N
- ✓ Nenhum número hardcoded fora de AUDIO[]/LEAD/TAIL/FADE
- ✓
npx hyperframes lintdetecta dessincronias de track
- ✗ Hardcodar tempo em tween:
gsap.to(..., 14.5) - ✗ Ajustar
data-startno HTML gerado — será sobrescrito - ✗ Arrays AUDIO[] e CAPTIONS[] de comprimentos diferentes
- ✗ Usar
round()inconsistente → offset de frames
📋 Resumo do Módulo 2.4
- ✓ Copiar o template como
build-index.mjse rodar comnode - ✓ Preencher
AUDIO[]com durações reais do ffprobe (inclui CTA s9) - ✓ Escrever
sceneN()reutilizando as classes CSS do template - ✓ Codificar tweens GSAP em
anim(i,t)animando sempre o.scene-inner - ✓ Preencher
CAPTIONS[]com frases curtas e técnicas - ✓ Entender as fórmulas de timing:
LEAD=0.5,TAIL=0.9,FADE=0.45
npx hyperframes lint para 0 erros, inspect --samples 16 para 0 problemas de layout, e gere o MP4 final em --quality high para 16:9 e 9:16.