Criador de vídeos explicativos
Você faz vídeos da mesma forma que um motion designer faria, mas em código: cada cena é um pequeno módulo JS que constrói DOM/SVG e adiciona animações a uma timeline GSAP pausada. O renderizador percorre essa timeline quadro a quadro (t = frame / fps), captura a tela de cada quadro e envia os quadros para o ffmpeg. A saída é determinística: o mesmo projeto sempre renderiza os mesmos quadros.
SKILL_DIR abaixo significa o diretório base desta skill (a pasta que contém este arquivo).
Configuração (uma vez por máquina)
cd SKILL_DIR/scripts && npm install && npx playwright install chromium
Você também precisa do ffmpeg no PATH. Cada script imprime este comando se as dependências estiverem faltando.
Fluxo de trabalho
- Storyboard primeiro. Entre em acordo sobre o tópico, público, duração alvo e se deve ter narração, então escreva a lista de cenas em
video.json. Cada cena recebenarration(o que a narração diz) evisual(o que está na tela). Leiareference/storyboard.md. Não escreva código de cena até que o storyboard faça sentido por si só. - Estrutura inicial:
node SKILL_DIR/scripts/new.mjs <project-dir> --title "...", então editevideo.jsone escreva um módulo por cena emscenes/<id>.js. Usereference/animation-patterns.mdpara receitas ereference/design.mdpara layout, tipografia e cor. Com narração, defina durações de cena como"auto"e temporize animações comcue('frase'). Ambos os exemplos são vídeos completos com narração:examples/kubernetes-overview/(ElevenLabs) eexamples/how-dns-works/(Gemini). - Narração (se desejado): siga
reference/voiceover.md. Em resumo: adicione um bloco"voice"aovideo.json, executenode SKILL_DIR/scripts/narration.mjs status <project-dir>, chame a ferramentagenerate-speechdo MCP OpenRouter uma vez por cena listada com exatamente os argumentos que ela imprime, e registre cada arquivo salvo comnarration.mjs add. Faça isso antes da verificação (passo 4), porque o áudio real altera as durações das cenas e os tempos decue(). - Verificação:
node SKILL_DIR/scripts/lint.mjs <project-dir>. Ele carrega cada cena no Chromium e relata erros de carregamento, animações mais longas que sua cena, animações criadas fora detl, elementos fora do quadro, texto cortado e narração muito longa para sua cena. Corrija cada aviso a menos que seja intencional. - Visualize:
node SKILL_DIR/scripts/render.mjs <project-dir> --contact-sheet, então Leia o PNG (<project-dir>/out/contact-sheet.png). Verifique hierarquia, sobreposição, legibilidade, quadros vazios, temporização. Para detalhes, renderize momentos específicos:--stills 12.5,30. Itere até parecer correto, então faça mais uma passagem de contact-sheet após sua última edição. - Renderize:
node SKILL_DIR/scripts/render.mjs <project-dir>→<project-dir>/out/video.mp4. Os clipes de narração são mixados automaticamente. Dê ao usuário o caminho, a duração e a lista de cenas. Eles podem visualizar interativamente comnode SKILL_DIR/scripts/serve.mjs <project-dir>(scrubber, play/pause, reprodução de narração,calterna legendas,msilencia).
Bandeiras de renderização úteis: --out file.mp4, --from 10 --to 20 (parcial), --scale 0.5 (rascunho rápido), --workers N, --crf 18, --audio track.mp3 (use sua própria faixa de áudio em vez da narração), --no-voice (silencioso), --audio-codec aac (o padrão é MP3, que também toca no VS Code).
Estrutura do projeto
my-video/
video.json # título, largura, altura, fps, semente, transição, tema, cenas[]
scenes/<id>.js # um módulo por cena
assets/ # imagens/SVGs, referenciadas como '/assets/x.svg'
audio/ # clipes de narração + manifest.json (de narration.mjs; mantenha-os, eles custam dinheiro)
style.css # opcional, carregado automaticamente se presente
out/ # renderizações (contact-sheet.png, stills/, video.mp4)
Campos de cena em video.json: id (obrigatório, também o nome de arquivo padrão scenes/<id>.js), duration (segundos, ou "auto": ajusta ao áudio da narração, ou uma estimativa de velocidade de leitura antes do áudio existir, e nunca menor que as animações), narration, visual, file opcional e transition opcional ("fade", o padrão, ou "cut"). O bloco "voice" de nível superior é descrito em reference/voiceover.md.
Contrato da cena
import { h, reveal, pop } from '/__runtime/helpers.js';
export default function ({ el, tl, gsap, theme, width, height, random, onFrame, cue, voice }) {
const title = h('div', { class: 'h1' }, 'Olá');
el.append(h('div', { class: 'center' }, title));
reveal(tl, title, { at: 0.3 }); // tl time 0 = início desta cena
tl.to(title, { color: 'var(--accent)', duration: 0.5 }, cue('a ideia principal')); // quando essa frase for falada
}
el: contêiner desta cena. Ele cobre todo o palco 1920×1080 e é mostrado e ocultado para você.tl: timeline desta cena. Todo movimento deve estar emtl(ou outra timeline adicionada a ela). Nunca usegsap.to(...)diretamente, animação CSSanimation/transition,setTimeout,setInterval,requestAnimationFrame,<video>, ou qualquer coisa que dependa do tempo de relógio de parede. O renderizador apenas move o playhead da timeline, então qualquer outra coisa acaba congelada ou dessincronizada no MP4.cue(frase, offset = -0.15)retorna o tempo da cena quandofrase(insensível a maiúsculas/minúsculas, deve aparecer nesta cena de narração) é falada. Uma vez que o áudio existe, é alinhado às pausas medidas do clipe, então frases que iniciam uma cláusula caem na palavra falada. Ele lança um erro se a frase não for encontrada. Use-o para cadaatsincronizado com a narração.voicefornece{ start, duration, end, estimated }para a fala nesta cena (por exemplo, para manter um efeito de loop atévoice.end).- Para desenho quadro a quadro (canvas, movimento procedural), use
onFrame((sceneTime) => draw(sceneTime)). É chamado em cada quadro enquanto a cena está visível. - Sobreposição (opcional):
"overlay": "scenes/_overlay.js"emvideo.jsonadiciona um módulo desenhado acima de cada cena durante todo o vídeo, para timecode, barra de progresso, logotipo ou borda de quadro. Ele recebe{ el, tl, onFrame, scenes, duration, ... }, ondeonFrame((t) => ...)recebe o tempo global esceneslista ostart/endde cada cena. random()é semeado por cena, eMath.randomtambém é semeado, então layouts aleatórios são reproduzíveis.- A função pode ser
async(por exemplo, parafetch('/assets/data.json')). As fontes são carregadas antes da execução das cenas, portanto medir texto funciona. - As cores do tema são variáveis CSS:
var(--bg) --surface --surface2 --fg --muted --accent --accent2 --accent3 --accent4 --danger, mais--fonte--mono. Substitua-as emvideo.json→"theme": { "accent": "#ff3366" }. - Plugins GSAP são registrados globalmente: DrawSVG, MotionPath, MorphSVG, SplitText, Text, ScrambleText, CustomEase.
Padrão de qualidade
- Uma ideia por cena. Algo na tela deve se mover ou mudar pelo menos a cada 2–3 segundos, e cada animação deve cair em sincronia com a linha de narração que ilustra (use
cue(); narração corre a ~2.5 palavras/segundo). - O texto é grande: corpo ≥ 40px e rótulos ≥ 26px no palco 1920×1080. Mantenha-o dentro da área segura (
.safe, margens de 96/128px). - Construa diagramas peça por peça, em vez de mostrar um diagrama finalizado.
- Deixe uma pequena pausa no final de cada cena antes do fade, para que o estado final possa ser lido.
Narração
O áudio vem do servidor MCP OpenRouter (generate-speech); esta skill apenas planeja e grava. Se essas ferramentas não estiverem disponíveis, informe ao usuário que a narração precisa do servidor MCP OpenRouter conectado e renderize sem narração enquanto isso. reference/voiceover.md tem o procedimento completo, modelo e escolhas de voz, e custos.