O que stickman-explainer faz?
Transforma um roteiro curto em um mp4 finalizado de 1080×1920. Você escreve um storyboard JSON; scripts/render.py desenha cada frame (linhas trêmulas a tinta, line-boil a 8 fps, revelações com fade/wipe/pop) e codifica com ffmpeg.
assets/example_storyboard.json é apenas um exemplo de sintaxe (3 cenas); não copie seu layout ou acessórios de personagens como modelo.
Padrões de estilo visual
- Use uma tela 9:16 (1080×1920, 30 fps). Mantenha cada cena em uma ideia, geralmente 2,5–4 segundos, com legendas curtas dentro de áreas seguras da plataforma.
- Use papel quente (
#f3ebdd), tinta escura (#2b2723), e acento vermelho-tijolo semântico (#b8372d). Ouro (#d89d48) é opcional apenas para o motivo do sol. Mantenha o resto plano: sem gradientes, sombras ou efeitos 3D. - Desenhe figuras simples de tinta preta com rostos claros e bastante espaço negativo. Não adicione capacetes, coroas, capas, roupas vermelhas ou outros acessórios a menos que a história ou o usuário os peça. Não infira figurinos a partir de um nome histórico.
- Mantenha as cenas legíveis: no máximo 2 personagens e 5 ícones pequenos. Faça o assunto principal dominante; evite sobreposição de legendas e adereços. Use no máximo 2 linhas de legenda.
- Torne adereços complexos reconhecíveis. Desenhe um nó com voltas distintas que se cruzam, um centro apertado e pontas de corda visíveis. Dê a uma espada um cabo claro, guarda e lâmina pontiaguda. Após um corte, deixe uma lacuna visível entre as duas pontas da corda.
- Quando os tempos de narração estiverem disponíveis, faça o destaque vermelho da legenda seguir a palavra falada durante toda a narração. Use um intervalo
karaokepara cada palavra separada por espaço na legenda; palavras não ativas permanecem na cor normal do texto. - Prefira layouts assimétricos e deixe espaço negativo deliberado. Linhas de chão, sóis, cartões de título, balões de pensamento e outros motivos são opcionais; escolha-os apenas quando eles apoiarem a cena.
Fluxo de trabalho
- Roteiro → cenas. Divida a história em cenas de uma frase (≤ 6 palavras de legenda, 2,5–4 s cada). Padrão: cartão de título → gancho/conflito → revelação concreta. Cada cena adiciona exatamente uma nova informação. Quando a narração for fornecida, alinhe os limites da cena às frases faladas.
- Escolha um visual para cada frase. Uma metáfora por cena: personagem + adereço, balão de pensamento ou uma fileira de ícones (X = "não tem"). Mantenha ≤ 2 personagens e ≤ 5 ícones.
- Escreva o storyboard (esquema abaixo). Com tempos de narração no nível da palavra, adicione um intervalo
karaokepara cada palavra separada por espaço na legenda, para que o acento vermelho siga a palavra sendo falada. Use**palavra**apenas para um destaque estático quando o tempo da palavra não estiver disponível; não combine marcação estática comkaraoke. - Verifique os stills primeiro (rápido, ~2 s/cena):
python scripts/render.py story.json --stills out/stills→ abra cadascene_NN.png, corrija sobreposição/posicionamento, repita. - Vídeo rascunho:
python scripts/render.py story.json out/draft.mp4 --draft(sem supersampling, 12 fps). - Final:
python scripts/render.py story.json out/final.mp4 [--audio voiceover.mp3]. As durações das cenas devem corresponder ao comprimento das frases da narração; meça o áudio comffprobee definadurationde acordo. Os intervalos das palavras são em segundos desde o início da sua cena e devem vir do alinhamento da narração ou da escuta cuidadosa. Verifique se cada palavra vermelha aparece com seu áudio falado, não antes nem depois.
Mensagens de erro e validação
render.py valida o storyboard antes de desenhar: type/pose/face/kind/font desconhecidos, campo obrigatório ausente, id repetido na mesma cena, duration <= 0, caminho lines de ponto único, resolução ímpar (não par) e id de carry inexistente são rejeitados com mensagem clara em vez de traceback. Aviso é emitido para elementos que nunca serão vistos (at após o fim da cena). Se o arquivo de áudio for mais curto que o vídeo, o final é preenchido com silêncio; se for mais longo, é cortado; a duração do vídeo nunca muda.
Esquema do storyboard
{"meta": {"width":1080,"height":1920,"fps":30,"boil_fps":8,"transition":0.18,
"palette":{"accent":"#b8372d"}, "audio":null, "font_dirs":[]},
"scenes":[{"duration":3.4, "caption":"Foi **Alexandre**, o Grande.",
"caption_at":0.5, "caption_y":null, "elements":[ ... ]}]}
Para ênfase de palavras seguindo a fala, forneça um array karaoke com um objeto { "start": segundos, "end": segundos } por palavra separada por espaço em caption. Os tempos são locais à cena; a ênfase vermelha muda em cada intervalo. A legenda não deve conter marcação **...** quando karaoke estiver presente.
{"duration":2.8, "caption":"Cortou o nó com um golpe.", "caption_at":0.35,
"karaoke":[{"start":0.35,"end":0.76},{"start":0.76,"end":1.02},
{"start":1.02,"end":1.53},{"start":1.53,"end":2.15}],
"elements":[ ... ]}
Cada elemento: type, id opcional (para carry), at (s, tempo de aparecimento), dur (s, revelação), anim = fade | wipe | rise | pop, until/out_dur (s, desaparecimento).
Posições x,x2,y,ground ≤ 2 são lidas como frações da largura/altura, caso contrário pixels. Tamanhos são sempre em pixels.
| tipo | campos principais |
|---|---|
ground | y (obrigatório); x, x2 opcionais, se não fornecidos ~2%–98% da tela — linha de base trêmula |
sun | x,y,r,rays |
text | text (marcação **accent**), x,y,size,font = sans/sans_bold/sans_light/serif, tracking, caps, color, max_w, max_lines (padrão 3), rules (linha dupla para cartão de título; largura e espaçamento automáticos conforme o texto) |
figure | x,ground,h,head_r, pose = stand/arms_open/point/wave/think/shrug/sit, dir (±1), face = neutral/smile/sad/surprised/angry, accessory opcional = helmet/crown/hat/glasses, cape opcional, look |
prop | kind = barrel/bowl/bucket/house/book/box/triangle/coins/tree, x,y (canto superior esquerdo), w,h, cross (X de acento), flip |
thought | x,y,w,h, tail_to [px,py] (posição da cabeça), inner: lista de elementos em px local ao balão |
lines | paths (lista de listas de pontos, px local), closed, smooth, color, width — escape hatch para qualquer forma personalizada |
Continuidade entre cenas (carry)
Para transportar um elemento de uma cena para a próxima, dê a ele um id e chame-o na cena seguinte com carry. O elemento transportado começa com a mesma trepidação de linha (mesma seed), portanto parece ser "o mesmo desenho" mesmo com a mudança de cena; a transição de cena padrão é um corte (sem crossfade).
"carry": [
"ground",
{"id": "sun", "set": {"r": 122}},
{"id": "alex", "set": {"x": 120, "face": "smile"}, "tween": {"at": 0.1, "dur": 0.9}},
{"id": "diog", "until": 0.1, "out_dur": 0.5}
]
As linhas são processadas em ordem: "ground" é transportado exatamente como está · sun é transportado com campo alterado (a alteração é instantânea no início da cena) · alex tem campo alterado e posição interpolada ao longo do tempo (tween) · diog é transportado, mas desaparece gradualmente dentro da cena.
"carry": "all"transporta tudo ("carry_except": ["id", ...]exclui itens). A legenda nunca é transportada.- Um elemento não transportado desaparece instantaneamente no início da cena; para desaparecimento suave, use
until/out_dur. Um elemento que desaparece comuntilnão pode ser transportado para a próxima cena. tweenanima apenas posição (x e y/ground); mudanças de tamanho (r,h,w) e aparência (face,pose) são aplicadas instantaneamente no início da cena.- Elementos transportados são desenhados abaixo dos novos elementos.
iddeve ser único entre cenas para elementos a serem transportados. - Um elemento com o mesmo
idusa a mesma seed em cada cena; para um novo elemento específico da cena, não forneçaid.
Adicione novos adereços/poses estendendo PROPS / hands em render.py; mantenha o mesmo estilo de tinta (use stroke(), nunca linhas simples).
Layout: decida novamente para cada vídeo
Não existem coordenadas/modelos fixos. Como o assunto de cada vídeo é diferente, derive o layout do conteúdo da cena:
- Primeiro, escolha a única ideia visual para aquela frase (comparação, lista, sonho, jornada, número…), depois monte o layout que se encaixa nela. O layout pode variar entre cenas no mesmo vídeo.
- Abra espaço para o elemento importante: o assunto principal deve ser o maior/mais proeminente; os elementos secundários devem ser pequenos. Deixe espaço vazio (espaço negativo) de forma deliberada.
- Tamanho, posição e número variam conforme o conteúdo (1 personagem ou 2; fileira de ícones ou um único objeto grande; mapa/linha do tempo). Se necessário, desenhe formas personalizadas com
lines. - Não adicione figurino/cobertura automaticamente para figuras históricas. As figuras são desenhadas sem acessórios por padrão; capacete, coroa, chapéu ou capa são adicionados apenas se a narrativa ou o usuário explicitamente exigir. Não espalhe a cor de acento como adereço na roupa do personagem.
- Em cenas de nó e corte, construa o objeto de forma legível: desenhe a corda com várias voltas que se cruzam visivelmente, mostre o nó apertado no centro e diferencie as pontas. Na espada, o punho, a guarda e a lâmina pontiaguda devem ser distinguíveis. Após o corte, deixe as duas pontas cortadas da corda espaçadas e claramente visíveis; a espada e o nó não devem ser cobertos pela legenda. Desenhe formas de corda/espada personalizadas com
lines. - Linhas de chão, sol, cartão de título e outros motivos são opcionais; não use se não combinarem com o assunto (ex.: interior, noite, conceito abstrato).
- Coloque a legenda em um local legível e longe da interface da plataforma; se houver conflito, ajuste com
caption_y. Verifique conflitos e estouros visualmente nos stills, não confie em números. - As coordenadas de desenho são fornecidas em pixels (ou proporção 0–2) na tela de 1080×1920; planeje os valores do zero para cada cena.
Pegadinhas
- Sempre veja os stills antes de codificar; elementos sobrepostos e raios de sol cortados são os defeitos mais comuns. Use
--draftpara rascunho, renderização completa para detalhes: quadros inalterados não são redesenhados, um vídeo de 10 s leva cerca de 20 s em qualidade total. - A legenda quebra em até 86% da largura (máximo 3 linhas), mas diminui se uma palavra não couber ou exceder 3 linhas. Divida uma frase com mais de 2 linhas em duas cenas. A posição padrão da legenda é apenas um fallback; forneça
caption_yna cena se necessário. - Fontes: instale Poppins e Playfair Display (ou Lora) ou forneça
--font-dir. Se nenhuma fonte TrueType for encontrada, o renderizador emite um aviso e caracteres turcos (İ ş ğ â) podem ficar ausentes; verifique nos stills. Pastas de fontes do Linux, macOS e Windows são varridas. - Mantenha a cor de acento semântica (assunto, palavra-chave, negação). Não adicione mais cores.
- Quando
karaokeestiver presente, o destaque vermelho da legenda segue a narração palavra por palavra usando os intervalos fornecidos. Não deixe uma palavra-chave vermelha fixa na tela; renderize palavras não ativas na cor normal do texto da legenda.