Communitygithub.com

Painterly video — Pintura animada al óleo con código

Pinta escenas con código y conviértelas en video — apariencia de óleo construida con pinceladas reales (cerdas, empaste, pincel seco, trama de lienzo), mezcla espectral de pigmentos, una revelación de la pintura (el cuadro pintándose pincelada a pincelada) y una fase de

¿Qué es Painterly video — Pintura animada al óleo con código?

Este skill permite a un agente de IA (como Claude Code) pintar escenas animadas con apariencia de óleo, generando videos a partir de código. Cada marca es una pincelada real, no un filtro sobre un render. El motor utiliza WebGPU/WGSL en Chrome headless para dibujar instancias de pinceladas, y exporta a MP4 mediante WebCodecs y Mediabunny, con control de calidad vía ffmpeg. El flujo de trabajo guía al agente a través de un briefing, estudio de referencias (opcional), composición (masas de valor, paleta desde pigmentos), pintado de atrás hacia adelante con pinceles especializados (cielo, nubes, montañas, agua, árboles, etc.), y múltiples rondas de revisión de imágenes fijas antes del render final con animación (revelado, viento, cámara, ondulaciones). Está diseñado para agentes que necesiten generar videos de aspecto pictórico: escenas animadas estilo speed-painting, fondos animados para historias o videos musicales, o estudios de composición de obras maestras. El skill es autocontenido: no requiere APIs externas ni servicios en la nube; todo se ejecuta en hardware local. Disparadores típicos: 'pintar', 'pintura animada', 'escena pintada', 'óleo', 'painterly', 'speedpaint', 'living painting'.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Video pictórico (óleo animado)

Pintas con código como un pintor con el pincel: decide primero el cuadro (composición, valores, paleta), luego coloca pinceladas reales de atrás hacia adelante, observa el resultado y ajusta. El motor convierte esas pinceladas en fotogramas; nada es un filtro sobre un render — cada marca es una pincelada que tú colocaste.

El método se aprendió de cuatro demostraciones de pintura (proceso, pino, árboles pictóricos, nubes al atardecer) y de medir 13 paisajes maestros de dominio público. Las lecciones destiladas están en references/; el código funcional en engine/ y examples/.

Configuración (una vez por proyecto)

cp -R /ruta/a/painterly-video/engine ./painter-engine
cd painter-engine
npm ci
node bin/paint.mjs still ../ruta/a/escena.js --t 8          # → PNG junto a la escena (stills/<escena>/)

Reemplaza /ruta/a/painterly-video con la carpeta que contiene este SKILL.md (por ejemplo ~/.claude/skills/painterly-video). Necesita Node ≥ 22.12, Google Chrome con WebGPU (o CHROME_PATH), ffmpeg y ffprobe. Los estudios de referencia además necesitan Python con tools/requirements.txt. Sin proceso de compilación ni claves de API de renderizado; el renderizado se ejecuta en hardware local. Inicia una nueva escena copiando el archivo más cercano en examples/ (sunset-river.js cielo/nubes/agua · lone-pine.js vertical, sujeto único · lake-path.js árboles/profundidad/camino · sunset-study.js cielo dramático de masas de nubes oscuras con luz de borde (un estudio de la estructura de una demostración) · mountain-hamlet.js montañas, cabaña, rocas, figura caminando, humo · brush-chart.js pinceles y mezclas).

Flujo de trabajo — no omitas los pasos de observación

  1. Briefing. Sujeto, estado de ánimo, hora del día, formato (16:9, 9:16, 1:1 — cualquier --size), duración y cómo se divide el tiempo: revelado de pintura, cuadro vivo o ambos (por defecto: ~60 % revelado, ~40 % vivo).
  2. Estudia una referencia cuando exista (tools/study.py, ver references/composition.md). Reporta masas de valor, clave, croma, paleta y punto focal — copia la estructura, nunca el cuadro.
  3. Componer antes que color. Escribe la composición como comentario al inicio de la escena: altura del horizonte (nunca en el medio), punto focal (en un tercio, donde van el contraste de valor más fuerte y el croma más alto), la entrada, qué lo enmarca y un plan de valores — un valor (0–1) para cada forma grande, en tres masas desiguales. references/composition.md.
  4. Paleta a partir de pigmentos, colocada sobre el plan de valores. tone(mix({ ultramarine: 1, transYellowOxide: 1 }), 0.42). Mantén el croma bajo en todas partes excepto en el acento focal (maestros: croma medio OKLab 0.02–0.06, área de alto croma bajo ~8 %).
  5. Pintar de atrás hacia adelante con los pinceles de sujeto (paintSky, paintCumulus, paintHills, paintMountains, paintWater, paintTreeline, paintPine, paintFir, paintTree, paintGrass, paintRocks, paintHouse, paintGlow, paintMoon, walker, boat, smoke…) y layer.scatter/stroke/path en bruto para cualquier otra cosa. Una p.phase('nombre') por etapa, capas creadas de lejos a cerca. references/api.md.
  6. Observa, critica, ajusta — al menos dos rondas. Renderiza un fotograma fijo, abre el PNG y evalúalo como una pintura: entrecierra los ojos (¿se leen las tres masas de valor?), ¿está el punto focal donde se planeó?, ¿hay algo igualmente espaciado / del mismo tamaño / igualmente saturado?, ¿alguna marca se ve mecánica? Corrige la causa en el código. Ejecuta study.py sobre tu propio fotograma y compáralo con el plan. Este bucle es la habilidad; los primeros renders nunca son correctos.
  7. Tiempo. p.timelapse() para el revelado, luego movimiento: sway, drift, shimmer, p.wind, p.camera (paralaje por profundidad de capa), p.dynamic para cosas que se desplazan. references/animation.md.
  8. Hoja de contactos, luego render. still --n 12 --sheet → revisa cada etapa de la línea de tiempo; luego render → qa. references/pipeline.md.
  9. Informa con honestidad: qué se revisó automáticamente, qué observaste, qué no.

Esqueleto de escena

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 = ancho / alto del render
  const p = new Painting({ aspect, seed, ground: '#c9b89a' });   // lienzo entonado, nunca blanco
  p.duration = 16;
  const A = aspect;                                     // unidades de escena: x 0…A, y 0 (arriba) … 1 (abajo)

  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 });                 // cuadro terminado a los 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;
}

Reglas que lo hacen lucir pintado (la lista breve)

  1. El valor hace el trabajo, el color recibe el crédito. Planifica tres masas de valor desiguales; prueba de entrecerrar los ojos en cada fotograma.
  2. Fondo entonado, nunca blanco. Elige la imprimatura para armonizar el cuadro (siena cálida bajo verdes, crema pálido bajo un atardecer).
  3. Lejos primero, luego adelante. Lejos = más claro, más frío, más gris, más suave, pintura más delgada, marcas más pequeñas. Cerca = oscuros más profundos, mayor contraste y croma, pintura más espesa, marcas más grandes. Usa atmosphere(color, d, air).
  4. Oscuros primero y delgados, claros al final y espesos. thick 0.1–0.3 para sombras y lejanía, 0.8–1 para brillos; los brillos son pequeños y escasos.
  5. Claro contra oscuro, oscuro contra claro. Rompe cada masa con marcas de un pequeño paso de valor (±0.05) respecto a lo que hay debajo; p.valueAt(x, y) / p.sample(x, y) te indican lo que hay.
  6. Las pinceladas siguen la forma: alrededor de un cúmulo de nubes, a lo largo de una pendiente, planas sobre el agua, hacia arriba en una brizna de hierba, hacia abajo en un tronco.
  7. Mezcla verdes y tierrálos (earth(mix({ ultramarine: 1, cadmiumYellow: 1 }), 0.3)); guarda el croma puro para un solo acento.
  8. Varía todo: tamaños, espaciados, alturas, intervalos. Igual = mecánico. Grupos impares, formas superpuestas, un dominante.
  9. Bordes perdidos y encontrados: suaves donde las formas se alejan o retroceden (soft 0.5+), nítidos en el punto focal.
  10. Función pura del tiempo. Una escena es determinista a partir de seed; un fotograma depende solo de t. Nunca uses Math.random() ni el tiempo real — usa layer.rand / rng(seed).

Dónde suelen fallar las cosas (aprendido al construir esto)

  • El blanco se traga las mezclas. El mezclador espectral pondera los pigmentos por luminancia; una mezcla 1:9 color:blanco es casi blanca. Mezcla el tono con poco o nada de blanco, luego colócalo con tone(c, value). Atenúa resultados chillones con chroma(c, 0.5–0.8).
  • Nubes como bolas con palos → muchos lóbulos pequeños sobre una base plana (cumulusShape lóbulos 40–60), luz solo en el lado que da al sol, toques redondos suaves, thick moderado.
  • Pino como palmera → las agujas son grupos redondeados (ráfagas de pinceladas cortas en abanico) posados sobre las ramas con huecos de cielo entre ellos, oscuros debajo, claros arriba; las ramas caen bajas y se elevan altas.
  • Árboles con ramas en paraguas / troncos negros → las ramas deben terminar dentro de la copa; los oscuros del tronco tienen valor ~0.22, no negro, y reciben atmosphere con la distancia.
  • Un área entera de color saturado (suelo naranja, cielo turquesa) → tierrálalo; dispersa el color puro como unas pocas marcas pequeñas.
  • El camino parece un río → gris claro cálido, pinceladas en la dirección del recorrido, violeta frío solo en sus sombras.
  • Agua hecha de guiones cortos parece digital → el agua son arrastres largos y nivelados fundidos entre sí, con solo unas pocas marcas de ondas cerca del espectador (lo que hace paintWater); no añadas filas de guiones a mano.
  • El mar se lee como un espejo → paintWater({ chop: 1 }) para lomos de olas y crestas blancas; mantén chop: 0 para lagos y ríos.
  • Luz de lámpara/ventana como un parche plano → paintGlow sobre un entorno oscuro, luego el pequeño núcleo caliente; una pincelada larga y suave no es un resplandor.
  • Rayos como púas blancas → pocos, amplios, muy suaves, de baja opacidad (valores predeterminados de paintRays).
  • La trama del lienzo se convierte en una rejilla sobre pintura pálida y delgada → mantén look.weave ≤ 0.2.
  • Montañas como un muro escamoso o rayas de cebra → construye el horizonte con peaks(), mantén el rango lejano (pintura delgada, size ≈ 0.02, rango de valor estrecho) y deja que paintMountains lo divida en unos pocos planos grandes iluminados y en sombra.
  • Una banda plana no es un banco de nubes / colina / línea de árboles → varía el borde superior, los valores internos y deja espacios.
  • La escena falla con un error vago del navegador → la CLI verifica la sintaxis primero; un error en tiempo de ejecución imprime la línea de la escena. Una variable local que sombrea un color (leaf, sky, hill) es la causa habitual de c.map is not a function.
  • Leer un fotograma obsoleto: verifica siempre la salida del comando para detectar errores antes de abrir el PNG.
  • WebGPU y WebCodecs solo existen en un origen seguro — la CLI sirve http://127.0.0.1; nunca abras escenas mediante file://.

Archivos de referencia

  • references/painting-lessons.md — qué enseña cada demostración y cómo se asigna a las llamadas del motor.
  • references/composition.md — composición de una escena, el plan de valores, números de los maestros, study.py.
  • references/api.md — Referencia de Painting / capa / pincel / color / pincel de sujeto, cómo escribir un nuevo pincel de sujeto.
  • references/animation.md — ritmo de revelado, movimiento vivo, cámara y paralaje, capas dinámicas, ebullición, audio.
  • references/pipeline.md — CLI, configuración de render, rendimiento, QA, solución de problemas, entregables.

Skills relacionados