Communitygithub.com

pintor-de-cena/painter-skill

Pinte cenas em código e transforme-as em vídeo com aparência de pintura a óleo, usando pinceladas reais, mistura espectral de pigmentos e animação de revelação das pinceladas.

O que é painter-skill?

Esta skill permite que um agente de IA (como Claude Code, Cursor ou Codex) crie vídeos animados com aparência de pintura a óleo a partir de descrições textuais ou de cena. O sistema não aplica filtros sobre renders: cada marca visual é uma pincelada real posicionada pelo código, construindo a imagem camada por camada (do fundo para a frente). O resultado final pode incluir uma fase de revelação (a pintura sendo feita pincelada a pincelada) e uma fase de "pintura viva", com vento, nuvens, água cintilante e câmera com paralaxe. A stack técnica utiliza WebGPU/WGSL para renderização de pinceladas no Chrome headless, WebCodecs + Mediabunny para exportação MP4 e ffmpeg para garantia de qualidade. O usuário deve descrever a cena (assunto, clima, hora do dia, formato, duração) e o agente planeja a composição e paleta antes de gerar o código, seguindo regras de pintura como massas de valor, pigmentos limitados e variação de pinceladas. A skill se baseia em demonstrações de pintura reais e medições de obras-primas em domínio público, armazenadas em `references/`. O fluxo de trabalho enfatiza a revisão visual em múltiplas rodadas: renderizar um still, analisar como uma pintura e ajustar o código até que a composição, os valores e a paleta estejam corretos.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ninjasweb/painter-skill/tree/HEAD

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Vídeo pictórico (painterly video)

Você pinta com código da mesma forma que um pintor pinta com um pincel: decida primeiro a imagem (composição, valores, paleta), depois aplique pinceladas reais de trás para frente, olhe o resultado e ajuste. O motor transforma essas pinceladas em quadros; nada é um filtro sobre um render — cada marca é uma pincelada que você colocou.

O método foi aprendido a partir de quatro demonstrações de pintura (processo, pinheiro, árvores pictóricas, nuvens de pôr do sol) e da medição de 13 paisagens mestras em domínio público. As lições destiladas estão em references/; o código funcional está em engine/ e examples/.

Configuração (uma vez por projeto)

cp -R /caminho/para/painterly-video/engine ./painter-engine
cd painter-engine
npm ci
node bin/paint.mjs still ../caminho/para/cena.js --t 8          # → PNG ao lado da cena (stills/<cena>/)

Substitua /caminho/para/painterly-video pela pasta que contém este SKILL.md (por exemplo ~/.claude/skills/painterly-video). Precisa de Node ≥ 22.12, Google Chrome com WebGPU (ou CHROME_PATH), ffmpeg e ffprobe. Estudos de referência adicionalmente precisam de Python com tools/requirements.txt. Nenhuma etapa de build ou chaves de API de renderização; a renderização roda no hardware local. Inicie uma nova cena copiando o arquivo mais próximo em examples/ (sunset-river.js céu/nuvens/água · lone-pine.js vertical, sujeito único · lake-path.js árvores/profundidade/caminho · sunset-study.js céu dramático de massas escuras de nuvens com luz de contorno (um estudo da estrutura de uma demonstração) · mountain-hamlet.js montanhas, cabana, rochas, figura caminhando, fumaça · brush-chart.js pincéis e misturas).

Fluxo de trabalho — não pule as etapas de observação

  1. Briefing. Sujeito, clima, hora do dia, formato (16:9, 9:16, 1:1 — qualquer --size), duração e como o tempo é dividido: revelação da pintura, pintura viva, ou ambos (padrão: ~60 % revelação, ~40 % viva).
  2. Estude uma referência quando existir (tools/study.py, veja references/composition.md). Ela relata massas de valor, key, croma, paleta e ponto focal — copie a estrutura, nunca a imagem.
  3. Componha antes da cor. Escreva a composição como um comentário no topo da cena: altura do horizonte (nunca no meio), ponto focal (em um terço, onde vão o contraste de valor mais forte e o croma mais alto), a entrada, o que emoldura, e um plano de valores — um valor (0–1) para cada forma grande, em três massas desiguais. references/composition.md.
  4. Paleta a partir de pigmentos, colocada no plano de valores. tone(mix({ ultramarine: 1, transYellowOxide: 1 }), 0.42). Mantenha o croma baixo em todos os lugares, exceto no acento focal (mestres: croma OKLab médio 0.02–0.06, área de alto croma abaixo de ~8 %).
  5. Pinte de trás para frente com os pintores de sujeito (paintSky, paintCumulus, paintHills, paintMountains, paintWater, paintTreeline, paintPine, paintFir, paintTree, paintGrass, paintRocks, paintHouse, paintGlow, paintMoon, walker, boat, smoke…) e layer.scatter/stroke/path bruto para qualquer outra coisa. Uma p.phase('nome') por estágio, camadas criadas de longe → perto. references/api.md.
  6. Olhe, critique, ajuste — pelo menos duas rodadas. Renderize um still, abra o PNG e julgue como uma pintura: aperte os olhos (as três massas de valor são legíveis?), o ponto focal está onde planejado, algo está igualmente espaçado / igualmente dimensionado / igualmente saturado, alguma marca parece mecânica? Corrija a causa no código. Execute study.py no seu próprio still e compare com o plano. Este loop é a habilidade; primeiros renders nunca estão certos.
  7. Tempo. p.timelapse() para a revelação, depois movimento: sway, drift, shimmer, p.wind, p.camera (paralaxe por profundidade da camada), p.dynamic para coisas que viajam. references/animation.md.
  8. Folha de contato, depois renderize. still --n 12 --sheet → verifique cada estágio da linha do tempo; depois render → qa. references/pipeline.md.
  9. Relate honestamente: o que foi verificado automaticamente, o que você olhou, o que você não olhou.

Esqueleto da cena

import { Painting, mix, tone, mixOk, skyColor, paintSky, ridge, paintHills, paintGrass, key, ease } from 'painter';
export const meta = { duration: 16, fps: 30 };

export default function ({ aspect, seed }) {            // aspect = largura / altura do render
  const p = new Painting({ aspect, seed, ground: '#c9b89a' });   // tela tingida, nunca branca
  p.duration = 16;
  const A = aspect;                                     // unidades da cena: x 0…A, y 0 (topo) … 1 (fundo)

  const skyTop = tone(mix({ ultramarine: 2, titaniumWhite: 2 }), 0.72), skyLow = tone(mix({ cadmiumOrange: 1, titaniumWhite: 3 }), 0.88);
  const hillCol = tone(mix({ ultramarine: 1, burntUmber: 1 }), 0.3);
  const hill = ridge({ y: 0.68, amp: 0.05, freq: 2, seed });

  p.phase('sky');
  paintSky(p.layer('sky', { depth: 0 }), { color: skyColor({ top: 0, bottom: 0.7, stops: [[0, skyTop], [1, skyLow]] }), bottom: 0.72 });
  p.phase('land');
  paintHills(p.layer('hill', { depth: 0.6 }), { top: hill, color: (x, y, k) => hillCol, size: 0.03 });
  paintGrass(p.layer('grass', { depth: 1 }), { bounds: [0, 0.8, A, 1.05], dark: hillCol, light: tone(hillCol, 0.5), blade: 0.05 });

  p.timelapse({ start: 0.3, end: 10 });                 // imagem finalizada aos 10 s
  p.camera = (t) => ({ zoom: key(t, [[10, 1], [16, 1.07]], ease.inOut) });
  p.wind = (t) => ({ strength: key(t, [[0, 0], [10, 0.15], [12, 1]]) });
  return p;
}

Regras que fazem parecer pintado (a lista resumida)

  1. O valor faz o trabalho, a cor leva o crédito. Planeje três massas de valor desiguais; teste de apertar os olhos em cada still.
  2. Tela tingida, nunca branca. Escolha a imprimatura para harmonizar a imagem (siena quente sob verdes, creme claro sob um pôr do sol).
  3. Distância primeiro, depois para frente. Longe = mais claro, mais frio, mais cinza, mais macio, tinta mais fina, marcas menores. Perto = escuros mais escuros, maior contraste e croma, tinta mais grossa, marcas maiores. Use atmosphere(cor, d, ar).
  4. Escuros primeiro e finos, claros por último e grossos. thick 0.1–0.3 para sombras e distância, 0.8–1 para realces; realces são pequenos e poucos.
  5. Claro contra escuro, escuro contra claro. Quebre cada massa com marcas de um pequeno passo de valor (±0.05) do que está embaixo; p.valueAt(x, y) / p.sample(x, y) dizem o que está lá.
  6. Pinceladas seguem a forma: ao redor de um aglomerado de nuvem, ao longo de uma encosta, plano na água, para cima em uma lâmina de grama, para baixo em um tronco.
  7. Misture verdes e os amarronze (earth(mix({ ultramarine: 1, cadmiumYellow: 1 }), 0.3)); reserve croma puro para um acento.
  8. Varie tudo: tamanhos, espaçamentos, alturas, intervalos. Igual = mecânico. Grupos ímpares, formas sobrepostas, um dominante.
  9. Arestas perdidas e encontradas: suaves onde as formas se viram ou recuam (soft 0.5+), nítidas no ponto focal.
  10. Função pura do tempo. Uma cena é determinística a partir de seed; um quadro depende apenas de t. Nunca use Math.random() ou tempo de relógio — use layer.rand / rng(seed).

Onde as coisas dão errado (aprendido construindo isso)

  • O branco engole as misturas. O misturador espectral pondera pigmentos pela luminância; uma mistura 1:9 cor:branco é quase branca. Misture o matiz com pouco ou nenhum branco, depois coloque com tone(c, valor). Corte resultados berrantes com chroma(c, 0.5–0.8).
  • Nuvens como bolas com gravetos → muitos lóbulos pequenos em uma base plana (cumulusShape lóbulos 40–60), luz apenas no lado voltado para o sol, pinceladas suaves redondas, thick modesto.
  • Pinheiro como palmeira → agulhas são aglomerados arredondados (explosões de pinceladas curtas em leque) sobre os galhos com buracos de céu entre eles, escuro embaixo, claros em cima; galhos curvam para baixo e alcançam para cima.
  • Árvores com galhos de guarda-chuva / troncos pretos → os ramos devem terminar dentro da copa; escuros do tronco são valor ~0.22, não preto, e recebem ar (atmosphere) com a distância.
  • Uma área inteira em cor saturada (chão laranja, céu turquesa) → amarronze; espalhe a cor pura como algumas poucas marcas pequenas.
  • Caminho parece rio → cinza claro quente, pinceladas ao longo da direção do movimento, violeta frio apenas em suas sombras.
  • Água feita de traços curtos parece digital → água são longos arrastos horizontais mesclados entre si, com apenas algumas marcas de ondulação perto do observador (o que paintWater faz); não adicione fileiras de traços manualmente.
  • Mar parece espelho vítreo → paintWater({ chop: 1 }) para dorso de ondas e cristas brancas; mantenha chop: 0 para lagos e rios.
  • Luz de lâmpada / janela é uma mancha plana → paintGlow sobre um entorno escuro, depois o pequeno núcleo quente; uma longa pincelada suave não é um brilho.
  • Raios como espinhos brancos → poucos, largos, muito suaves, feixes de baixa opacidade (padrões de paintRays).
  • Trama da tela se transforma em grade em tinta fina e clara → mantenha look.weave ≤ 0.2.
  • Montanhas como parede escamosa ou listras de zebra → construa o horizonte com peaks(), mantenha a cordilheira distante (tinta fina, size ≈ 0.02, faixa de valor estreita) e deixe paintMountains dividi-la em alguns poucos planos grandes iluminados e sombreados.
  • Uma faixa plana não é um banco de nuvens / colina / linha de árvores → varie a borda superior, os valores internos e deixe lacunas.
  • A cena falha com um erro vago do navegador → a CLI verifica a sintaxe primeiro; um erro em tempo de execução imprime a linha da cena. Uma variável local sombreando uma cor (leaf, sky, hill) é a causa usual de c.map is not a function.
  • Lendo um still desatualizado: sempre verifique a saída do comando em busca de um erro antes de abrir o PNG.
  • WebGPU e WebCodecs só existem em uma origem segura — a CLI serve http://127.0.0.1; nunca abra cenas via file://.

Arquivos de referência

  • references/painting-lessons.md — o que cada demonstração ensina e como mapeia para chamadas do motor.
  • references/composition.md — compondo uma cena, o plano de valores, números dos mestres, study.py.
  • references/api.md — referência de Painting / camada / pincel / cor / pintor de sujeito, escrevendo um novo pintor de sujeito.
  • references/animation.md — ritmo de revelação, movimento vivo, câmera e paralaxe, camadas dinâmicas, ferver, áudio.
  • references/pipeline.md — CLI, configurações de render, desempenho, QA, solução de problemas, entregáveis.

Habilidades Relacionadas