O que motionkit faz?
Um filme é uma página mais um script cujo renderFrame(f) desenha o quadro f (30 por segundo, 1920×1080) e nada mais. O Chrome oculto avança os quadros, o ffmpeg codifica (o mp4 é dobrado para 60 quadros por segundo em alta qualidade, cerca de 4 a 5 MB por segundo de filme), a trilha sonora é sintetizada.
Onde as coisas estão
- Motor: a pasta
engine/ao lado deste arquivo (movimentos, renderizador, som, verificações, fontes). Chame-a de$Ee use seu caminho completo. Leia a partir dela; nunca coloque filmes nela (a configuração adiciona seu próprionode_moduleslá, o que é esperado). - Pasta de trabalho: a pasta em que o usuário está trabalhando. Filmes (
<nome>.html,<nome>.js),assets/,ref/eout-<nome>/vão todos para lá, e todos os comandos são executados a partir dela. Uma página encontralib.js,moves.jsefonts/no motor por si só. Se a pasta de trabalho for um repositório git, adicioneout-*/eref/ao seu.gitignore.
Primeiro: configurar (uma vez por máquina)
- Execute
sh $E/setup.sh. Ele apenas verifica: bun, ffmpeg com ffprobe, python3, uv, um navegador Chromium (Chrome, Chromium ou Edge;CHROME_PATHsobrescreve) e o único pacote do motor. - Se imprimir
Ready, prossiga. Se apenasengine packageestiver faltando (normal em uma instalação nova), executesh $E/setup.sh --installsem perguntar: ele só baixa um pacote para a própria pasta da skill. Se uma ferramenta ou o navegador estiverMISSING, mostre ao usuário essa lista e os comandos de instalação que foram impressos, e pergunte uma vez antes de instalar software em sua máquina. - Em caso positivo, execute
sh $E/setup.sh --install(Homebrew no macOS, os instaladores oficiais para bun e uv), depois execute a verificação novamente. Linhas que dizemrun this yourselfprecisam da senha do usuário (sudono Linux) ou de um download manual: nunca as execute; forneça ao usuário o comando exato e aguarde. Se uma instalação falhar, forneça ao usuário o único comando para executar por conta própria. - Prove que funciona: a partir da pasta de trabalho, execute
bun $E/build.ts helloe confirme queout-hello/hello.mp4existe.
Testado apenas no macOS; informe isso se o usuário estiver no Linux ou Windows.
Exemplos prontos para copiar: $E/hello.js (um filme em uma grade de batidas, todo som de clipes, cenário desenhado em código), $E/title.js (um título transparente para sobrepor a filmagens), $E/moves-demo.js (cada movimento uma vez).
Construir um filme: clipes primeiro
Declare cada uso de um movimento uma vez como um clipe. A imagem, o som e a verificação de tempo de leitura vêm todos dessa única linha.
const M = window.MOVES, B = M.brand, G = M.grid(120); // 120 bpm: uma batida são 15 quadros, um compasso 60
const K = { line: M.clip("words", G.bar(1) + 2, 10, { text: "Copie o movimento.", x: 960, y: 590, size: 110, fam: B.display, stretch: B.wide, weight: 600, color: B.paper, until: G.bar(2) }) };
// em uma cena: K.line.draw(X, f) K.line.on(f) é verdadeiro enquanto deve estar visível
window.timeline = () => ({ fps: 30, total: TOTAL, bpm: G.bpm, sections: [[0, "intro"], [4, "construção"], [8, "drop"], [24, "outro"]], cues: M.cues(), captions: M.captions() });
Para um vídeo de lançamento ou produto, leia "Vídeos de lançamento e produto" abaixo primeiro. Inicie um filme com bun $E/new.ts <nome>: ele escreve <nome>.html e <nome>.js na pasta de trabalho, copiados do exemplo hello. Reescreva os clipes, cenas e texto. bun $E/build.ts <nome> então faz, em ordem: linha do tempo, trilha sonora (sound.py), verificação de som, verificação de tempo de leitura (lint.py), renderização, junção, verificação de sincronismo (sync.py) e escreve out-<nome>/<nome>.mp4. Uma linha do tempo sem bpm recebe uma trilha sonora mais simples de audio.py (apenas cortes). Opções da linha do tempo: sound: "fx" (apenas sons dos movimentos, sem música), sound: "none", alpha: true (fundo transparente, saída .mov; veja a seção EditMax).
Anatomia de um script de filme
$E/hello.js é o modelo; leia-o uma vez. O que cada parte é:
- Globais de
lib.js:W,H(1920, 1080),X(o contexto do canvas),clamp,lerp,TAU,hash,noise,txt(ctx, text, x, y, size, weight, color, fam, italic, align),rr(retângulo arredondado),vignette(strength, innerRadius). M.clip(move, start, length, options):startelengthsão quadros;lengthé quanto tempo o movimento leva para ser executado, não quanto tempo ele fica. Retorna{ draw(ctx, f), t(f) (0..1 durante o movimento), on(f), o (as opções) }. Após sua duração, um clipe continua desenhando seu estado final: ele não se esconde. Esconda-o terminando a cena, ou comif (K.x.on(f)), ondeoné verdadeiro destartatéuntil(para sempre semuntil). Para mostrar um movimento de texto finalizado dentro dofromde uma transição, desenhe-o emt = 1:M.words(ctx, 1, K.line.o). Efeitos únicos (burst,glitchCut) precisam também deK.x.t(f) < 1.- Formas de chamada dentro de um clipe: transições recebem
frometonas opções; wrappers (punch,shake,sweep,smear) recebemdraw. Todo o resto (pathDraw,burst,bars,chart,callout,logoDraw,bounce,ticker) recebe apenas opções.mesheparallaxsão fundos: chame-os diretamente,M.mesh(ctx, f / 240, { colors }), ondeté apenas um número que cresce lentamente. - Opções de texto:
xé o centro da linha (align: "center"é o padrão),ya linha de base.stretch: B.widedá o corte largo,stretch: nulldesliga. Nada mede ou quebra texto para você: no tamanho 150 largo, 13 caracteres ocupam cerca de 1.340 px, então renderize um quadro fixo e verifique se cada linha cabe dentro do quadro com margem. - Tabela de cenas:
[primeiro quadro, draw(f), drawAfterFinish(f)].renderFrameredefine o estado do contexto (mantenha essa linha), desenha a cena atual, depois granulação e vinheta, depois o terceiro slot para qualquer coisa que deva permanecer limpa (logotipos). window.ready: uma promessa que o renderizador espera:Promise.all([M.fonts(), M.levels("out-<nome>/levels.json"), M.load({ ... })]).M.levelsé necessário apenas paraM.eqe é seguro antes de o arquivo existir.- Linha do tempo:
sectionssão[batida, nome]; um filme curto pode usar apenas[[0, "intro"], [8, "drop"], [16, "outro"]], e cada início dedroprecebe o meio-tempo silencioso (mantenha ou coloque clipesmute:lá).synclista quadros onde um corte e uma batida grave devem coincidir: use o primeiro quadro de cada cena a partir do drop. "Mudança" é julgada contra a diferença quadro a quadro usual do filme, então uma transição que termina exatamente em um quadro de sincronismo não deixa nada para mudar lá e a verificação imprimeNO CHANGE: adicione um flash curto ou inicie o próximo movimento nesse quadro.cutsé cada início de cena. - Cores: leia tudo de
M.brand(B).M.rgba(B.ink, 0.7)dá uma cor com uma opacidade,M.mix(a, b, t)uma cor entre duas.M.setBrandmudaM.brandno lugar; chame-o antes de qualquer clipe ser declarado e definaaccentOnDarktambém (o acento usado em fundos escuros; ele não segueaccent). Para uma fonte sem corte largo, definawide: null. - Duração: o tempo de leitura define o mínimo: uma linha de 3 palavras precisa de cerca de 1,3 s depois de aparecer, então uma linha por compasso a 120 bpm. A trilha sonora vai cerca de meio segundo além do último quadro e é cortada ali; termine em uma seção
outropara que o corte caia em sua cauda. - Resultados da construção:
build.tssai com 0 mesmo quando uma verificação falha; falhas imprimem como linhasWARNING:com o detalhe acima delas. Trate cada aviso como uma falha: leia a saída (filtre as linhas de progressoquadro n/total), corrija, reconstrua até que não haja nenhuma. Quadros fixos são escritos comoout-<nome>/fNNNN.jpg; leia-os.bun $E/pure.ts <nome>confirma que os quadros renderizam iguais sozinhos e após outros quadros. sound.pyprecisa de numpy e é sempre executado através deuv run; os outros scripts Python usam apenas a biblioteca padrão.
Vídeos de lançamento e produto: mostre o produto
Um filme de palavras grandes em um fundo parece básico. Vídeos de lançamento que funcionam mantêm o produto na tela em um mundo visual único com profundidade e uma câmera que se move lentamente, usam texto pequeno e breve, e seguram os planos por 2 a 6 segundos (alguns compassos), não um corte por batida. Para um vídeo de lançamento ou produto, NÃO comece de hello:
- Decida o visual primeiro: um mundo de fundo, uma cor de destaque, uma família tipográfica. Diga ao usuário em duas linhas.
- Storyboard de 6 a 8 planos de coisas reais: a tela ou saída real do produto, comandos reais, código real, resultados reais. Nunca desenhe uma interface que o produto não possui, nunca invente números e nunca use o logotipo de outra empresa. As legendas são pequenos rótulos (
txt, mono, canto inferior esquerdo), não manchetes. - Construa um plano herói primeiro, renderize quadros fixos e compare com um vídeo de lançamento real antes de construir o resto.
A página deve carregar stage.js após moves.js (páginas feitas por new.ts fazem isso). Inicie renderFrame com M.frame(); M.reset(X);. Peças de palco (todas em MOVES):
| peça | o que é |
|---|---|
backdrop(ctx, f, { base, lights[], streak }) | quase preto com piscinas lentas de luz: o mundo em que tudo se assenta |
glass(ctx, x, y, w, h, { r, top, bottom, rim, shadow }) | um painel arredondado escuro com um aro de luz e uma sombra suave |
camera(ctx, cam, draw) com cam(a, b, t, ease) | olhe para um ponto de uma cena maior: { x, y, zoom, rot }; comece macro-perto e recue, ou deslize lentamente |
tilt(ctx, draw, { depth, focus, blur, fog, top, height, width, cx, roll }) | desenha uma cena em quadro inteiro como um plano inclinado, borda distante menor, mais escura e fora de foco |
tiles(ctx, f, { cells: [{ draw(ctx, t), label, bg, len, hold }], cols, x, y, w, gap }) | uma grade de telas pequenas, cada uma executando um movimento ao vivo (nunca coloque uma transição dentro de um tile) |
term(ctx, f, { x, y, size, lines: [{ text, at, speed, prompt, mark: "ok" ou "run", color }] }) | linhas de terminal que digitam ou aparecem em seu quadro |
code(ctx, { lines[], x, y, size, lead, hot, shown }) | JavaScript com cores de sintaxe suaves e uma linha iluminada |
wave(ctx, { peaks, x, y, w, h, played, beats[], blocks[] }) | a forma de uma trilha sonora real com um cursor de reprodução, marcas de batida e clipes em uma faixa |
Peças de palco não fazem som próprio: adicione cues manualmente à linha do tempo, { f, kind, gain } com tipos como type, tick, pop, ding.
Cortar para uma música real
- O usuário fornece o arquivo de música e deve ter o direito de usá-lo; diga isso e coloque o crédito que uma licença pedir no cartão final. Nunca baixe uma música de um site de streaming.
uv run $E/beats.py <arquivo>imprime o tempo e o quão alto cada compasso é. Escolha um trecho que comece em um compasso com uma mudança de seção alguns compassos depois, entãouv run $E/beats.py <arquivo> --from <segundos> --len <segundos> --out beats.jsonescrevebeats.jsonebeats.js.- Carregue
beats.jsna página antes do script do filme. Escreva cada início de cena como um número de batida:const b = (n) => window.BEATS.frames[n]. Corte em compassos (a cada 4 batidas), onde a faixa muda. - Na linha do tempo, defina
music: { file, from },sound: "fx"(os sons dos próprios movimentos permanecem, baixos, sob a música) ou"none", umbpm, esync: [quadros dos cortes reais]. A construção mistura a faixa a -16 LUFS, desvanece seu final e verifica se cada corte tem uma batida grave dentro de um quadro. Não adicione flashes apenas para passar nessa verificação.
Movimentos para o EditMax (um clipe na linha do tempo de um editor de vídeo)
Use isso quando o usuário estiver editando no EditMax e quiser um movimento no corte: um título animado sobre a filmagem, um cartão de intro ou outro, um tipo cinético entre as cenas. Precisa de duas coisas na mesma sessão do agente: um shell (para renderizar) e as ferramentas MCP do EditMax (inspect_timeline, import_media, ...; configurado a partir da aba Agentes do EditMax). O chat no aplicativo do EditMax recusa comandos de shell, então não pode executar esta skill: diga isso e instrua o usuário a conectar um agente como o Claude Code. Se as ferramentas do EditMax estiverem faltando ou não responderem, renderize o clipe de qualquer maneira e forneça ao usuário o caminho do arquivo para arrastar.
1. Escolha a rota. Prefira as próprias ferramentas do EditMax quando elas cobrirem o movimento, porque o texto permanece editável no aplicativo:
| desejado | use |
|---|---|
| um título ou terço inferior que desaparece, desliza, aparece, digita ou varre | add_text / add_lower_third + set_text_animation |
| um dissolve, push, slide, wipe ou iris entre dois clipes | set_transition |
| um clipe que se move, escala ou desaparece | set_keyframe, set_clip_transform, set_clip_fades |
| qualquer outra coisa: slam, scramble, split-flap, word wheel, cross-out, highlight, path ou logo draw, charts, counters, bursts, sequências de múltiplos movimentos, um intro ou outro completo | renderize um clipe com esta skill |
2. Leia o projeto. inspect_timeline: observe o tamanho do quadro, a taxa de quadros, as faixas e o tempo onde o movimento vai. Este motor renderiza 1920×1080 a 30 quadros por segundo. Um projeto 16:9 de outro tamanho o escala limpidamente. Para um projeto vertical ou quadrado, diga claramente que o clipe será letterboxed ou deve ser escalado e cortado com set_clip_transform, e que um título nativo pode ser melhor.
3. Renderize.
- Sobreposição (fica em cima da filmagem):
bun $E/new.ts <nome> --overlay, modelado em$E/title.js. A linha do tempo temalpha: trueesound: "fx"; a construção escreveout-<nome>/<nome>.mov(ProRes 4444 com fundo transparente). Regras: limpe o quadro (X.clearRect), nunca o preencha; semgrain,vignette,mesh,parallaxou transições de quadro inteiro; coloque uma sombra suave ou uma placa sob o tipo para que ele seja legível em qualquer filmagem; mantenha o tipo dentro dos 90% centrais do quadro; desapareça-o no final. - Quadro inteiro (intro, outro, um cartão entre cenas):
bun $E/new.ts <nome>, modelado em$E/hello.js; a construção escreveout-<nome>/<nome>.mp4. - Som (defina
sound:na linha do tempo): omita para a trilha sonora completa (um intro ou outro autônomo);"fx"para apenas os sons que os movimentos fazem (qualquer coisa que toque sobre filmagem, fala ou música do usuário);"none"para silêncio. Pergunte qual o usuário deseja se a edição já tiver música. - Construa, corrija cada
WARNINGe leia os quadros fixos antes de continuar.
4. Coloque o arquivo em algum lugar que dure. O EditMax referencia arquivos importados, não os copia. Copie o arquivo finalizado de out-<nome>/ para uma pasta que o usuário mantenha (pergunte uma vez; sugira ~/Movies/Motions/) e importe de lá.
5. Coloque-o sem mover o corte do usuário. insert_clip com um tempo é uma separação