vid-product-launch
Gera um vídeo narrativo de lançamento de produto em MP4 a partir de uma descrição de produto e contexto de lançamento. Pipeline: Animações HTML/CSS → Chromium headless (Playwright, quadro a quadro) → FFmpeg (H.264 MP4). Sem React. Sem APIs de vídeo com IA. Sem Python. Custo zero de execução além de Playwright + FFmpeg.
Regras Críticas (leia antes de cada geração)
-
O slogan não é opcional. Não o pule. Ele é a promessa completa do produto em 4–6 palavras. Se o usuário não forneceu um, derive um da descrição — uma destilação nítida, em voz ativa. Nunca escreva "[slogan aqui]" ou deixe em branco.
-
O momento da revelação deve parecer merecido. As seções de provocação e construção existem para criar tensão. Se o nome do produto aparecer nos primeiros 20% do vídeo, a narrativa desmorona. Nunca revele o nome do produto antes da seção Revelação.
-
Tamanho da fonte do nome do produto: mínimo 120px para 16:9, mínimo 80px para 9:16. O nome deve ser o maior elemento no momento da revelação. Se não for dominante, a revelação falha.
-
Uma estatística de prova. Não uma lista. Se o usuário fornecer várias estatísticas, escolha a mais forte. Uma lista de 5 números destrói o impacto. Um número superdimensionado o cria.
-
Use
window.renderFrame(t)— sem@keyframesCSS para transições de cena. A busca porcurrentTimeno CSS é silenciosamente ignorada para buscas reversas no Chromium. A função renderFrame calcula estilos de elementos diretamente a partir de milissegundos. O Playwright a chama uma vez por quadro. -
Sem
animation-delayem NENHUM elemento. Não é necessário com renderFrame. Se você escreveranimation-delay, pare — você está usando a arquitetura errada. -
window.__videoReady = trueapenas dentro dedocument.fonts.ready.then(...). Nunca defina de forma síncrona. -
Exponha
window.__stopPreview(). O loop de pré-visualização rAF compete com as chamadas evaluate/screenshot do Playwright. O script de captura chama__stopPreview()antes do loop de quadros. -
Use
t < startMs(nãot <= startMs) nas verificações de limite de cena.t <= 0no quadro 0 torna a cena 1 preta. -
Body = dimensões exatas em pixels. Largura e altura são números inteiros. Sem
%,vw/vh,rem. -
Leia
references/scene-library.mdEreferences/style-presets.mdantes de gerar QUALQUER HTML. -
Nunca despeje HTML no chat. Salve em arquivo. Mostre apenas o resumo.
-
O canvas de granulação de filme DEVE ser 240×135, não W×H. Defina
width="240" height="135"no elemento canvas e depois estique comstyle="width:[W]px;height:[H]px". Granulação em resolução total a 1920×1080 é 64× mais lenta — 8MB de ImageData por quadro — e fará com que exportações de 1800 quadros levem horas. -
A seção de provocação DEVE ser escura (#000 ou próximo do preto), independentemente do tom. Fundos brancos na seção de provocação parecem slides de demonstração, não vídeos de lançamento de produto. O arco narrativo do escuro para o claro (provocação escura → construção escura → revelação do produto) é como os vídeos de lançamento criam drama. Mesmo os predefinidos
minimaleenergeticdevem usarbackground: #000para as cenas de provocação-problema e construção de tensão. -
Cada palavra de provocação deve ser sua própria batida com tamanho de fonte de 200px+. Palavra por palavra em uma única linha não é impactante o suficiente. Cada palavra do problema (ex.: "Pesquisar." / "Escrever." / "Divulgar." / "Repetir.") recebe 1500–1800ms de tempo de tela com
font-size: 200px; font-weight: 900centralizada, uma de cada vez. Veja o padrãotease-wordsem scene-library.md. -
A seção de construção deve mostrar conteúdo que narre o problema — não apenas partículas. Use o padrão
terminal-card: 3 cartões aparecem sequencialmente com uma animação de máquina de escrever mostrando o trabalho manual sendo feito. Os cartões têmborder: 1px solid rgba(255,255,255,0.07), acento de ponto azul, texto monoespaçado. Apenas partículas por 20 segundos é tempo de tela vazio. -
Fundo CSS com grade de pontos em cenas escuras. Adicione
background-image: radial-gradient(circle, rgba(255,255,255,0.035) 1px, transparent 1px); background-size: 60px 60px;nas cenas de construção, revelação, prova e CTA. Não custa nada computacionalmente e adiciona profundidade.
Passo 1: Coleta de Informações
Obrigatório:
product_name— o nome do produto ou funcionalidade sendo lançadaproduct_description— 2–3 frases: o que faz, para quem é, benefício principal
Parâmetros opcionais e padrões:
| Parâmetro | Padrão | Descrição |
|---|---|---|
| tagline | auto | 4–6 palavras — a promessa central do produto |
| problem_statement | inferido automaticamente | 1 frase para a seção de provocação (a dor que o produto resolve) |
| proof_stat | inferido automaticamente | Métrica única (ex.: "500+ equipes", "10× mais rápido", "$2M economizados") |
| cta | auto | URL ou frase de ação (ex.: "opendirectory.dev", "Entre na lista de espera") |
| launch_date | — | String de data ISO (habilita a cena do cartão de contagem regressiva) |
| tone | cinematográfico | cinematográfico / energético / minimalista / emocional |
| duration | 60 | 30 / 60 / 90 (segundos) |
| aspect_ratio | 16:9 | 16:9 (1920×1080) / 9:16 (1080×1920) |
| letterbox | falso | Barras pretas cinemáticas 2.35:1 — apenas 16:9 |
| music | — | Caminho para arquivo de áudio (mp3/m4a/wav) |
| fps | 30 | Quadros por segundo (24, 30 ou 60) |
Se product_name ou product_description estiver faltando, pergunte exatamente:
"Para criar o vídeo de lançamento, preciso de duas coisas:
- Nome do produto — qual é o nome do produto?
- Descrição do produto — 2–3 frases: o que ele faz, para quem é, qual é o principal benefício?
Opcional: tagline (4–6 palavras), estatística de prova (um número), CTA (URL ou frase de ação), tom (cinematográfico / energético / minimalista / emocional), duração (30 / 60 / 90s)."
Se ambos estiverem presentes → prossiga imediatamente para o Passo 2.
Passo 2: Arquitetura Interna (nunca mostrada ao usuário)
1. Derive parâmetros ausentes:
- tagline (se não fornecida): Escreva uma a partir da descrição do produto. 4–6 palavras. Voz ativa. Sem enchimento ("O futuro de…", "Apresentando…"). Derive a promessa mais nítida possível.
- problem_statement (se não fornecida): Infira da descrição do produto. 1 frase declarando a dor. Nunca menciona o nome do produto. Escrita para fazer o público concordar com a cabeça.
- proof_stat (se não fornecida): Infira da descrição do produto. Se nenhuma estatística estiver disponível, use um indicador de escala credível ("Confiado por 500+ equipes", "De dias para minutos").
- cta (se não fornecida): Use o domínio do produto se inferível do nome do produto, ou "Saiba mais" como frase de ação.
2. Calcule o tempo das seções a partir de duration:
| Seção | 30s | 60s | 90s | Fórmula de início |
|---|---|---|---|---|
| Provocação | 0–5s | 0–10s | 0–15s | 0ms |
| Construção | 5–12s | 10–30s | 15–40s | Fim da provocação |
| Revelação | 12–20s | 30–45s | 40–60s | Fim da construção |
| Prova | 20–25s | 45–55s | 60–75s | Fim da revelação |
| CTA | 25–30s | 55–60s | 75–90s | Fim da prova |
Converta cada limite para milissegundos. Atribua a constantes:
TEASE_START_MS, TEASE_END_MS
BUILD_START_MS, BUILD_END_MS
REVEAL_START_MS, REVEAL_END_MS
PROOF_START_MS, PROOF_END_MS
CTA_START_MS, CTA_END_MS
3. Selecione cenas por seção:
- Provocação: Sempre começa com
blackout-opener(1500ms). Depoistease-words(cada palavra-chave do problema como sua própria batida de 1600–1800ms com 200px+). Selaunch_datefor fornecido, opcionalmente adicionecountdown-cardno final da provocação. - Construção: Sequência
terminal-card(3 cartões mostrando tarefas manuais sendo digitadas) + linha de acento opcional de fechamento. Duração ≥ 30s recebe todos os 3 cartões. Duração = 30s usa 2 cartões. Nunca use apenas partículas por mais de 5s — a tela deve mostrar conteúdo que narre o problema. - Revelação:
reveal-hero— sempre primeiro nesta seção. Opcionalmente seguido portagline-cardse duração ≥ 60s. - Prova:
proof-statse uma estatística numérica estiver disponível. Caso contrário,feature-bullet. - CTA:
cta-card— sempre, sempre por último.
Contagens típicas de cenas: 30s = 4 cenas, 60s = 6–7 cenas, 90s = 7–8 cenas.
4. Calcule o tempo por cena:
Dentro de cada seção, divida o tempo igualmente entre as cenas dessa seção.
Exceção: blackout-opener sempre recebe exatamente os primeiros 1500ms da Provocação.
5. Determine as dimensões de pixel:
16:9→ W=1920, H=10809:16→ W=1080, H=1920
6. Cálculo de letterbox (se ativado, apenas 16:9):
- Altura do conteúdo 2.35:1 = W / 2.35 = 1920 / 2.35 ≈ 817px
- Altura da barra = (H - 817) / 2 = (1080 - 817) / 2 ≈ 132px
- Barra superior:
top: 0; height: 132px - Barra inferior:
bottom: 0; height: 132px
7. Sinalizadores de efeitos cinemáticos (por predefinição):
cinematográfico: granulação-de-filme=LIGADO (canvas 240×135, opacidade 0.025), vinheta=LIGADO, vazamento-de-luz=LIGADO (dourado quente), grade-de-pontos=LIGADO em cenas escurasenergético: granulação-de-filme=DESLIGADO, vinheta=DESLIGADO, grade-de-pontos=LIGADO, flash branco na revelaçãominimalista: granulação-de-filme=LIGADO (canvas 240×135, opacidade 0.022), vinheta=LIGADO (sutil, radial 0.5), grade-de-pontos=LIGADO, sem vazamento-de-luz — cor de acento = #4B9FFF (azul elétrico)emocional: granulação-de-filme=LIGADO (canvas 240×135, opacidade 0.018, mistura de tom quente), vinheta=LIGADO (suave), vazamento-de-luz=LIGADO (cobre quente), grade-de-pontos=DESLIGADO
Todas as predefinições: as cenas de provocação e construção SEMPRE usam fundo escuro (#000 ou próximo do preto), independentemente da predefinição.
8. Incorpore window.__sfxTimeline no HTML (sempre — mesmo quando nenhuma música for fornecida):
O script de exportação lê este array, sintetiza cada tipo de SFX nativamente com aevalsrc/anoisesrc do FFmpeg e coloca eventos em deslocamentos de milissegundos exatos usando adelay. Nenhum arquivo de áudio externo é necessário.
Referência do tipo de SFX:
| Tipo | Som | Duração | Notas |
|---|---|---|---|
word-hit | Soco sub (50Hz) + clique transitório (2.2kHz) + rajada de ruído | 180ms | Um por palavra de provocação — 3 camadas via amix |
type-sequence | Cliques de teclado mecânico a 15Hz (envoltório de pulso sin^30) | 1.9s | Um por cartão — executa por toda a TYPE_DUR |
whoosh | Varredura de ruído de duas bandas (corpo 1.1kHz + ar 4–8kHz) | 700ms | Na transição BUILD_CLOSE |
tension-riser | Rumor baixo (250Hz) + tom sub crescendo com rampa t/2.8 | 2.9s | Começa em BUILD_CLOSE + 700ms — atinge o pico em REVEAL_START |
reveal-boom | Sub (45Hz) + corpo (90Hz) + brilho (4.5–11kHz) + eco de 85ms | 900ms | Exatamente em REVEAL_START — O impacto |
counter-tick | Clique harmônico (880Hz + 440Hz + 1760Hz + 2640Hz) | 80ms | 3 batidas — decrescendo 0.32 → 0.26 → 0.20 |
cta-chime | Acorde maior de A (440 + 554 + 659 + 880Hz) + brilho de eco | 1.2s | Exatamente em CTA_START |
Mapa de tempo dos SFX — calcule valores ms a partir das constantes de seção, nunca codifique:
window.__sfxTimeline = [
// ── PROVOCAÇÃO: um word-hit por batida de palavra, último mais alto ───────────────────────
{ ms: WORD_BEATS[0].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[1].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[2].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[N].start, sfx: 'word-hit', vol: 0.65 }, // última palavra mais alta
// ── CONSTRUÇÃO: type-sequence por cartão (1.9s, corresponde a TYPE_DUR 1800ms) ────────
{ ms: CARDS[0].start + 180, sfx: 'type-sequence', vol: 0.28 },
{ ms: CARDS[1].start + 180, sfx: 'type-sequence', vol: 0.28 },
{ ms: CARDS[2].start + 180, sfx: 'type-sequence', vol: 0.28 },
// ── TRANSIÇÃO: whoosh depois elevador de 2.9s atingindo pico em REVEAL_START ───────────
{ ms: BUILD_CLOSE, sfx: 'whoosh', vol: 0.50 },
{ ms: BUILD_CLOSE + 700, sfx: 'tension-riser', vol: 0.35 },
// ── REVELAÇÃO: o impacto cinematográfico ─────────────────────────────────────────────
{ ms: REVEAL_START, sfx: 'reveal-boom', vol: 0.88 },
// ── PROVA: ticks harmônicos, decrescendo ───────────────────────────────────
{ ms: PROOF_START, sfx: 'counter-tick', vol: 0.32 },
{ ms: PROOF_START + 2200, sfx: 'counter-tick', vol: 0.26 },
{ ms: PROOF_START + 4400, sfx: 'counter-tick', vol: 0.20 },
// ── CTA: acorde maior de A aterrissando ────────────────────────────────────────────
{ ms: CTA_START, sfx: 'cta-chime', vol: 0.62 },
];
Coloque este bloco imediatamente antes do loop de pré-visualização, depois de window.renderFrame.
Passo 3: Geração de HTML
Leia references/scene-library.md E references/style-presets.md antes de escrever qualquer código.
Use os nomes de classe CSS exatos, a estrutura HTML e os padrões de renderFrame desses arquivos.
Esqueleto HTML obrigatório:
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
[link CDN da fonte do predefinição de estilo]
<style>
:root {
[todos os tokens CSS do predefinição de estilo]
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: [W]px; height: [H]px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
position: relative;
}
.scene {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px;
opacity: 0;
will-change: opacity, transform;
}
.scene-inner {
width: 100%;
max-width: 1200px;
}
[cole o CSS de cada tipo de cena em scene-library.md]
</style>
</head>
<body>
<!-- Elementos de sobreposição cinematográfica — renderizados sobre todas as cenas -->
[se granulação-de-filme: LIGADO]
<canvas id="grain-overlay"
width="[W]" height="[H]"
style="position:fixed;inset:0;pointer-events:none;opacity:0.025;mix-blend-mode:overlay;z-index:50"></canvas>
[fim se]
[se vinheta: LIGADO]
<div id="vignette-overlay"
style="position:fixed;inset:0;background:radial-gradient(ellipse at center,transparent 35%,rgba(0,0,0,0.65) 100%);pointer-events:none;z-index:51"></div>
[fim se]
[se vazamento-de-luz: LIGADO]
<div id="light-leak"
style="position:fixed;inset:0;background:linear-gradient(135deg,rgba(255,220,140,0.5) 0%,rgba(255,255,255,0.3) 45%,transparent 70%);opacity:0;pointer-events:none;z-index:52"></div>
[fim se]
[se letterbox: LIGADO]
<div id="lbox-top" style="position:fixed;top:0;left:0;width:[W]px;height:132px;background:#000;z-index:100"></div>
<div id="lbox-bot" style="position:fixed;bottom:0;left:0;width:[W]px;height:132px;background:#000;z-index:100"></div>
[fim se]
<!-- Cenas -->
[HTML da cena dos modelos em scene-library.md, um por cena selecionada]
<script>
window.__videoReady = false;
window.TOTAL_DURATION_MS = [duration * 1000];
// ── Constantes de tempo das seções ──────────────────────────────────────────────────
const TEASE_START_MS = [N];
const TEASE_END_MS = [N];
const BUILD_START_MS = [N];
const BUILD_END_MS = [N];
const REVEAL_START_MS =
…
[truncado para tradução]