motion-video
Estúdio na pasta motion-studio/, dentro da pasta aberta no Claude Code (a raiz do projeto, onde fica .claude/). Todos os comandos abaixo rodam de dentro de motion-studio/.
0. Instalação (primeira vez)
Se motion-studio/.venv ou motion-studio/node_modules não existirem, ou se o usuário pedir para "preparar/instalar":
- Rode
bash motion-studio/tools/setup.sh. Ele confere Node, FFmpeg e Python 3, cria.venvcom numpy/scipy, instala o Puppeteer e faz um preview de teste. - Se faltar Node, FFmpeg ou Python, o setup mostra o comando de instalação para o sistema (Mac:
brew, Windows:winget, Linux:apt). Peça permissão antes de instalar qualquer coisa. No Windows, depois dowingeté preciso reabrir o terminal.
Multiplataforma: os scripts rodam em bash, ou seja, no Terminal do Mac/Linux e no Git Bash do Windows (que o Claude Code no Windows já usa). Chame sempre com bash tools/<script>.sh. tools/env.sh detecta o sistema, o Python (python3/python) e a .venv (bin/ ou Scripts/). Só no Mac: cutout.swift e o vigia de RAM do render-chunks.sh.
3. Confirme com o usuário mostrando projects/exemplo/out/preview.jpg.
engine/ engine.core.js (helpers) · engine.runtime.js (montagem/export) · player.html · sfx.py
styles/<estilo>/ style.js (tokens: cores, fontes, fundo, transição, sons) · STYLE.md (gramática visual)
projects/<nome>/ project.js · scenes.js · audio.json · out/ (frames, video.mp4, final.mp4, sheet.jpg)
templates/project/ ponto de partida de projeto novo
tools/ render.sh · render.mjs · check.py · music.py · ref.sh · server.py
1. REGRAS DE MOVIMENTO — valem para TODO estilo e formato
Estas regras ficam acima do estilo. O STYLE.md muda a aparência, nunca estas regras.
- Zero dead holds. Nenhum elemento fica 100% parado esperando o próximo evento.
- Toda cena tem
cam(ctx, f, a, b, 1, 1.05–1.12). - Todo elemento que chegou continua em drift:
driftemwords,- prog(...,E.lin)*Nna posição, uma leve rotação nos cards e um crescimento de 3–7% em pílulas/ícones.
- Toda cena tem
- Uma chegada dura até perto do próximo evento. Use
E.out5/E.smooth/E.push(cauda longa). EviteinOutQ/inOutCem chegadas, porque travam no fim; eles servem só para movimentos de câmera curtos e decididos. - A saída continua o momentum da deriva, na mesma direção e acelerando com
E.inC+ blur. Nunca sumir parado. - Os eventos se sobrepõem. A próxima animação começa 4 a 12 frames antes da anterior terminar.
- Movimento rápido tem motion blur (
smearcom a velocidade). - Transições nascem de um elemento em cena (x/y no
TR). Nada de fade genérico, a não ser que o estilo peça. - Molas macias em cards:
spring({k:80,c:14}). Molas firmes (k:200,c:15) só em pops pequenos. - Loop: o fundo é periódico em N frames, então o frame final pode emendar no frame 0.
- Verificar com dados: o
check.pyprecisa sair sem nenhum "DEAD HOLD — corrigir". "DRIFT LENTO" é aceitável se for intencional.
2. Fluxo (barato em tokens)
- Briefing numa mensagem só. Se faltar algo essencial, pergunte tudo de uma vez: formato, duração, estilo (
ls styles/), marca, textos e cenas, música (caminho e trecho), referência. - Storyboard em texto: uma tabela com cena, frames, headline, demo e transição. Não faça storyboard em HTML. Espere o usuário aprovar.
- Projeto:
cp -r templates/project projects/<nome>. Editeproject.jse escrevascenes.jsreutilizando a API (seção 4). Leia só oSTYLE.mddo estilo escolhido. Não leia oengine.core.jsinteiro: a API está abaixo. - Preview barato:
tools/render.sh <nome> preview. Ele geraout/preview.jpg(1 quadro a cada 30 frames, em 1/3 da resolução). Veja essa única imagem e corrija layout e timing. 4b. Storyboard em imagem (sempre mostre antes de renderizar o vídeo final): definaBOARD=[[frame,'título','nota'],...]no scenes.js e rodetools/render.sh <nome> board→out/board.png(uma imagem só). SEMPRE abra a imagem antes de enviar. - Render final:
tools/render.sh <nome>. Ele faz os frames, o MP4, os sons, a mixagem e o relatório de dead holds em texto. Só abra oout/sheet.jpgse o relatório ou o usuário indicarem problema. - Música: primeiro
python tools/music.py <mp3>(dá o BPM e os drops em texto). Depois, noaudio.json, usealign: {musicTime: <drop>, frame: <clique/momento-chave>}. - Ajustes de som não precisam de re-render:
tools/render.sh <nome> audioleva segundos. - Entregar:
out/final.mp4com SendUserFile.
Robustez:
- Fontes ficam locais em
motion-studio/fonts/(fonts.filesno style.js); o render FALHA se uma fonte cair no fallback. - Footage é carregado sob demanda (não pré-carregar milhares de quadros → estoura memória).
- Render longo: rode em background. Se cair no meio, re-renderize só a faixa:
FROM=a TO=b node tools/render.mjs projects/<nome> full 8777, depois ffmpeg +render.sh <nome> audio. - Recorte de fundo (PNG sem fundo):
swift tools/cutout.swift <entrada> <saida.png>(só macOS; no Windows/Linux peça ao usuário um PNG já sem fundo). - Confira os trechos de footage: cortes de B-roll dentro do clipe aparecem na grade; limite a contagem/
speeddo FOOT.
Regras de economia:
- Edite com
Edit, nunca reescreva oscenes.jsinteiro. - Agrupe os ajustes do usuário numa rodada só.
- Peça e use números de frame.
- Nada de screenshots do player: use
preview.jpg,sheet.jpge o texto docheck.py. - Para Python use o da
.venv(source tools/env.sh→$PY).
3. Novo ESTILO a partir de referência ("treinar estilo")
- Rode
tools/ref.sh <video> <dir> 4e olhe só as gradessheet_*.png(no máximo 2 imagens). - Extraia:
- Paleta (hex), tipografia e fundo
- Gramática: como as cenas se encadeiam, o tipo de transição, a câmera
- Duração por beat e o tipo de easing
- Faça
cp -r styles/tech-premium styles/<novo>e edite:style.js: tokens; os blobs podem ficar coma: 0se o fundo for chapadoSTYLE.md: gramática e durações, no mesmo formato do tech-premium
- Se o estilo precisar de um componente que não existe na engine (ex.: kinetic type 3D, máscaras de forma), adicione ao
engine.core.jscomo função genérica e documente na seção 4. Assim todos os estilos ganham o componente. - A seção 1 continua valendo, sempre.
4. API da engine (globais)
- Constantes:
W H FPS N· coresK(escuro)WH(claro)O(acento)B(contraste)CARD· fontesDISPLAYeMONO - Tempo:
prog(f,a,b,ease)→ 0..1spring(f,start,{k,c})- easings em
E:lin inC outC inOutC inOutQ out5 smooth push cam cb(x1,y1,x2,y2)cria uma curva bezierlerp clamp
- Cena:
bg(ctx,f,dark): fundo do estilocam(ctx,f,a,b,s0,s1): zoom contínuolayer(ctx,alpha,blur,L=>...): alpha/blur de grupo
- Texto:
words(ctx,f,[[txt,cor?],...],{x,y,size,weight,start,stagger,dur,color,exit,drift,am,ls}): entrada palavra por palavra com blurlogo(ctx,f,{start,exit,offset,text})smear(ctx,velocidade,am=>draw(am)): motion blurpenLine(ctx,x,y,w,progresso): sublinhado à mão
- UI 3D: desenhe o card num canvas
mk(w,h)(userr,circle,font(peso,tam,fam)) e chamepersp(ctx,canvas,cx,cy,{ry,rz,s,light}) - Assets / editorial (estilo editorial com fotos e vídeo):
project.js:assets:['x.png'](emprojects/<p>/assets/) ·footage:{nome:N}(gerado portools/footage.sh <p> <nome> <video> <ini> <dur>)IMG('x.png')·FOOT(nome, frameLocal, speed)·cover(ctx,img,x,y,w,h,{s,ox,oy})footage(ctx,f,img,{a,b,s0,s1,bw,edgeBlur,dim}): tela cheia, zoom contínuo, P&B, borda borradastackText(ctx,f,[[txt,frame?,cor?],...],{x,y,size,align,stagger,exit,drift,glitch}): Anton empilhada com glitchcaption(ctx,f,txt,{x,y,size,color,start,exit,shadow}): legenda Helveticacutout(ctx,f,img,{x,y,w,start,rot,orbit,seed,exit,from}): PNG em órbita ·photoCard(ctx,img,cx,cy,w,h,{fill,inset,rot,s,r})vignette(ctx,amt,rgb,inner)·hash(n)(pseudo-aleatório determinístico)- estilo pode ter
vignette:{amt,rgb,inner,breathe,cycles}global
- Objetos reais (ref dnyx; serve para qualquer objeto: ancorado, cortado pela borda, conteúdo troca por corte seco):
phone(ctx,f,{x,y,w,start,from,dist,screen:(g,w,h,f)=>{},exit,exitDir,light}): iPhone realista ·statusBar(g,w,{color})product(ctx,f,img,{x,y,w,start,from,rot,spin,contact,reflect,exit}): PNG do produto com sombra de contatoassemble(ctx,f,[{draw,sc:[x,y,rot],row:[x,y],to:[x,y,s]}],{t:{in,row,go}}): peças soltas → fileira → para dentro do objetopromptPill(ctx,f,txt,{x,y,w,h,start,cps,dark}): campo de prompt digitando ·bigWord(ctx,f,txt,{x,y,size,color}): Anton gigante cortadaedgeIn(f,start,from,dist)· fundo com grid viastyle.bg.grid· o estilo pode definirw/h/fps
- Montagem (
scenes.js):SC=[{a,fn}]·TR=[{a,b,x,y,type?}](circle|fade|wipe|zoomblur|whipcomdir)SFX=[[nome,frame,ganho,pan,{opts}]]PROJECT.initpara pré-cálculos (ex.: miniaturas comTHUMB/BS)
- Sons (
sfx.py):whoosh{dur,f0,f1,peak,panFrom,panTo}pop{f0,f1,dur}click{freq,dur}tickchimeboom{dur}scribble{dur}shimmer{dur}- rajadas
typing{to,step}clicks{to,step} - a transição ganha som automático (
style.sfx.transition) - som novo = função em
GEN
Estilos incluídos: tech-premium (produto/UI 3D, 60fps). Novos estilos são criados com a seção 3.
Exemplo completo de projeto: projects/exemplo/ (15s, 5 cenas, sons sincronizados; para música, preencha audio.json).