Kinetic Promo Video
Construa um vídeo promocional vertical com aparência de transmissão a partir apenas de uma descrição do negócio. O vídeo é escrito como uma única página HTML controlada por um relógio mestre pausado, depois renderizado quadro a quadro e codificado. Sem editor, sem banco de imagens, sem aplicativo de linha do tempo — e cada quadro é reproduzível a partir de um timestamp.
Entradas
$brief— o que é a coisa, para quem é, o que a torna diferente. Obrigatório. Extraia da conversa, do README do repositório ou pergunte.$reference— opcional. URL de vídeo ou arquivo local cujo visual deve ser combinado.$brand— opcional. Nome, tagline, URL, cores, logotipo. Se ausente, use um espaço reservado claramente sinalizado (nunca invente uma marca silenciosamente e apresente como final).$duration— opcional. Padrão 30–35 s sem áudio, 45–60 s com narração (a voz define o comprimento — vejareferences/audio.md). Mantenha abaixo de 60 s para Shorts/Reels/TikTok.$audio— padrão: narrado, sem música. Escreva uma narração e construa a linha do tempo em torno dela, a menos que o usuário peça uma peça sem áudio. Música é a única coisa que fica desligada a menos que explicitamente solicitada.
Objetivo
promo.mp4 — 1080×1920, 30 qps, H.264, reproduzível e correto na primeira exibição —
além do scene.html que o gerou, um render.py que o reproduz, um arquivo legenda voice.srt
para peças narradas e um README com o roteiro de batidas e as constantes para re-marcação.
Sucesso não é "um arquivo existe": são quadros que você visualizou e que são legíveis, sem
sobreposição de texto, sem elementos visíveis antes de sua deixa, e sem silêncio morto.
Etapas
1. Verifique a toolchain
which ffmpeg ffprobe yt-dlp python3
python3 -m venv .venv && .venv/bin/pip install -q playwright && .venv/bin/playwright install chromium
ffmpeg e um Chromium para Playwright são requisitos obrigatórios. yt-dlp é necessário
apenas para desmontagem de referência. Prefira um venv — o Python do sistema do usuário pode ser
gerenciado externamente.
Critérios de sucesso: ffmpeg -version funciona e .venv/bin/python -c "from playwright.sync_api import sync_playwright; sync_playwright().start().chromium.executable_path" imprime um caminho.
2. Estabeleça o alvo visual
Dois caminhos — escolha com base na existência de $reference.
2a. Referência fornecida → desmonte-a
Leia references/teardown.md e siga-o. Resumindo: baixe, analise, extraia folhas de contato de 1 qps, leia as imagens da folha e anote
dimensões, duração, paleta, tratamento tipográfico, estrutura de batidas e vocabulário de movimento
antes de escrever qualquer código.
Nunca descreva uma referência que você não examinou quadro a quadro. O título ou a descrição de uma página não lhe diz nada sobre seu design de movimento.
2b. Sem referência → derive a linguagem do próprio assunto primeiro
Não abra default-style.md ainda. Passe dois minutos respondendo a quatro perguntas
sobre o assunto real:
- Quais são os objetos? Livros, potes, faturas, rostos, um painel. Esses são o que deve se mover.
- Em que luz eles vivem? Abajur, showroom, luar, brilho de tela, luz do dia através de uma janela.
- Como eles se movem fisicamente? Coisas com massa se acomodam. Papel vira e vinco. Líquidos se espalham. Dados estalam.
- Qual é a espinha dorsal? O elemento recorrente que carrega o argumento — uma contagem regressiva, um relógio atravessando a noite, um recibo crescendo, uma rota se preenchendo. Idealmente é a afirmação, não decoração.
Se essas quatro respostas lhe derem uma aparência coerente, construa-a. Um promo de skincare sobre reparo noturno ganha paleta noturna, tipo serifado, movimento lento de acomodação e um dial de 22h às 7h — não anéis brilhantes.
Recorra ao estilo padrão quando o assunto genuinamente não sugerir nada específico
(a maioria dos softwares B2B, serviços abstratos). Então leia references/default-style.md e use-o como está: é uma especificação completa e validada
(azul marinho escuro + brilho azul, sans-serif geométrico, tipo cinético com desfoque, horizonte brilhante, anel de encerramento). Informe ao usuário qual estilo você está usando em uma linha e que eles podem trocá-lo nomeando um vídeo de referência — depois continue. Não pare para aprovação.
Verificação de honestidade antes de construir. Se os tempos de suas batidas estiverem dentro de ~0,5 s do modelo e as tomadas características sobreviveram — a pilha de palavras ecoada, o anel florescendo, o cartão horizonte-laje, desfoque como único movimento de texto — você reestilizou o modelo em vez de projetar para o assunto. Isso é bom quando você escolheu a queda deliberadamente, e um defeito quando não escolheu.
Critérios de sucesso: uma especificação curta e escrita — tamanho da tela, qps, duração, hexes da paleta, fonte e os movimentos que você usará — à qual você possa se referir enquanto constrói. Se você derivou uma linguagem nativa do assunto, ela também nomeia a espinha dorsal e os movimentos que você está deliberadamente não usando.
3. Escreva o roteiro de batidas
Leia references/audio.md e escreva a faixa de voz primeiro — ela está ligada por padrão.
A voz domina a linha do tempo: gere as linhas, meça-as e derive os tempos das batidas a partir das medições, em vez de encaixar a fala em números redondos. Mantenha os tempos medidos por linha — eles se tornarão voice.srt na entrega. Pule esta etapa apenas quando o usuário pedir uma peça sem áudio.
Leia references/beats.md para a estrutura e regras de cópia, depois mapeie os verdadeiros
diferenciadores do usuário nas batidas. O terço médio é onde o produto é vendido: deve conter as razões específicas pelas quais esta coisa é melhor, não adjetivos. Assuma reprodução sem áudio — a maioria dos espectadores de Shorts/Reels nunca ouve a voz — então
cada afirmação na narração também deve aparecer como texto na tela, não apenas como narração.
Escreva o roteiro de batidas como uma tabela (faixa de t → batida) antes de escrever código. Errar o ritmo é muito mais caro de corrigir depois que as cenas são construídas.
Artefatos: tabela de batidas com faixas de segundos explícitas somando $duration.
Critérios de sucesso: cada batida remete a algo real sobre o produto, e as faixas são contíguas sem lacunas > 0,3 s.
4. Estruture a partir dos modelos
$SKILL abaixo é o diretório desta própria skill — aquele que contém este SKILL.md.
Resolva-o a partir de sua listagem de skills em vez de adivinhar um caminho; geralmente
está montado como somente leitura, o que é bom porque tudo será copiado.
mkdir -p video && cp "$SKILL/templates/render.py" video/
cp "$SKILL/templates/scene-starter.html" video/scene.html
bash "$SKILL/templates/fetch-fonts.sh" video/fonts # subconjuntos Poppins, ~40 KB
scene-starter.html já contém o harness de animação (seek, win, io,
pop, swap, draw, RNG semeado, atmosfera) e sete exemplos de batidas cobrindo
cada padrão que vale a pena copiar. Não reescreva o harness — ele codifica os bugs
listados em Regras. Substitua as batidas de exemplo; mantenha tudo acima delas.
Critérios de sucesso: python render.py --contact produz contact.png a partir do
starter inalterado.
5. Construa as cenas
Uma IIFE por batida, cada uma criando seu próprio layer() controlado por win(layer, tIn, tOut).
Dentro, cada elemento animado recebe exatamente uma chamada io() / pop() / swap().
Construa mockups de UI (painéis, telefones, laptops, cartões, chips) como DOM + CSS puro e anime-os — eles carregam a sensação de "produto real" que o tipo puro não consegue. Gráficos são divs e SVG inline; sem biblioteca de gráficos, sem imagens, sem requisições externas.
Um corte seco é um movimento legítimo: duas camadas win() compartilhando um limite de tempo
exato, inseridas com steps(1,end), fornecem um corte deliberado de quadro único — a
ferramenta certa para um salto tonal, como mundo da história → cor plana da marca.
Tudo o mais suaviza; o corte funciona porque é o único.
Logotipos multicoloridos reais são frequentemente ilegíveis tanto em cenas escuras quanto em fundos planos de marca — coloque o ativo em uma pequena placa branca em vez de recolorí-lo.
Regras: veja a seção Regras. Não são preferências de estilo — cada uma é uma falha específica que esta skill já encontrou.
Critérios de sucesso: python render.py --contact renderiza sem erros.
6. Loop de pré-visualização — olhe para cada quadro
python render.py --contact # visão geral de 24 quadros, ~20 s
python render.py --at 6.2 12.0 19.5 # momentos específicos, resolução total
Leia os PNGs resultantes com sua ferramenta de leitura de imagem — seja qual for o nome que o harness usa — e inspecione-os. Então corrija e repita. Itere aqui até a folha estar limpa — uma renderização completa é 10× mais lenta, então nunca a use como seu loop de feedback.
As duas visualizações respondem a perguntas diferentes. A folha de contato é para estrutura:
tempo, sobreposições, quadros mortos, ordem das batidas. É 270×480 por quadro e
sistematicamente subestima trabalhos escuros ou de baixo contraste — uma peça que pareça turva e quebrada na folha muitas vezes está limpa em tamanho real. Julgue contraste, paleta e
legibilidade apenas a partir de quadros --at, e extraia pelo menos um por batida sempre que
a paleta for escura, mais um por pictograma ou ilustração — a reconhecibilidade não pode ser julgada no tamanho do mosaico (veja Regra 12). Não aumente o brilho para corrigir algo que a folha exagerou.
O que procurar em cada passada:
- dois textos legíveis ao mesmo tempo durante a transição
- qualquer coisa visível antes de sua deixa (o clássico bug de
fill: 'both'— veja Regras) - elementos de fundo que parecem formas reconhecíveis em vez de atmosfera
- manchas translúcidas empilhadas — camadas alfa sobrepostas se compõem em arcos de interseção visíveis e parecem discos; use um elemento com um aro que se dissolve dentro de sua própria caixa, dimensionado maior que o quadro
- quadros mortos — qualquer momento sem nada na tela
- texto ou mockups apertados contra as bordas do quadro, ou muito pequenos para uma tela de telefone
Ponto de verificação humano: nenhum é necessário, mas se o usuário estiver presente, mostre a ele a folha de contato antes da renderização completa — anotações de ritmo e cópia são mais baratas agora.
Critérios de sucesso: você visualizou uma folha de contato na qual cada quadro lê limpo e nenhum defeito acima está presente.
7. Renderize e verifique
Teste o codificador primeiro. Renderize uma fatia de ~2 s — via uma flag de intervalo se sua
cópia de render.py tiver uma, caso contrário encurte temporariamente as constantes de duração
— e ffprobe o resultado. Problemas de codificador e multiplexação aparecem em segundos
aqui em vez de depois de uma renderização completa de vários minutos.
python render.py # -> promo.mp4, ~1 captura de tela por quadro
# peças narradas: multiplexe apenas após o processo do codificador TER SAÍDO
ffmpeg -y -i promo.mp4 -i vo/voice.wav -c:v copy -c:a aac -b:a 192k promo_av.mp4
ffprobe -v error -show_entries format=duration \
-show_entries stream=codec_type,codec_name,width,height,nb_frames \
-of default=noprint_wrappers=1 promo_av.mp4
ffmpeg -y -v error -i promo_av.mp4 -vf "fps=1/1.2,scale=180:320,tile=10x3" -frames:v 1 verify.png
Execute a renderização completa em segundo plano. São ~1 captura de tela por quadro — um vídeo de 50 s
é ~1500 quadros e excede confortavelmente um timeout de comando de 600 s. Monitore o
processo de renderização para sair, não para o mp4 aparecer: o arquivo existe no disco muito
antes de estar finalizado, e multiplexar cedo dá moov atom not found.
Para peças narradas, escreva voice.srt a partir dos tempos por linha medidos na
etapa 3 — os números já existem; esta é uma passada de formatação, não um trabalho novo.
Leia verify.png — verificar a saída codificada, não o navegador, é o
ponto. Em seguida, exclua as folhas de rascunho e o diretório de quadros.
Critérios de sucesso: ffprobe relata a duração esperada, as dimensões esperadas
(1080×1920 a menos que o usuário tenha mudado a proporção), e
nb_frames == fps × duração; para peças narradas, um stream de áudio está presente,
a duração corresponde à faixa de voz, e voice.srt existe; verify.png corresponde ao roteiro de batidas de ponta a ponta. Um arquivo curto com um final faltando geralmente significa
que -shortest encontrou uma faixa de voz desatualizada — veja references/audio.md.
8. Entregue
Escreva um README ao lado do vídeo com: o roteiro de batidas, as constantes de re-marcação, os comandos de renderização e as duas pegadinhas do harness. Em seguida, informe ao usuário:
- onde está o arquivo e suas especificações exatas (tamanho, duração, qps e se é narrado ou sem áudio — além da voz TTS usada, para que possa ser regenerada)
- o caminho de
voice.srtpara peças narradas, e que Shorts/Reels/TikTok o aceitam como upload junto com o vídeo - a tabela de batidas
- cada espaço reservado e número inventado, explicitamente mencionado — nomes de marca, URLs e qualquer métrica mostrada em um mockup. Diga claramente que números fabricados devem ser substituídos antes da publicação se as afirmações precisarem ser defensáveis.
- que não há link compartilhável — é um arquivo local — a menos que o usuário tenha pedido hospedagem
Ofereça-se para abrir: open promo.mp4.
Regras: nunca confirme ou publique sem aprovação explícita. Nunca envie o vídeo para nenhum lugar como efeito colateral da produção.
Critérios de sucesso: o usuário sabe o caminho do arquivo, o que é espaço reservado e como mudá-lo.
Regras
Restrições rígidas. Cada uma é uma falha que este pipeline já produziu.
-
Narrado por padrão, nunca música. Escreva uma narração e deixe-a dominar a linha do tempo a menos que o usuário tenha pedido uma peça sem áudio. Música e design de som permanecem desligados a menos que explicitamente solicitados — você não pode fornecer faixas licenciadas e nunca extraia áudio de um site de streaming. Assuma reprodução sem áudio independentemente: cada afirmação da narração é espelhada como texto na tela, e peças narradas são enviadas com um
voice.srt. Quando você enviar uma peça sem áudio, diga "sem áudio" ao relatar para que a reprodução silenciosa não seja interpretada como um bug. -
Uma animação por elemento, cobrindo entrada → espera → saída. Duas animações WAAPI separadas com
fill: 'both'conflitam: o clipe de saída aplica seu primeiro keyframe desdet = 0, fixando o elemento visível durante todo o vídeo. O helperio()existe unicamente para evitar isso. Sintoma: tudo em uma cena aparece de uma vez no início da cena em vez de animar para dentro. -
Texto consecutivo deve sair completamente antes do próximo entrar. Duas linhas legíveis na tela durante um crossfade são lidas como um bug de renderização, não uma transição. Agende a saída da linha N para ser concluída antes do início da entrada da linha N+1.
-
Determinismo é inegociável. Semeie o RNG. Nunca chame
Date.now(),Math.random(),
... [truncado para tradução]