film-engine: filmes desenhados a partir de código, um quadro de cada vez
Onde o kit vive: execute cada comando desta skill a partir da pasta
reels-maker(o clone de github.com/thativanng/reels-maker que contémtemplate/etools/). Se o projeto não tiver nenhuma, configure-o primeiro seguindo o SETUP.md desse repositório.
A ideia em um parágrafo
Um filme aqui é uma única página HTML com uma regra: window.seek(t) deve desenhar exatamente o quadro para o instante t, sempre, a partir apenas de t. Nada na imagem pode ler um relógio real. Um renderizador então avança t a 30 FPS, captura cada quadro em 2x e o envia para o ffmpeg. Como a página é determinística, uma re-renderização corresponde (em média, dentro de uma fração de nível; ruído de GPU pode mover pixels individuais em até cerca de 60 níveis), um still em 17,3 s é o quadro que o vídeo mostrará, e cada verificação (film-audit) pode examinar qualquer momento sem reproduzir o filme.
A rota (não pule uma estação)
- Brief e look primeiro. reel-intake termina em um brief assinado e storyboard (formato, duração, som, o que nunca aparece); reel-style termina em um look escolhido. Não abra as etapas de construção desta skill sem ambos.
- Storyboard. reel-intake o escreve (tempo · palavras na tela · som e legenda · porquê; references/brief.md), e a pessoa o assina antes de você construir. Um estilo com banner adiciona uma coluna de banner.
- Test strip. Construa cerca dos primeiros 10 s no look escolhido (um filme em estilo walk pode precisar de 15–30 s para mostrar um capítulo), tire stills, obtenha um sim. Uma construção completa em um look não assinado é trabalho desperdiçado.
- Construa o filme completo (o pacote de estilo diz como construir cenas).
- Gates (film-audit): a verificação de colisão a 40 FPS, as sondas de pixel, depois um auditor independente. Nada vai para a pessoa antes disso.
- Renderize:
node tools/render.mjs --page reels/<name>/page.html --out reels/<name>/out/film-silent.mp4(quadros 2x, redimensionados com lanczos, sem tags de cor). - Áudio:
python3 template/audio.py out/film.wav(uma lista de cues no mesmo relógio do filme). - Finalize:
zsh tools/mux.sh out/film-silent.mp4 out/film.wav out/film.mp4. Isso resulta em −14 LUFS, um limitador, um arquivo H.264 sem tags e uma pré-visualização para celular no mesmo formato do filme (lado curto 720 px). - Capa:
node tools/cover.mjs --page reels/<name>/page.html --out reels/<name>/out/cover.jpg. Uma página com modo de capa (o?coverdo starter, a partir da configcover) desenha o still projetado; qualquer outra página fornece o quadro emT=. É salvo no formato do próprio filme e mantido abaixo de 2 MB, mais o recorte central 3:4 para um filme 9:16 (a grade do Instagram, relatada). O LinkedIn aceita uma capa apenas no momento da publicação: depois disso, apenas o texto de uma postagem pode ser editado (LinkedIn Help, "Edit a shared post", https://www.linkedin.com/help/linkedin/answer/a522811). Sua especificação de miniatura, JPG ou PNG no formato do vídeo e no máximo 2 MB, vem da ajuda do LinkedIn conforme citado por um resumo de busca; a página em si não carregaria, então trate como não verificado. - Envie a pré-visualização para celular primeiro, depois o arquivo completo e a capa.
O contrato da página
window.seek(T): desenha o quadro T. É o único lugar que anima qualquer coisa.window.ready: uma promise que resolve após fontes, texturas e o primeiro quadro.window.DUR: a duração do filme em segundos.window.FILM = { w, h, format, safe }: o tamanho do quadro e a zona de segurança em pixels. Todas as ferramentas dimensionam sua janela a partir disso, então a página possui seu próprio formato.- Opcional:
window.cover()desenha a capa em vez de um quadro, eFILM.coverAtnomeia o momento em que ela se encontra (tools/cover.mjs usa ambos; sem eles, pega o quadro emT=). window.collide(T)ewindow._dbg: hooks para film-audit (veja essa skill).- As ferramentas servem a pasta do kit via http por conta própria (módulos ES não carregam de
file://). Para assistir a uma página ao vivo em um navegador,npm run servee abra http://127.0.0.1:8765/template/starter.html.
Formatos, o starter e outros idiomas
template/formats.jsoncontém os quatro tamanhos (9:16 1080×1920 · 4:5 1080×1350 · 1:1 1080×1080 · 16:9 1920×1080), o mapeamento plataforma → formato e a zona de segurança de cada plataforma com sua fonte. A linha de formato do brief escolhe a partir dele.- Cada reel vive em sua própria pasta,
reels/<name>/(gitignorada): seu brief, storyboard,page.html(uma cópia do starter ou de film.html), seureel.jsonoucareer.jsone seuout/. As páginas carregam fontes e formats.json da raiz do kit, então uma cópia funciona a partir dali. Aponte as ferramentas para ela:PAGE=reels/<name>/page.html. template/starter.html+template/reel.jsoné o esqueleto para todo look, exceto o voxel walk. Suas palavras e tempos vivem na config, ele se dimensiona a partir de?fmt=ou da config e mantém cada palavra dentro do núcleo de segurança da plataforma.?guidesmostra as zonas cobertas em vermelho, apenas para verificação; nunca renderize com isso ligado. É um esqueleto, não um look: reel-style reescreve seus tokens e layout.template/film.html(o template voxel) é apenas 4:5, e opta por não usar zonas de segurança (safeé 0): seu banner e legendas ficam perto das bordas por design, então a sondasafesempre passa lá e o olho do auditor é a verificação. Suas câmeras são configuradas para 4:5; outra proporção significa reconfigurá-las e reexecutar cada gate.- Chinês, Japonês, Coreano: o starter carrega Noto Sans TC do Google Fonts no momento da renderização (apenas os caracteres que a config usa) e executa tipo CJK em aproximadamente 0,72× o tamanho latino (seus caracteres têm largura total de em). Escreva
\nonde uma linha deve quebrar: o chinês quebra entre quaisquer dois caracteres, então uma linha longa precisa de uma quebra escolhida por você.template/reel.zh.jsoné um exemplo prático. Qualquer outro script precisa de sua própria fonte na pilha.
Relógios e temporização
- Um relógio de filme dirige as cenas, a câmera, o banner, as legendas e o som. Quando você insere um beat mais tarde sem re-temporizar tudo, use um remapeamento: o relógio da cena pausa no ponto de inserção enquanto as sobreposições mantêm o relógio real:
O script de áudio precisa da mesma tabela: escreva umconst INS = [[24.0, 5.0, 'detail']]; // no tempo da cena 24.0, toque um beat inserido 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 }; }RT(t)que adiciona as durações inseridas a um tempo de cena e coloque cada cue emRT(t), ou cada cue após a inserção deriva. (O template não tem inserções, então seu audio.py não precisa de uma.) - Easing:
eiopara movimentos de câmera,eopara coisas chegando,trap(velocidade trapezoidal) para caminhar e dirigir, para que um movente não "estraga" nas extremidades. - Chaves de câmera são absolutas
A(pos, target)ou seguemF(offset, targetOffset)a partir do ponto de solo do herói. Misture entre chaves comeio. Segure em um beat que importa; um plano que se move o tempo todo soa como nervoso.
As sobreposições (no DOM, acima do canvas)
- Banner: ano, depois empresa, depois a pílula de cargo, mais uma linha do tempo cujo ponto respira no ano atual. O banner muda quando a nova cena ESTÁ NA TELA (seu conjunto subiu, a roupa mudou, o herói chegou), nunca quando o código começa a construí-la. Verifique isso em uma folha de quadros a ±1 s em torno de cada mudança.
- Legendas: uma linha de história por beat, com a frase-chave em negrito e acento. Use
text-wrap: balance. Dê a um leitor cerca de 3 palavras por segundo e esmaeça uma legenda antes de um movimento rápido de câmera descobrir o assunto atrás dela. - Um balão de fala que segue um personagem: projete o ponto da cabeça e prenda o balão ≥24 px dentro do quadro.
- Cartão final: nome, uma tagline, no que são bons, uma citação. A cena desfoca atrás dele.
Áudio
- As cues são escritas no relógio do filme, próximas ao momento a que pertencem (passos por perna de caminhada, um sino quando algo aparece, um baque quando pousa).
- Sem sons de whoosh em transições; soam como baratos. Use sinos, tiques e impactos suaves.
- A base padrão em
template/audio.pyé gerada a partir de ondas senoidais, então é gratuita para uso em qualquer lugar. Se alguém trouxer uma faixa gerada por IA, verifique sua licença: alguns pesos de modelos só permitem uso não comercial. - Loudness: two-pass loudnorm para −14 LUFS (meça, depois aplique linearmente), depois um limitador. Uma passagem ultrapassa.
Fatos de renderização que nos custaram dias (mais em references/gotchas.md)
- Renderize em 2x e reduza a escala. EffectComposer remove o anti-aliasing do canvas, então uma renderização 1x tem bordas irregulares em todos os lugares.
- Sem tags de cor no MP4. Um pipe JPEG o marca como full-range, e alguns players então deslocam as cores.
setparams=...=unknownno filtro. - Dois rostos no mesmo plano piscam (z-fighting). Dê ao da frente um deslocamento de profundidade.
- Fontes devem ser carregadas (
document.fonts.load) antes que qualquer texto do canvas seja desenhado, ou ele cai silenciosamente no fallback. - GPU ligada no Chromium headless (
--use-angle=metalno macOS). Sem ela, uma renderização é cerca de 10x mais lenta.
Trabalhando sem um prompt a cada passo
A pessoa disse sim às regras de permissão do kit uma vez (SETUP.md passo 4); fique dentro delas, ou elas serão perguntadas repetidamente:
- Escreva e altere arquivos com as ferramentas Write e Edit: páginas, reel.json, brief, storyboard, AUDIT.md. Não os gere com um script Python ou shell, um heredoc ou
sed -i; cada um desses é um prompt de aprovação. - Meça com as ferramentas do kit (stills, probe, sheet, collide, render, cover), não com trechos avulsos de
python3 -c. - Sem necessidade de
mkdir, loops ou cadeias: toda ferramenta cria sua própria pasta de saída e aceita uma lista de tempos em uma única chamada (stills.mjs … 1.5,6,12). - Os scripts próprios de um reel (seu script de som, um helper) vivem em
reels/<name>/e executam comopython3 reels/<name>/audio.pyounode reels/<name>/x.mjs, o que as regras cobrem.
Comandos
Execute cada ferramenta como um único comando simples (flags, sem VAR=… inicial, sem loops, sem cadeias &&). Assim, as regras de permissão da pessoa (node tools/*) o cobrem e eles não são solicitados a aprovar cada etapa. As variáveis de ambiente ainda funcionam para scripts.
npm run doctor # verifica esta máquina primeiro
npm run demo # a prova de 12 segundos: out/demo/
node tools/stills.mjs "2,8.5,15" # stills 2x → out/stills (o template voxel)
node tools/stills.mjs --page template/starter.html --format 16:9 1.6 # o starter em outra proporção
node tools/stills.mjs --page template/starter.html --cfg reel.zh.json 1.6 # outra config (aqui: chinês)
node tools/stills.mjs --dsf 1 --out out/chk 2,8.5 # quadros 1x rápidos para uma folha
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