Chinese Ink Wash 中国水墨
The Swordsman and the River·48s·Lemo-Opuscarby Lemomo
Xieyi ink painting on rice paper, animated: blank paper is space, one brushstroke is a mountain, a wave, a gesture or a cut.
The demo: A swordsman crosses the river on the water and splits the current with a single stroke.
Xieyi ink painting on rice paper, animated: blank paper is space, one brushstroke is a mountain, a wave, a gesture or a cut. References (grammar only): Shanghai Animation Film Studio's Feelings of Mountains and Water (1988) (blank-paper space, ink bloom, guqin pacing, figures in a few strokes); the bamboo-forest fight of Crouching Tiger, Hidden Dragon (weightlessness, stillness before action). Copy none of their characters, compositions or music.
1. Essence, and what it is not
An ink painting (水墨) painted and unrolled before you: black ink on warm rice paper in five tones, plus one red, the vermilion seal. No Western outlines: forms are made by loaded brushstrokes whose dark edge is the contour (没骨, "boneless" painting), by wet washes that bleed into the paper, and by dry, fast strokes where the paper shows through the bristle tracks (飞白, "flying white").
Most of the frame is empty paper (留白): water, sky, mist, silence. Things are painted into existence, not cut to.
Not watercolour (no colour washes), not vector art with a brush texture (no flat fills or closed outlines), not a calligraphy title sequence.
2. Materials & rendering
- Paper is procedural, in the compositor, never a texture file: warm rice paper (around
#F2ECDE), cloud mottling, long fibres, pulp flecks, grain that fades when zoomed out (or it shimmers), a light vignette; anchored to the world. - Ink is stroke opacity multiplied onto paper (never an overlay), in five tones (墨分五色):
| Tone | Opacity | Use |
|---|---|---|
| 焦 jiao (burnt) | ~.96 | calligraphy, the sharpest lines, moss dots |
| 浓 nong (thick) | ~.82 | contours, dark edge of loaded strokes |
| 重 zhong (heavy) | ~.60 | secondary lines, folds |
| 淡 dan (light) | ~.34 | mountain bodies, robe and body washes |
| 清 qing (clear) | ~.16 | far mountains, faces, mist |
- Brushstroke model: centreline × pressure profile × width noise, plus 4–60 bristles whose ink load drops along the stroke and breaks on long-scale noise.
dryruns from a solid wet stroke to flying white (dry strokes are bristle tracks only). - Loaded stroke: bristles darker on one side, so the stroke carries its own contour: this makes bodies, rocks and waves read as ink, not vectors.
- Two layers: the wet one bleeds in the compositor (noisy blur, edge pigment accumulation, granulation); the dry one only gets fibre jitter and paper tooth.
- Masses: soft polygons, jittered edges, a gradient; never flat fills.
- Landscape: mountains dissolve downward into mist bands, with 披麻皴 texture and 苔点 moss dots on near ranges only; rocks are outline → axe-cut texture → shadow wash → dots.
- Water is blank paper, moved by faint dry streaks and open ink rings. Solid water: Song-dynasty parallel wave lines (dense near the edge) or a pale body with a few huge loaded strokes along its face.
- Figures are xieyi: one pale body mass plus two loaded strokes, burnt-ink lines for limbs; features, if ever shown, in 3–5 strokes.
- Calligraphy: an OFL brush font is only a skeleton: trace centrelines in stroke order, pressure from glyph thickness, hidden-tip entry (藏锋), tapered exits.
3. Colour logic
- Ink only, from cool grey at low density to warm black at full density. No other hue in the world.
- One vermilion (around
#B02E22), for the seal and at most one tiny accent in the story (an object the protagonist carries). The accent finds the protagonist in wide shots and can rhyme with the seal. Never a red wash, never red type. - Values carry depth: far = pale and soft, near = dark and textured. If a frame gets busy, remove ink rather than add tone.
4. Type & subtitles
- Subtitles are inscriptions (题跋): a classical italic serif (Cormorant Garamond italic), ink-coloured, no box, ≤ 2 lines, placed in each shot's empty paper. A small vermilion square marks the start of each line. They appear wet and dry in a fraction of a second. Hold ≥ max(1.8 s, speech + 0.6 s).
- Titles are written on the painting itself, in vertical columns in its empty space, with a vermilion 白文 seal drawn by code (lines knocked out of a slightly irregular red square, with ink-paste speckles). They move with the world.
- Screen-space overlays reset the camera transform.
5. Motion quality
- Figures step at 12 fps (on twos); their position, the camera, blooms, ripples and splatter move on ones at 24 fps.
- Stable strokes on twos: seed every stroke by its index and compute bristle breaks along normalized arc length × a fixed reference length, so flying-white gaps don't crawl every drawing. Allow only a small "boil" on the width noise.
- Painted into existence: strokes appear in drawing order.
- Stillness before action (when a decisive stroke is the event): hold, with at most a slow push-in, then let the stroke cross the frame in a few frames. Consequences start only after the stroke has finished.
- Nothing is tweened like a vector shape: things are painted, bloom or dissolve into mist.
6. Camera grammar
A vocabulary, not a route. Opening and ending come from the topic.
| Move | Expresses | Can serve |
|---|---|---|
| Locked frame on the paper | The page is the stage; things are painted in | a thing forming; a line being read; a still decision |
| Right-to-left pan (handscroll) | Time and distance as one painting | a journey; a history; a process |
| Vertical tilt (hanging scroll) | Height, stages stacked | seasons; a climb; generations |
| Lateral tracking, lead room, slow far parallax | Motion through a world | travel; a chase; a commute |
| High wide, a tiny figure in blank paper | Smallness, solitude | loneliness; nature's scale; one life among many |
| Low angle, dark mass towering, slow push-in with shake | Weight, threat | an obstacle; a storm; pressure |
| Hard cut to an extreme close-up | A break in the flow (the style's only hard cuts) | a decision; a shock; a memory |
| Push into an ink bloom or mist | Passing between states | memory; dream; a season turning |
| Pull back until a mark joins the whole | The part was inside something larger | a summing-up; irony of scale |
| Return to an earlier framing | The rhyme shows what changed | aftermath; growth; before/after |
Framing: 60–80 % empty paper; dark foreground may pass along the frame bottom. Transitions are ink blooms, pans along the scroll or mist; never a generic dissolve or a wipe.
7. Sound palette
- Guqin-led, not piano and strings: guqin (
core/audio/pluck.pyguqin: open and stopped strings, slides, vibrato, glassy harmonics, sweeps), a low breathy xiao, big drum and timpani for mass, frame drum for steps, a small gong for a seal. Chinese pentatonic modes. - Cue to the picture, never loop: unmetred plucks suit emptiness, a pulse suits movement, rolls suit mass; painted strokes can each land on a pluck.
- Silence is literal: zero the whole bus (music, ambience, reverb tails) with short fades. At most one sound inside it (a drip, one ring). Place it where the story holds its breath.
- Foley follows the material: ink drop (short "tink" + soft wet thud), brush air (band-passed noise with bristle grains, never a metal whoosh), steps on water (small splash), splatter (wet ticks), seal (wooden thump + paper crackle).
- Voice: calm, few lines, like colophons that don't explain the picture. Duck music ~8 dB and ambience too; verify each line with whisper on the final mix.
8. Native moves
A menu: use the ones your story needs.
- One stroke is everything. Fits content like: a product born from one line; a city growing from a single street; a day beginning with one bird.
- Emptiness is matter. Removing ink reveals paper white. Fits content like: a door opening in a wall; a crowd parting; an absence in a family portrait.
- Brush = gesture = action. Stroke order becomes choreography. Fits content like: a dancer's phrase that spells a word; a signature; a chef's knife work.
- Flying white (飞白). One fast dry stroke across the frame, strongest out of a silence. Fits content like: a sprint finish; a decision; a comet.
- Ink bloom transition. The next scene grows inside a spreading drop with a dark tide-line (noisy radial mask; world-anchored for elements, in the compositor for scenes). Fits content like: memories; seasons; one era giving way to the next.
- The handscroll. One long painting panned right to left, seen whole at last. Fits content like: a history; a supply chain from field to hand; a day in a street.
- Splitting a mass. Halves clipped either side of the cut; one rises and fades, one slumps; splatter stays. Fits content like: breaking a record; a barrier falling; an idea splitting in two.
9. Pitfalls of the medium
- Dry brush looks like dashed lines → breaks from long-scale noise and an ink load that drops along the stroke.
- Parallel semi-transparent strokes make bodies look like organ pipes → one base mass + two loaded strokes.
- Big side-brush strokes look like a hairbrush → a gradient mass + a few low-dryness strokes. Gradients in vertical bands leave seams → a cached per-pixel layer.
- A stroke body split between wet and dry layers loses density → full body in the dry layer plus a wet halo.
- Clipping calligraphy with the font outline notches the junctions → font as skeleton only.
- Uniform hatching inside a wave turns it into a wire mesh → loaded strokes along the face.
10. Engine
In demo/: ink.js (brush engine: strokes, bristles, loaded strokes, masses, blots), comp.js (WebGL rice-paper compositor: bleed, edge pigment, paper, bloom), land.js (mountains, cun, cliffs, pine, reeds, ripples), wave.js (gestural wave), glyph.js (skeleton-traced calligraphy), seal.js (vermilion seal), hero.js (xieyi figure rig), subs.js (inscription subtitles), music/score.py (guqin writing with core/audio/pluck.py); ?test=sheet|pose|frame pages draw model sheets from the real engine.
11. Variation space
You decide the structure, the characters (or none), the landscape, the opening, the ending, the camera path, the pacing and where the red goes. All far from our demo:
- Structures: one character, stroke by stroke (no figure; each stroke of one large character opens into a vignette of the topic); a hanging scroll (a tilt from sky to ground through four stages, tone deepening); two brushes in dialogue (alternating strokes answer each other until they meet in one bloom).
- Openings: in motion (a figure already painted and moving; the world washes in around it); ink first (a fully inked frame; ink is lifted where light or a road appears); the bristles (a wet stroke fills the frame; only when it lifts do we see what it drew).
- Endings: one small mark (close on a footprint or a leaf while the ink around it dries; no pull-back); evaporation (the painting un-paints in reverse stroke order); mid-gesture (the brush stops before the last stroke lands).
How our demo was made (story, shots, score, end card, build): DEMO.md. Read it after your treatment exists.
Director's notes for the demo film
One example among many. Don't reuse its story, arc, shots, props or timings.
Demo: The Swordsman and the River (48 s) · ink-wash.mp4 · source in demo/
Story & structure
A swordsman crosses a river on the water and splits a towering wave with one stroke. One character, one goal, one turn. The brush itself is the event: the stroke that cuts the wave is also the character 水 written in the air.
Arc (48 s): emptiness → a drop of ink (creation) → a small figure in vast space → lightness and joy (the qinggong run across the water) → an overwhelming obstacle drawn as mass (a wet splash of dark ink becoming a wave) → silence → one decisive stroke → release → scale reveal (the figure is a tiny mark in the whole painting) → seal. The closing line is philosophical rather than triumphant ("cut water with a sword and it only flows on").
Native moves spent: one stroke is everything (the opening drop), brush = gesture (the written 水), flying white (the cut, after total silence), splitting a mass (the wave), the handscroll (the final pull-back and seal).
Shots
| Beat | Camera |
|---|---|
| Opening | Locked frame of blank paper; the drop falls into it. |
| Establishing | Slow right-to-left pan (scroll unrolling), ending on a high wide frame: the figure 150–180 px tall, the river a huge blank. |
| Travel | Lateral tracking with the figure at ~60 % of the frame, lead room ahead; very slow parallax far mountains; occasional dark reeds passing at the very bottom. |
| Threat | Low angle, horizon low, the wave towering, the figure tiny in a corner; slow push-in with growing shake. |
| Silence | Hard cut to an extreme close-up (hat brim, no eyes); then an over-the-shoulder frame putting the figure, the written character and the wave on one axis. |
| Release | Back to the same high wide scale as the establishing shot. |
| Ending | Continuous pull-back from the figure to the whole handscroll on its silk mount; seal; end card below the scroll. |
Why this route, for this story: the locked blank paper put "nothing" before "something"; the scroll pan ending high and wide made the figure tiny; the extreme close-up in the silence was the film's only hard cut; the release returned to the establishing scale so the rhyme showed what changed; the pull-back to the mounted scroll turned the whole journey into one painting. Each of these is one choice from STYLE.md §6, not the style's default opening or ending.
The hold before the strike is a slow 5 % push-in; the stroke then crosses the frame in ~4 frames, and the wave only starts splitting after it.
Score structure
Original guqin score (demo/music/score.py), F-gong pentatonic (F G A C D), cued by section rather than looped. Free-time opening, 60 BPM for landscape, 90 BPM for travel (footsteps on beats), drum roll for the threat, digital silence, three glassy harmonics for the three written strokes, silence again, then drum + seven-string sweep on the cut; the theme returns for the release; last note = seal.
Foley specific to this film: sword draw (scabbard scrape + long thin metallic ring, cut before the second silence), the cut (tearing air + river roar + low boom). The sword ring and a single drip are the only sounds allowed inside the silences; the ambience hard-cuts into the first silence.
Voice: calm British narrator, Kokoro bm_george, lang en-gb, speed .82–.86, 4–6 lines. Music ducked ~8 dB under voice, ambience ducked too: a river roar under a line made whisper hear "Halfway" as "This way". Every line checked with whisper and again on the final mix.
Palette & props
- Paper: base
#F2ECDE–#F3EBDD(rgb .95/.925/.87), cloud mottling ±7 %, three fibre directions (+1.8 % light), vignette ~.22. Ink from cool greyrgb(.52,.55,.58)to warm blackrgb(.075,.068,.062). Vermilion#B02E22: the seal and the hero's sword tassel, which finds him in wide shots and rhymes with the final seal. - Compositor numbers: wet bleed is a 12-tap noisy Poisson blur of ~5 px at 1:1; edge pigment = small blur − large blur.
- Ink tones as used: burnt for calligraphy, sword, hat brim line, moss dots; thick for contours and hat; heavy for folds and scabbard; light for mountain bodies and robe washes; clear for far mountains, face, mist.
- Stroke profiles in
ink.js:brush,tip,nail,lens,fan,rise,robe,even+ per-point pressure; loaded strokes usedarkDir,side,brWet(their bristles go into the wet layer); body fill fades as(1-dry)^1.6;dry.3 = textured, .6+ = flying white. - Mountains (
land.js): per pixel once and cached, pointed exponential peaks with 3-octave roughness; far ranges tone .12–.16 untextured, near .4–.5 textured; range ends faded with a horizontal alpha mask, never by sloping the ridge (it crosses the river). Pine: one crooked loaded trunk, a few tip branches, fan-shaped needle clusters. Reeds: long tip strokes + lens leaves, foreground only. - Water: ink rings at each footfall, one stroke of 1.86π with an open gap, r ∝ age^0.6, fading over ~2.4 s.
- The wave: a pale wet body + 4–5 huge loaded strokes curled over at the crest + a few thin wave lines + spray as irregular blots.
- The hero (
hero.js): 100-unit rig (hip, chest, head, arms, legs, hem points, flare, sheath, ribbon). Hat = one wet mass + two dry ridge lines + a burnt-ink brim line; robe = one pale mass + two loaded strokes; legs and sword in burnt ink; one ribbon in the wind. The face stays under the hat; features shown once (sword brows, phoenix-eye lids, one nose line, drooping moustache). - Qinggong leap: body pitched ~30° forward, one knee tucked, the other leg trailing, robe pulled back into a big pale mass with a dark back edge, sleeves streaming. Step cycle: touch (14 %) → rise → glide (45 %) → descend; height
sin(π·phase); one ink ring per touch. - The written 水: traced from Ma Shan Zheng,
solid: true, dryness .17, exits tapered to .12–.18, 45° hidden-tip lead-in. - Stable strokes:
gapLenfixed reference length; boil on a 5-drawing cycle. - The split wave: the upper half rises and rotates, the lower slumps and squashes, with curling crests along both torn edges and pale wash blocks; splatter flies along the sword direction with gravity.
- Ink bloom: mask
d·(0.62 + 0.8·fbm); element blooms use a 1/4-res mask withdestination-in.
End card
Title: two vertical columns written on the scroll itself (English letterforms rotated 90°, Cormorant SC 600, wide tracking) + seal; it fades in wet → dry and evaporates when the pan starts. Subtitles: Cormorant Garamond italic 500, 46 px, 1 px tracking, drying in ~0.35 s.
End card: the whole scroll on a dark table with silk borders and rollers; "CHINESE INK WASH" (Cormorant SC 600, 44 px, 14 px tracking) and the credit line (italic 30 px) in pale paper colour below. The "LemoLab × Claude Opus 5.5" sign-off belongs to this library's demo only; a user's film carries no LemoLab credit and no copy of this card.
Build notes
styles/ink-wash/demo/
ink.js brush engine (strokes, bristles, masses, blots) comp.js WebGL rice-paper compositor (bleed, edge, paper, bloom)
hero.js xieyi figure rig + poses face.js close-up face (one use)
land.js mountains, cun, cliffs, pine, reeds, ripples wave.js gestural wave
glyph.js "水" traced from Ma Shan Zheng + the cut stroke seal.js vermilion seal
world.js the handscroll world, reveals, shots 1–2 cross.js crossing + rising (shots 3–4)
climax.js silence, writing, the cut (5–6) passage.js parted river, bow, scroll, end card (7–8)
story.js timeline (single source of truth) shots.js dispatcher, transitions, subtitle spots, sound events
subs.js inscription subtitles music/score.py original guqin score mix.py foley + voice + music build.sh
- Write
story.jstimes first (voice durations fromcore/tts/tts.py), then brief the score with the same numbers (the composer can run in parallel). node core/render/still.mjs styles/ink-wash/demo --range a:b:1+core/render/sheet.pyfor overview sheets;?test=sheet|pose|shui|framepages for design work;?nosub=1,?poster=1.node core/render/events.mjs→mix.py→video.mjs --fps 24 --workers 3(1152 frames ≈ 60–130 s) →mux.sh … 24 0(grain 0: the paper is in the shader).- Or simply
sh styles/ink-wash/demo/build.sh.
Pitfalls tied to this demo's props:
- Subtitles inherited the world camera transform in panning shots and slid off screen.
- A cliff drawn with
flip=-1put the figure in mid-air; the back of a cliff polygon showed a straight vertical edge; a rock's base was cut by the layer canvas bottom → compute screen extents after flipping, slant the back side, fade layer bottoms with adestination-ingradient. - The cut began splitting the wave while the stroke was still crossing the frame.
- A scene with rare, heavy per-pixel steps can exceed Playwright's default screenshot timeout; just retry the still render (the frame itself is deterministic).




