Skip to content
Back to skills

Painterly

ASecurity

Painterly (名画笔触) motion style — a moving oil painting made of thick brush strokes, Van Gogh / Loving Vincent style. Every stroke has body, bristle lines, a lit ridge and a shadowed edge; strokes follow the form (around a swirling sky, along hills, across water, up a wall), parts of the painting flow, the rest gently "boils" the way a hand-painted film does, and the camera moves through the painting. Use when asked for a Van Gogh / Starry Night / oil painting / 油画 / 梵高 / 星月夜 / 名画动起来 / painterl...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 9, 2026
ai-agentspythongophp

Security analysis

A100/100

Pro scans all 9 files and shows the line behind each finding

Scanned October 9, 2026

npx -y skills add sunsiyuan/ultramotion --skill painterly --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Painterly?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Painterly
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sunsiyuan-painterly/badge)](https://www.skillsdirectory.com/skills/sunsiyuan-painterly)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: painterly
description: Painterly (名画笔触) motion style — a moving oil painting made of thick brush strokes, Van Gogh / Loving Vincent style. Every stroke has body, bristle lines, a lit ridge and a shadowed edge; strokes follow the form (around a swirling sky, along hills, across water, up a wall), parts of the painting flow, the rest gently "boils" the way a hand-painted film does, and the camera moves through the painting. Use when asked for a Van Gogh / Starry Night / oil painting / 油画 / 梵高 / 星月夜 / 名画动起来 / painterly / brush-stroke animation, a painting that comes alive, or an art-history or museum piece, rendered from code to mp4 with music.
---

# Painterly

`brush.js` paints with oil strokes on a Canvas 2D page; `render.py` renders the page to mp4 with the music from `sound.py`. Open any file in `examples/` in a browser and it plays.

## Workflow

1. Copy `brush.js`, `render.py`, `sound.py` and the example closest to your idea into your project; work on the copies.
2. Decide the painting: which painter or painting, which scene, what moves (*What to paint*).
3. **If it is a real painting, paint from the original** (*Painting from the original*): download a public-domain image of it and let every stroke take its colour and direction from it. Only invent the picture yourself when there is no original (a new scene in a painter's manner).
4. Write `paint(x, y, t)` — the colour and the stroke direction at every point (*The paint function*). Add what moves, the camera, and anything drawn on top.
5. `python3 render.py page.html --preview --at 1,6,12` and look at the contact sheet; open one frame full size and look at the strokes up close.
6. `python3 render.py page.html --out video.mp4`.

Needs ffmpeg and Python with `playwright`, `Pillow`, `numpy`, `scipy`, `soundfile`; uses Playwright's Chromium or the installed Google Chrome. **If you're in a sandbox and the browser won't start**, ask for permission to run it outside the sandbox.

## Stillness with one thing moving

Before writing any motion, ask: *this moment in the painting — what happens in the next second?*

- **A large stillness, one small life.** Most of the painting only breathes; one small thing really moves — a petal letting go, a dragonfly touching the water, a crow lifting off, a candle flame, a curtain stirring.
- **The movement comes from the painting's own meaning.** Sunflowers that are wilting drop a petal; a summer pond gets a dragonfly; a wheatfield under a storm sends its crows up; a bedroom has a shutter tapping in the wind. Not an effect added on top — the moment continuing.
- **What moves carries time.** Something falling, arriving, leaving, growing, fading.
- **One or two, slowly.**

## How it moves

These are the style.

- **The painting breathes, never flickers.** Every stroke is repainted now and then, but only a share of them at a time (`boil`: `rate` repaints a second, `share` of the strokes each time), so the surface shimmers like a hand-painted film. Defaults: 12 a second, a sixth of the strokes.
- **Strokes follow the form.** Around a swirl, along a hill, across water, up a wall, along the curve of a boat or a cypress flame. The direction field is what makes it read as Van Gogh; a field that ignores the form reads as a texture laid on top.
- **What should flow, flows.** `flow` slides strokes along their own direction — sky, water, smoke, wind through wheat, a cypress licking upwards. Stars and lamps get rings that keep turning.
- **The camera moves through the painting.** Make the painting larger than the screen and drift, push in, or walk down a street. Slow — a full move takes most of the video.
- **Something lives in it.** A flock of crows lifting off, a couple strolling away, a boat drifting — painted with the same brush (see *People and props*).

## Painting from the original

Famous paintings are in the public domain; Wikimedia Commons has good scans — `https://commons.wikimedia.org/w/index.php?title=Special:FilePath/<file name>&width=1200` downloads one (send a User-Agent). Save it next to the page, resized to about 1000 px.

```js
window.__scene = Painterly.fromImage('ref/sunflowers.jpg', img => {
  const R = Painterly.reference(img, { width: 1296, height: 2304 });          // fitted into the painting area ('cover' or 'contain')
  const paint = (x, y, t) => { const p = R.at(x - sway(x, y, t), y); return { c: p.c, a: p.a, size: p.size, flow: p.coherence > .35 ? 7 : 0 }; };
  const P = Painterly.create({ width: 1296, height: 2304, view: [1080, 1920], paint, lines: R.lines, camera: … });
  return Painterly.scene(P, { duration: 18, music: { notes } });
});
```

`R.at(x, y)` gives the painting's colour, the direction its brushwork runs (from the grain of the picture), how clear that direction is (`coherence`), and a smaller stroke `size` where there is detail. `R.lines` are the painter's own dark drawing lines; pass them as `lines` and they are redrawn thin on top (`linesOffset(x, y, t)` moves them with the motion).

**Make it move — a still painting with strokes is not enough.** Sample the painting at a moving point to animate it: flowers stirring (`x - sway`), water rippling, a sky turning around its centre, wheat in waves. Add `flow` where the brushwork has a clear direction, something that lives in it (a petal falling, a crow, a boat), and a camera move. The original stays recognisable because the colours and directions still come from it.

Very finely broken paintings (Monet, Seurat) turn to texture at full view: push the camera in, or lower `strokes.size`.

## What to paint

Stay close to the original painting: its composition, its palette, its subject. Pick the painting from the occasion and the audience; change what moves and where the camera goes.

| painter / painting | what makes it |
|---|---|
| Van Gogh — The Starry Night, Wheatfield with Crows, Café Terrace at Night, Sunflowers, Irises, The Bedroom | swirling direction fields, complementary blue and yellow, Prussian-blue contours, stars and lamps in rings |
| Monet — water lilies, Japanese bridge, Impression, Sunrise | short broken horizontal strokes on water, no contours, light pastel palette, reflections that wobble |
| Seurat / pointillism | `strokes: { size: .4, impasto: .3 }` and `len` near .4 — dots, not strokes; pure colours side by side |
| Munch — The Scream | long wavy strokes that warp the whole picture, blood-orange sky |
| Hokusai — The Great Wave | not oil: lower `impasto`, flat Prussian blue, white foam strokes curling at the tips |

## The paint function

```js
const P = Painterly.create({
  width: 1080, height: 2400,          // the painting; larger than the view lets the camera travel
  view: [1080, 1920],                 // the video frame
  paint: (x, y, t) => ({ c: [r, g, b], a: angle, flow: 0, size: 1, len: 1 }),
  camera: t => ({ x, y, zoom }),      // the point at the centre of the frame, in painting pixels
  contours: t => [{ pts: [[x, y], …], color: [22, 36, 82], width: 5 }],   // Prussian-blue outlines around the main forms
  after: (ctx, t) => { P.stroke(x, y, angle, length, width, [r, g, b], key) },  // things drawn on top with the same brush
  boil: { rate: 12, share: 1 / 6, amount: 1 },
  strokes: { size: 1, density: 1, impasto: 1, bristles: 3 },
  background: '#0e1222',
});
window.__scene = Painterly.scene(P, { duration: 18, music: { notes }, sounds: [{ t: 9, dur: 3, kind: 'whoosh' }] });
```

`paint` returns, for any point: `c` the colour, `a` the stroke angle, `flow` how fast strokes slide along `a` (px/s, 0 = still), `size` the stroke scale (below .8 adds a second, finer layer of strokes — use it for windows, faces, signs, stars), `len` a length multiplier. Give each region its own direction rule.

Helpers on `Painterly`: `swirl(x, y, vortices, base)` → `{ a, band }` for a turning sky; `glow(x, y, cx, cy, r, reach)` → rings around a star or lamp; `inPoly(x, y, pts)`; `vn` / `fbm` noise; `hex`, `mix`, `seg`, `ease`.

Each stroke is a flat-brush ribbon: square and thick where it starts, bristle tracks side by side, a second colour picked up along one side, a ragged dry-brush tail, each brushload slightly warmer or cooler (`strokes.temperature`). The paint is laid on two layers — colour and thickness — and a lighting pass turns the thickness into impasto: ridges, a soft key light from `light: [dx, dy]`, a glint on the thickest paint. An underpainting shows faintly between strokes. `impasto: 0` gives flat strokes.

## People and props

Draw them with the same brush, in `after`, so they look painted rather than pasted on:

- `P.figure(x, y, height, { coat, skin, hat, dress, rim, rimSide, step, k })` — a person in the Loving Vincent manner: a silhouette filled with strokes running down the coat or dress, a coloured rim on the side the light comes from (`rim` = the light's colour), a brushed Prussian-blue outline; `step` animates the walk. Seated people: draw the figure, then the table in front of it.
- `P.mass(points, colour(x, y), angle(x, y), { size, width, k })` — any prop (a table, a chair, a boat, a lamp post): a polygon filled with strokes along its form and outlined with a broken brush line. `P.ellipsePts(x, y, rx, ry)` for round things.
- `P.stroke(x, y, angle, halfLength, width, colour, key)` — a single stroke (a crow's wing, a ripple).

## Examples

- `sunflowers.html` — Sunflowers, **painted from the original** (`ref/sunflowers.jpg`): colour and brush direction from the painting, the flowers stirring in a breeze, the painter's dark lines redrawn on top, a petal falling, the camera leaning in.

- `starry-night.html` — The Starry Night, invented in code (no original used — the way to paint a new scene in Van Gogh's manner): wind streams with two big spirals inside, the moon and stars in turning rings, a cypress flaming upwards; the camera drifts down from the sky to the village and leans in towards the lit windows.
- `wheatfield-crows.html` — Wheatfield with Crows: a storm sky churning, wind running through the wheat in waves, three paths, crows lifting off and flying into the distance.
- `cafe-terrace.html` — Café Terrace at Night: everything in one-point perspective, a yellow awning and lit terrace with people at the tables, short strokes on the cobbles, flower-like stars, a couple strolling down the street, lit from the café on one side and the night on the other.

## Sound

Write music as notes on the page: `{ t, m (MIDI), d (seconds), inst, v }` with `kalimba bell marimba piano pluck pad bass organ whistle` and drums `kick snare hat clap shaker`. Sounds: `whoosh swish riser` (with `dur`), `shimmer sparkle chime ding boom pop tada thud`. Choose the music from the painting: a slow minor pad and piano for a night sky, a low unsettled pad for the wheatfield, a waltz for the café.

## Check your output

Look at the contact sheet and at one frame full size. If you see:

- **strokes that look like rice grains, embroidery or mosaic** — strokes too small or too even; keep the default size, and give regions their own direction rule.
- **plasticine worms** — too much relief; lower `impasto`.
- **people or props that look pasted on** (flat fills, clean computer lines) — draw them with `P.figure` / `P.mass`, never with plain canvas shapes.
- **a texture laid over shapes** — the direction field ignores the form; give each region its own angle rule.
- **flicker** — `boil.share` or `amount` is too high, or something random is not seeded by the stroke's key.
- **everything looks the same muddy blue** — neighbouring regions need more contrast in value, not just hue; add contours.
- **details lost (windows, stars, faces)** — return `size` below .8 there.
- **a slideshow** — nothing flows and the camera doesn't move; add `flow` to the sky or water and a camera move.

Files in this skill

  • SKILL.md11.6 KB
  • brush.js23.5 KB
  • examples/cafe-terrace.html6.9 KB
  • examples/ref/SOURCES.md429 B
  • examples/starry-night.html6.3 KB
  • examples/sunflowers.html2.4 KB
  • examples/wheatfield-crows.html5 KB
  • render.py4.4 KB
  • sound.py14 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.