Animação pintada
Um método para criar curtas-metragens animados inteiramente em código, destilado de JohnHeibel/PDoomVideo (um videoclipe de 156 s pintado por Claude Opus) e seu kit generalizado ClaudeAnimationBase (MIT), que está incluído aqui como template/.
Como funciona: cada quadro é uma função pura do tempo t. studio.html carrega p5.js + p5.brush e seus arquivos de cena; cada tomada pinta o quadro inteiro 1920×1080 com preenchimentos de aquarela e linhas de tinta em movimento. render.mjs utiliza Chrome headless para renderizar folhas de contato para revisão, ou todos os quadros em paralelo, e ffmpeg codifica o MP4 (com áudio, se fornecido).
Fluxo de trabalho
0. Scaffold
bash ~/.claude/skills/painted-animation/scripts/new_project.sh <diretório-do-projeto> # adicione --keep-demo para manter a cena de exemplo conectada
Isso copia o template, desconecta a cena de exemplo, executa npm install e verifica node / ffmpeg / Chrome. Em seguida, leia <diretório-do-projeto>/ANIMATION_GUIDE.md na íntegra antes de projetar qualquer coisa: ele contém as regras, os princípios de animação e a API completa do motor / Clawd. Veja docs/emotions.jpg e docs/views.jpg (as folhas de modelo) com a ferramenta de Leitura.
Para um videoclipe ou qualquer coisa com mais de ~20 s, leia também references/music-video.md.
Para todo videoclipe, complete esta triagem antes do storyboard ou implementação de cena:
- Peça ao usuário para fornecer tanto o arquivo de áudio da música quanto seu arquivo de letras
.lrc. Não invente, transcreva ou aproxime letras ausentes quando um LRC é esperado. - Sempre peça a direção criativa, mesmo que o usuário já tenha mencionado parte dela. Pergunte como uma pergunta em linguagem natural em uma única mensagem, cobrindo a história desejada e a sensação geral, além de qualquer ideia visual recorrente ou conteúdo a evitar. Não divida em perguntas separadas, campos, uma lista de verificação ou um formulário de cinco partes. Diga ao usuário que ele pode responder em uma frase ou deixar em branco. Exemplo: "Que tipo de história e sensação geral este videoclipe deve ter, e há alguma imagem recorrente que você queira incluir ou algo que queira evitar? Você pode deixar isso em branco se não tiver preferência."
- Confirme os arquivos recebidos e resuma a direção criativa inferida a partir da única resposta. Internamente, extraia história, emoção, atmosfera/imagética, motivo visual e conteúdo a evitar; não peça ao usuário para responder essas cinco categorias separadamente. Trate uma resposta em branco como "sem preferência", não como entrada faltante, e proponha uma interpretação adequada durante o storyboard. Se o áudio ou arquivo LRC estiver faltando, pause o fluxo de videoclipe e solicite-o.
- Meça o tempo com
scripts/beat_grid.py; as linhas de letras geralmente são mais rápidas do que você imagina. Trate o áudio e LRC fornecidos como a fonte de verdade para o tempo.
1. Storyboard antes do código
Escreva STORYBOARD.md no projeto (formato no guia: logline, arco do mundo + paleta, motivo, arco emocional do personagem, depois tomadas com leituras — a lista temporizada do que o espectador deve entender). Exemplo prático: examples/xiaozhen/ (um videoclipe com letras de 31 s: storyboard e código completo da cena).
Para um videoclipe, transforme a história e o clima do usuário em uma narrativa visual coerente e um arco emocional, em vez de ilustrar cada letra literalmente. Mostre o storyboard ao usuário e deixe-o reagir antes de construir, a menos que ele tenha dito para prosseguir.
2. Construa tomada por tomada
- Defina
duration,bpm,offset(eaudio) emsrc/config.js. - Um arquivo por cena/capítulo em
src/scenes/, envolto em IIFE, terminando comshots([[t0, fn], ...]); adicione seu<script>emstudio.html. - Bloqueie as poses-chave primeiro, verifique-as como imagens estáticas, depois adicione o movimento entre elas.
3. Renderize e veja — cada tomada, várias vezes
Você não pode ver o movimento lendo código. A partir do diretório do projeto:
node render.mjs --sheet=0.1,0.8,1.6,2.4 --cols=4 --w=480 --out=out/check/a.jpg # quadros-chave
node render.mjs --strip=2.1:2.6 --out=out/check/strip.jpg # todos os quadros de um momento
node render.mjs --sheet=2.3 --crop=760,420,500,400 --w=500 --out=out/check/face.jpg # detalhe em resolução total
Abra cada imagem com Leia e verifique-a contra a lista de revisão do guia: evento claro? personagem grande o suficiente? leituras temporizadas para que um espectador de primeira viagem as compreenda? antecipação / acompanhamento, sem cortes bruscos? pés e objetos segurados tocando? transições em cada emenda? sem texto, sem 3D, sem preto/branco puro, sem brilhos turvos? Corrija e veja novamente. Orçamento: ≥ 1 folha por tomada, uma tira por movimento-chave e transição, um recorte por rosto que carrega a história.
Folhas de contato, imagens estáticas, tiras curtas e outras prévias de poucos quadros podem ser renderizadas e revisadas no ambiente cloud atual. Mantenha essas prévias deliberadamente pequenas e direcionadas; elas são para validação visual, não um substituto para renderizar o vídeo completo.
4. Renderize o vídeo
Nunca inicie uma renderização completa diretamente. Após a revisão da prévia ser concluída, diga ao usuário que a renderização completa em cloud pode ser muito lenta e pergunte explicitamente se ele deseja que o vídeo final seja renderizado localmente ou na cloud. Aguarde a resposta; aprovação do storyboard, aprovação da prévia ou uma instrução geral anterior para prosseguir não constituem autorização de renderização.
- Se o usuário escolher renderização local, a renderização completa em cloud é proibida. Não inicie a sequência completa na cloud a menos que o usuário posteriormente mude explicitamente sua escolha. Finalize os arquivos do projeto validados, pergunte pelo sistema operacional local se desconhecido e forneça os pré-requisitos exatos,
npm install, comandos de renderização de quadros e codificação para aquela plataforma. Mantenha as imagens de prévia disponíveis para que o usuário saiba o que foi aprovado. - Se o usuário escolher renderização em cloud, confirme a duração esperada, o número de quadros e o provável custo/tempo, depois prossiga com a renderização retomável com múltiplos workers.
- Se o usuário não responder ou der uma resposta ambígua, não execute uma renderização completa. Pergunte novamente por uma escolha explícita entre local ou cloud.
node render.mjs --clip --out=out/video.mp4 # vídeos curtos, um worker
node render.mjs --frames --workers=4 && node render.mjs --encode --out=out/video.mp4 [--audio=assets/song.mp3] # longos, paralelo + retomável
Relate o caminho de saída, duração e ms/quadro. Ofereça open out/video.mp4.
As regras mais importantes
Estas são as regras condensadas do guia; o guia é a autoridade.
- Meio artesanal. Apenas
paint()/inkLine()(p5.brush) — nuncarect/ellipse/fillsimples do p5. Apenas 2D plano: giros são feitos através de vistas-chave desenhadas (turn(),spinView()), nunca projeção 3D. Luz viaglow()(aditivo), porque a mistura de pigmentos faz amarelo sobre azul ficar verde. - Sem texto. Sem legendas, rótulos, placas ou balões de fala; mostre em vez disso. Reações são emotes pintados (
!, suor, corações…). A única exceção é o karaokê em um videoclipe com letras, e no máximo alguns grandes efeitos sonoros. - Algo acontece em cada tomada — um evento, causa → reação, configurações que se pagam.
- Tempo para o espectador. Liste as leituras de cada tomada; uma leitura de cada vez; ações rápidas, significados sustentados; guie o olhar; as leituras definem a duração da tomada. Esta é a área onde a animação gerada mais falha.
- Vivo. Nada está parado (movimentos de espera de
feel(), câmeras à deriva, agitação); rostos mudam através deemotions()nunca por troca; o personagem é grande (plano médio u≈20–28, close-up 40–70); tudo segue o ritmo. - Transições em cada emenda, escolhidas para se adequar à história (varredura de pincel, íris / íris modelada, chicote, corte correspondente, corte em ação, arrasto de câmera).
- Uma peça. Um mundo, um arco de cor, um fio emocional, cenas ligadas, um final que rima com a abertura.
Peculiaridades do motor
- Funções puras de
t. Quadros renderizam em paralelo e fora de ordem: sem estado entre quadros, semMath.random(), sem física quadro a quadro.hash(i)para aleatoriedade estável,jit()para agitação. boilSeed(key)antes de cada elemento de fundo/prop separado, ou uma coisa em movimento faz tudo desenhado depois dela reagitar a cada quadro (tremulação).- Não nomeie globais como as do p5. p5 define
line,OVERLAY,text,color,scale, … como globais; umafunction line()de nível superior falha comCannot redefine propertye a página nunca fica pronta (o renderizador expira). Mantenha ajudantes dentro da IIFE da cena ou dê a eles nomes distintos. - Camadas em portas, janelas e telas: desenhe o que está dentro, depois pinte a parede/moldura ao redor de volta sobre ele, para que nada mais largo que a abertura apareça fora dela (o
inDoordo trem + batentes, as peças da moldura da TV). - Um
NaNem uma lista de pontos lançaFailed to construct 'OffscreenCanvas'— proteja geometria degenerada. - Sob zoom de câmera ≳ 2, formas enormes perdem contornos; o peso do contorno escala com o zoom, então reduza
swem close-ups. - Custo = número de preenchimentos e traços. Mire em ≤ ~1,5–2,5 s/quadro (o log imprime ms/quadro). Prefira menos formas, maiores. Em máquinas sem uma GPU real, preenchimentos de aquarela são lentos — considere menos formas de
fill, ou--soft-glcomo último recurso. - macOS: o renderizador usa
--use-angle=metale encontra/Applications/Google Chrome.appautomaticamente; caso contrário, passe--chrome=<caminho>.
Adaptando
- Clawd é o astro padrão, não um requisito. Novos personagens, props e emoções são desenhados com as mesmas ferramentas (
paint,ribbon,through,glow) e mantidos no modelo entre tomadas; novas emoções vão emEMOemsrc/clawd.js. src/karaoke.js(template) adiciona karaokê palavra por palavra para videoclipes com letras via o hookwindow.overlayHookemcore.js.LOOPS.name = t => {...}; LOOPS.name.len = 4;+node render.mjs --loop=name --pngfaz um loop contínuo para GIFs (ffmpeg -i out/loop_name/f%04d.png …).- Maior esforço de raciocínio tende a dar cenas mais extravagantes e detalhadas.