Malerisches Video
Du malst mit Code, wie ein Maler mit dem Pinsel malt: Entscheide zuerst das Bild (Komposition, Werte, Palette), lege dann echte Striche von hinten nach vorne, schau dir das Ergebnis an und passe es an. Die Engine verwandelt diese Striche in Einzelbilder; nichts ist ein Filter über einem Rendering – jede Markierung ist ein von dir gesetzter Pinselstrich.
Die Methode wurde aus vier Mal-Demonstrationen (Prozess, Kiefer, malerische Bäume, Sonnenuntergangswolken) und aus der Vermessung von 13 gemeinfreien Meisterlandschaften gelernt. Die destillierten Lektionen sind in references/; der funktionierende Code ist in engine/ und examples/.
Einrichtung (einmal pro Projekt)
cp -R /pfad/zu/malerisch-video/engine ./painter-engine
cd painter-engine
npm ci
node bin/paint.mjs still ../pfad/zu/szene.js --t 8 # → PNG neben der Szene (stills/<szene>/)
Ersetze /pfad/zu/malerisch-video mit dem Ordner, der diese SKILL.md enthält (z.B. ~/.claude/skills/malerisch-video). Benötigt Node ≥ 22.12, Google Chrome mit WebGPU (oder CHROME_PATH), ffmpeg und ffprobe. Referenzstudien benötigen zusätzlich Python mit tools/requirements.txt. Kein Build-Schritt oder Rendering-API-Schlüssel; das Rendering läuft auf lokaler Hardware. Beginne eine neue Szene, indem du die nächstgelegene Datei in examples/ kopierst (sunset-river.js Himmel/Wolken/Wasser · lone-pine.js vertikal, einzelnes Motiv · lake-path.js Bäume/Tiefe/Weg · sunset-study.js dramatischer Himmel mit dunklen Wolkenmassen und Gegenlicht (eine Studie des Aufbaus einer Demonstration) · mountain-hamlet.js Berge, Hütte, Felsen, Wanderfigur, Rauch · brush-chart.js Pinsel und Mischungen).
Arbeitsablauf – überspringe die Betrachtungsschritte nicht
- Briefing. Motiv, Stimmung, Tageszeit, Format (16:9, 9:16, 1:1 – jede
--size), Dauer und wie die Zeit aufgeteilt wird: Paint-on-Reveal, Living Painting oder beides (Standard: ~60 % Enthüllung, ~40 % lebendig). - Studiere eine Referenz, wenn vorhanden (
tools/study.py, siehereferences/composition.md). Sie berichtet über Wertemassen, Schlüssel, Chroma, Palette und Brennpunkt – kopiere die Struktur, niemals das Bild. - Komponiere vor der Farbe. Schreibe die Komposition als Kommentar oben in die Szene: Horizonthöhe (niemals die Mitte), Brennpunkt (auf einem Drittel, wo der stärkste Wertkontrast und das höchste Chroma liegen), der Hinführer, was es einrahmt, und ein Werteplan – ein Wert (0–1) für jede große Form, in drei ungleichen Massen.
references/composition.md. - Palette aus Pigmenten, platziert auf dem Werteplan.
tone(mix({ ultramarine: 1, transYellowOxide: 1 }), 0.42). Halte das Chroma überall niedrig außer beim Brennpunkt-Akzent (Meister: mittleres OKLab-Chroma 0,02–0,06, Bereich mit hohem Chroma unter ~8 %). - Male von hinten nach vorne mit den Motiv-Malern (
paintSky,paintCumulus,paintHills,paintMountains,paintWater,paintTreeline,paintPine,paintFir,paintTree,paintGrass,paintRocks,paintHouse,paintGlow,paintMoon,walker,boat,smoke…) und rohemlayer.scatter/stroke/pathfür alles andere. Einep.phase('name')pro Stufe, Ebenen erstellt fern → nah.references/api.md. - Schau hin, kritisiere, passe an – mindestens zwei Runden. Rendere ein Standbild, öffne das PNG und beurteile es wie ein Gemälde: kneife die Augen zusammen (Lesen sich die drei Wertemassen?), ist der Brennpunkt wie geplant, ist etwas gleichmäßig verteilt / gleich groß / gleich gesättigt, sieht irgendeine Markierung mechanisch aus? Behebe die Ursache im Code. Führe
study.pyauf deinem eigenen Standbild aus und vergleiche mit dem Plan. Diese Schleife ist die Fähigkeit; erste Renderings sind nie richtig. - Zeit.
p.timelapse()für die Enthüllung, danach Bewegung:sway,drift,shimmer,p.wind,p.camera(Parallaxe nach Ebenentiefe),p.dynamicfür Dinge, die sich bewegen.references/animation.md. - Kontaktbogen, dann rendern.
still --n 12 --sheet→ überprüfe jede Stufe des Zeitstrahls; dannrender→qa.references/pipeline.md. - Berichte ehrlich: was automatisch geprüft wurde, was du angesehen hast, was du nicht getan hast.
Szenen-Skelett
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 = Breite / Höhe des Renderings
const p = new Painting({ aspect, seed, ground: '#c9b89a' }); // getönter Malgrund, niemals weiß
p.duration = 16;
const A = aspect; // Szeneneinheiten: x 0…A, y 0 (oben) … 1 (unten)
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('himmel');
paintSky(p.layer('himmel', { depth: 0 }), { color: skyColor({ top: 0, bottom: 0.7, stops: [[0, skyTop], [1, skyLow]] }), bottom: 0.72 });
p.phase('land');
paintHills(p.layer('hügel', { depth: 0.6 }), { top: hill, color: (x, y, k) => hillCol, size: 0.03 });
paintGrass(p.layer('gras', { 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 }); // Bild fertig bei 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;
}
Regeln, die es gemalt aussehen lassen (die Kurzfassung)
- Der Wert leistet die Arbeit, die Farbe bekommt die Anerkennung. Plane drei ungleiche Wertemassen; kneife die Augen bei jedem Standbild zusammen.
- Getönter Malgrund, niemals weiß. Wähle die Imprimatura, um das Bild zu harmonisieren (warme Siena unter Grüntönen, helles Creme unter einem Sonnenuntergang).
- Entfernung zuerst, dann nach vorne. Fern = heller, kühler, grauer, weicher, dünnere Farbe, kleinere Markierungen. Nah = dunkelste Dunkelheiten, höchster Kontrast und Chroma, dickste Farbe, größte Markierungen. Verwende
atmosphere(color, d, air). - Dunkelheiten zuerst und dünn, Lichter zuletzt und dick.
thick0,1–0,3 für Schatten und Entfernung, 0,8–1 für Glanzlichter; Glanzlichter sind klein und wenige. - Hell gegen Dunkel, Dunkel gegen Hell. Brich jede Masse mit Markierungen, die eine kleine Wertstufe (±0,05) von dem entfernt sind, was darunter liegt;
p.valueAt(x, y)/p.sample(x, y)sagen dir, was dort ist. - Striche folgen der Form: um eine Wolkenkugel herum, entlang eines Abhangs, flach auf Wasser, einen Grashalm hinauf, einen Stamm hinunter.
- Mische Grüntöne und erde sie (
earth(mix({ ultramarine: 1, cadmiumYellow: 1 }), 0.3)); spare reines Chroma für einen Akzent auf. - Variiere alles: Größen, Abstände, Höhen, Intervalle. Gleich = mechanisch. Ungerade Gruppen, überlappende Formen, eine Dominante.
- Verlorene und gefundene Kanten: weich, wo sich Formen abwenden oder zurückweichen (
soft0,5+), scharf am Brennpunkt. - Reine Funktion der Zeit. Eine Szene ist deterministisch aus
seed; ein Einzelbild hängt nur vontab. Verwende niemalsMath.random()oder die Wanduhrzeit – verwendelayer.rand/rng(seed).
Wo Dinge schiefgehen (beim Bauen hiervon gelernt)
- Weiß verschluckt Mischungen. Der Spektralmischer gewichtet Pigmente nach Leuchtdichte; eine 1:9-Farbe:Weiß-Mischung ist fast weiß. Mische den Farbton mit wenig oder keinem Weiß und platziere ihn dann mit
tone(c, value). Schneide grelle Ergebnisse mitchroma(c, 0.5–0.8)`. - Wolken als Bälle mit Stöcken → viele kleine Lappen auf einer flachen Basis (
cumulusShapeLappen 40–60), Licht nur auf der der Sonne zugewandten Seite, runde weiche Tupfer, mäßigesthick. - Kiefer als Palme → Nadeln sind abgerundete Büschel (Ausbrüche von kurzen Fächerstrichen), die auf den Ästen sitzen, mit Himmelslöchern dazwischen, dunkel darunter, Lichter oben; Äste hängen tief und reichen hoch hinauf.
- Bäume mit Regenschirm-Speichen-Ästen / schwarzen Stämmen → Äste müssen innerhalb der Krone enden; Stammdunkelheiten haben einen Wert von ~0,22, nicht schwarz, und erhalten
atmospheremit der Entfernung. - Ein ganzer Bereich in gesättigter Farbe (oranger Boden, türkiser Himmel) → erde ihn; verstreue die reine Farbe als ein paar kleine Markierungen.
- Weg liest sich als Fluss → warmes hellgrau, Striche in Bewegungsrichtung, kühles Violett nur in seinen Schatten.
- Wasser aus kurzen Strichen sieht digital aus → Wasser besteht aus langen, waagerechten Zügen, die ineinander übergehen, mit nur wenigen Kräuselmarkierungen in der Nähe des Betrachters (was
paintWatertut); füge keine Reihen von Strichen von Hand hinzu. - See liest sich als gläserner Spiegel →
paintWater({ chop: 1 })für Wellenrücken und Gischt; behaltechop: 0für Seen und Flüsse bei. - Lampen-/Fensterlicht ist ein flacher Fleck →
paintGlowüber einer dunklen Umgebung, dann der kleine heiße Kern; ein langer weicher Strich ist kein Leuchten. - Strahlen als weiße Spieße → wenige, breite, sehr weiche, niedrige Deckkraft-Strahlenbündel (
paintRaysStandardeinstellungen). - Leinwandbindung wird zu einem Gitter auf blasser dünner Farbe → behalte
look.weave≤ 0,2 bei. - Berge als schuppige Wand oder Zebrastreifen → baue die Skyline mit
peaks(), halte die Reichweite fern (dünne Farbe,size≈ 0,02, schmaler Wertebereich) und lassepaintMountainssie in ein paar große beleuchtete und Schattenebenen aufteilen. - Ein flaches Band ist keine Wolkenbank / Hügel / Baumgrenze → variiere die Oberkante, die inneren Werte und lasse Lücken.
- Szene schlägt mit einem vagen Browserfehler fehl → die CLI prüft zuerst auf Syntax; ein Laufzeitfehler gibt die Szenenzeile aus. Eine lokale Variable, die eine Farbe überschattet (
leaf,sky,hill), ist die übliche Ursache fürc.map is not a function. - Lesen eines veralteten Standbilds: überprüfe immer zuerst die Befehlsausgabe auf einen Fehler, bevor du das PNG öffnest.
- WebGPU und WebCodecs existieren nur auf einem sicheren Ursprung – die CLI bedient
http://127.0.0.1; öffne Szenen niemals überfile://.
Referenzdateien
references/painting-lessons.md– was jede Demonstration lehrt und wie sie sich auf Engine-Aufrufe abbildet.references/composition.md– Komponieren einer Szene, der Werteplan, Zahlen von den Meistern,study.py.references/api.md– Painting / Ebene / Pinsel / Farbe / Motivmaler-Referenz, Schreiben eines neuen Motivmalers.references/animation.md– Enthüllungstempo, lebendige Bewegung, Kamera und Parallaxe, dynamische Ebenen, Sieden, Audio.references/pipeline.md– CLI, Rendereinstellungen, Leistung, Qualitätssicherung, Fehlerbehebung, Liefergegenstände.