Woodcut Print 木刻版画

The Bell Founder·59s·Lemo-Opuscarby Lemomo

A relief print that carves itself: every frame is a black wood block, and light only exists where a knife has cut it away. One colour at most, and only for the thing that burns.

The demo: A village spends a whole winter casting one bell; the first time it rings, the snow stops.

Folk talesCraft brandsBook trailers
Style prompt · STYLE.md

A relief print that carves itself: every frame is a black wood block, and light only exists where a knife has cut it away. One colour at most, and only for the thing that burns. References (grammar only): Frans Masereel Passionate Journey (1919) and Lynd Ward Gods' Man (1929), wordless woodcut novels told as a sequence of self-contained plates; Käthe Kollwitz's woodcuts, where emotion lives in the direction of the gouge; Gustave Doré's engraving, where line spacing and direction build light and volume. Never copy their plates, figures, borders or titles.

1. Essence, and what it is not

Each shot is one printed plate: black ink on laid paper, framed by a paper margin. It follows three rules of real relief printing:

  1. Black is the wood, white is what the knife removed. Light is carved: a bright area is many wide cuts, a dark area untouched wood.
  2. Pictures appear by being carved. Nothing fades in: cuts grow stroke by stroke along the form, coarse gouges first, fine lines after.
  3. The block is a mirror. Letters read backwards until the paper is pulled; that reversal is the style's own transition.

Not copperplate engraving (no swelling burin lines on white, no crosshatch built up from white ground), not linocut poster art (no flat colour areas), not a black-and-white photo run through a threshold filter (the cuts must follow the form).

2. Materials & rendering

  • Print frame: the printed image area sits inside a paper margin; the wider bottom margin holds captions. The image edge is the block edge: ragged ink, never a clean rectangle.
  • Knife classes (widths in 1080p pixels at 1× camera):
Tool Width Use
Knife k 1.2–2 contours, the white outline that separates a black figure from a black ground
V-gouge v 2–8 hatching, hair, beard, fine light
U-gouge u 10–22 rays, sky sweeps, the first rough-out
Stab stab r 3–8 snow, sparks, flecks of loam, wood chips
  • Hatching (Doré grammar in wood): line direction follows form (a round object is wrapped in arcs, slopes follow the fall line, fire and light radiate, cloth follows its folds). Width follows tone: bright = wide cuts almost merging into white, mid-tone = thin cuts, dark = no cuts. Far = dense and fine, near = sparse and coarse. Every line is a separate cut (30–160 px, blunt entry, tapered exit, chipped edge), never a continuous machine line.
  • Figures: black silhouette + white knife halo + grazing-light hatching on the lit edge only (light z ≈ 0.2, so flat areas stay black). Skin carved white, features left black (the wordless-novel rule).
  • Close-ups (hands, objects): sculpt a small height field (capsules, blobs, planks, creases), light it and run it through woodcutFilter (§10), so cuts follow the isophotes like an engraver's.
  • Printing: the mask (white = cut) goes through a WebGL2 compositor: knife burr on edges, ±6 % ink unevenness, wood-grain streaks, specks where ink didn't take, a mottled colour plate with a small fixed misregistration. Block mode: glossy inked wood with grain sheen, pale wood in the grooves.

3. Colour logic

  • Two values carry everything: near-black ink and warm laid-paper white. The raw wood colour appears only in shots that show the block itself.
  • At most one colour plate, printed only where white has been carved, so it glows out of the cuts and never sits on the black. A colour plate over ink is nearly invisible, so plan it on carved white.
  • The colour is reserved for the one thing that matters (a flame, molten metal, a heart, a signal lamp). It can spread at a peak and shrink to a single highlight afterwards; it is never decoration.
  • Examples of a plate: a burnt copper-orange for heat; a cold Prussian blue for water or night; a deep oxblood for a wound or a seal. Choose it from the topic.

4. Type & subtitles

  • Captions are letterpress in the paper margin, below the plate, like a plate caption in a woodcut novel. They never cover the image. A letterpress old-style face (IM Fell English, OFL) around 40–44 px, in ink, pushed through the same print shader. The ink comes up in 0.1 s with no slide and fades over 0.15 s. Hold ≥ max(1.8 s, speech + 0.6 s).
  • Titles are carved, not typed: cut into the block (IM Fell English SC), mirrored if the block is shown before the print is pulled.
  • Any closing text is a fresh print: letterpress in the margin or carved out of a black panel.

5. Motion quality

  • Characters on twos (12 fps); camera, particles, fire and light on ones (24 fps). Hatching lives in each body part's local space, so cuts move with the limb and don't boil; only fire and liquids re-cut every 2 frames, as deliberate boil.
  • Anticipation / action / follow-through on every key action: a few frames back → a 2-frame strike → settle + a short shake.
  • Carve reveal: each stroke has its own start time and speed (0.08–0.35 s). Reveal keys: light (bright areas first, coarse to fine), radial (from a point: rays, a pour), down, or any function.
  • Hard cut = a new impression: two frames of misregistration (a few pixels, then back).
  • Paper transition (the one transition grammar): brayer rolls ink (a wet highlight sweeps) → paper lays down → baren spirals, the image soaks through → the sheet peels on a cylinder toward camera, heavy then fast, its back showing the mirrored show-through.
  • What never moves: the paper margin.

6. Camera grammar

The camera moves over the paper; the margin stays fixed. The knife tip, or the brightest cut, is the focus.

Move What it expresses Can serve
Track with the knife tip Making; the line is the story a first mark; a route drawn on a map; a signature
Continuous pull-back as cuts spread A line becomes a world; scale a city from one street; a family tree; a reach of sound or news
Locked plate A page of a wordless novel; reading time a decision; a portrait; the aftermath
Slow macro push on a sculpted close-up Patience, touch, labour hands at work; a tool; a letter being read
Snap zoom and hold Shock; the detail that breaks a crack; a wrong number; a face turning
Frame in a frame (door, window, arch) Distance, exclusion, waiting a crowd outside; a sickroom; a border
Hard cuts on the beat, each a new impression Accumulation, a series of prints offerings; days passing; a list of names
Crane up from a detail to the whole Consequence a flood reaching a village; a light reaching faces
Rack between two plates via the peel One state printed over another before/after; a promise kept; generations

Opening and ending come from the topic. Framing: black masses on carved white or white on uncut black, never grey on grey; text only in the margin. Transitions: hard cut (new impression) or the peel; never a dissolve.

7. Sound palette

  • Instruments: low strings (contrabass, cellos; spiccato, pizzicato), wood and skin (log drum, woodblock, frame drum), gran cassa, timpani, one tam-tam, synthesised struck metal (modal: 3–5 inharmonic partials + hammer transient); a bowed saw or hurdy-gurdy drone for a folk colour.
  • Options: withhold the timbre of the story's object until it appears; a metric modulation instead of a tempo jump; a modal shift inside a long decay; one drone under a locked plate.
  • Foley follows the material, all synthesisable: knife into wood (transient + band-passed fibre tearing tied to knife speed), chips, brayer tack, paper air, baren rasp, the wet peel; metals by size (large = low, small = high and pure); resonant objects as modal models plus echoes for distance.
  • Silence is a tool: near-silence with one faint ambience, or true digital silence before the loudest moment; the first sound after it is chosen.
  • J/L cuts carry sound across plates and peels.
  • Mix: music ducks ~8 dB under voice and a little under key foley; foley ducks too under voice. Pan hard transients off centre. −14 LUFS.
  • Voice: few lines, low and plain, like captions spoken; the plates tell the rest.

8. Native moves

A menu: use the ones your story needs.

  • Light is carved. A burst of radial cuts from a point. Fits content like: a power grid switched on; a scientific insight; a lighthouse restored.
  • Carving = time. Patient work on screen, cut by cut. Fits content like: a year of training; a building rising; a craft apprenticeship.
  • Mirrored block → true print. A backwards word reads true when the sheet is pulled. Fits content like: a vote result; a name cleared; a misunderstanding put right.
  • Stab cuts as texture. White dots can freeze mid-air or be inked back in until the black is clean. Fits content like: stars going out over a city; rain stopping; a disease receding on a map.
  • The one colour plate spreads and withdraws. Fits content like: a blood donation; a sunrise over a harvest; a single lit window in a blackout.
  • Filter a sculpt or a frame into form-following cuts. Fits content like: a portrait of a real person drawn from a photo; a machine part; an archive image re-cut.
  • Wordless-novel sequencing. Self-contained plates, few or no lines; a gap can be silent. Fits content like: a migration; a strike; a life in seven plates.

9. Pitfalls of the medium

  • White marks on white areas are invisible → stab cuts need dark masses behind them.
  • Hatching every surface looks like fur or rain → grazing light; 3–6 cuts on the lit edge only.
  • Black faces with white cuts don't read small → skin white, features black, a black keep-line around white parts.
  • Hand-drawn close-up props look like mittens and blobs → height field + woodcutFilter.
  • Short hatch segments with a tonal wobble turn into zig-zag chevrons → long cuts (14–50 × spacing), one broad highlight band, a thin rim light, a stronger halo.
  • Steam or smoke on top of the subject reads as flames → draw it behind, so only wisps escape.
  • Lazily cached strokes shared between shots make frames depend on the render worker → every cache builds itself on first use.
  • Fine hatching + ink noise + grain inflate the file → a higher CRF with -tune grain; check a 1:1 crop. woodcutFilter NaN in flat areas → clamp before sqrt.

10. Engine

demo/engine/index.js (imports only /core/lib.js) carves and prints any path or shape, on two canvases: a mask (black = uncut, white = cut) and an optional colour plate. Main functions: shape (carved solid with halo and form-following hatching), cutAlong (a path as hand-cut strokes), hatch (streamline hatching of a region), flecks (stab cuts), rays (carved light), drawStrokes, plate, woodcutFilter (image or frame → cuts), makePrinter(W,H).render (print / block compositor). Reusable demo pieces: demo/trans.js (paper peel), demo/hands.js (height-field sculpts). Option table, a minimal example and the filter recipe: DEMO.md.

11. Variation space

You decide the story, characters (or none), plates, opening, ending, camera path, pacing and what the one colour is for. All far from our demo:

  • Structures: a book of hours (seven locked plates, one per hour, no moves inside a plate); the same block recut (carved deeper each scene, the image changing as wood goes); two blocks printed in turn (two places alternate impressions until one sheet carries both).
  • Openings: the finished print on a wall (then back into the block to see it cut); white paper (an inked block pressed onto it: the first image arrives whole); wood grain that becomes water or a field as the first cuts follow it.
  • Endings: the block cleaned (ink washed off, pale carved wood remains); a stack of prints (rising over many sheets of one plate); an uncarved corner (holding on black wood the knife never reached).

How our demo was made (story, shots, score, end card, build, engine reference): 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 Bell Founder (58.5 s) · woodcut.mp4 · source in demo/

Brief the demo was made to: a 40–60 second film in the Woodcut Print style.

Story & structure

A village with an empty belfry; a founder casts its first bell, fails, sacrifices something of his own, and succeeds. How the native powers were used in this story:

Native power Story use
Light is carved Stories about fire, dawn, discovery, a lamp being lit: the moment of light can literally be a burst of radial cuts.
Carving = time The picture growing cut by cut is patient work on screen. It suits craft, labour, perseverance, and first times.
The mirrored block → the true print A reveal or a change of heart: the audience reads a backwards word, the sheet is pulled, and it reads true.
Snow = stab cuts White dots are wounds in the wood. They can freeze mid-air, or be "inked back in" until the sky is clean. That makes an ending no other medium has.
One colour plate Save it for the one thing that matters (molten metal, a heart, a flame). Let it spread at the climax and shrink to a single highlight afterwards.
Wordless-novel sequencing Four or five spoken lines at most. Plates carry the story; the gap after a failure can be completely silent.

Story shape of this film: first knife stroke in the dark (the horizon) → the world carved → the first print pulled (mirror → true) → a long close-up of hands → a quick montage of small offerings → a first attempt that fails in close-up → a silent plate → a personal sacrifice → a second attempt where the colour plate floods the frame → the longest silence → the payoff sound → the colour withdraws to one highlight → the white marks are inked back in → the last cut is the horizon again.

How we adapted the topic: find the one thing that should glow (the colour plate: molten copper), the one thing that is made by hand (the long close-up: the founder's hands), and the one thing that should disappear at the end (the stab-cut texture: snow). That was one reading of this story; other topics find other answers or none.

Shots

"The knife is the camera": the knife tip, or later the brightest cut, was always the focus.

Beat Camera
Hook (0–3 s) Extreme close-up on a black block; tracking with the knife as it cuts one white line (the horizon).
World Pull back 3.5× → 1× as mountains, roofs and the empty tower are carved: a line becomes a world.
First print Locked off for the ink / paper / baren / peel; then tilt down and push in to the empty belfry.
Breath 4 s near-static macro on the founder's hands (slow push 1.0 → 1.13).
Montage Hard cuts on every beat, speeding up (1 / 1 / 1 / .5 / .5 / .25 / .25 s), each landing with a jolt.
First colour Push in on the crucible as the copper turns orange, so the first colour lands in close-up.
Failure Snap zoom (0.2 s) to the bell wall and hold 1.3 s while the crack climbs.
Silence Frame in a frame: the workshop door, villagers outside, the founder's back, the hammer slowly lowering. Nothing moves but snow.
Climax Start on the stream only (z 2.4), radial cuts burst from the mould, then crane up and out to show both faces lit.
Signature 8× → 1× continuous pull-back from the bell wall to the whole valley as the bell rings: the sound travels as far as the camera does.

Why this route, for this story: the knife-drawn horizon was both the first and the last cut, so the film closed a circle; the ringing bell and the pull-back had to travel the same distance. Each of these is one choice from STYLE.md §6, not the style's default opening or ending.

Demo action timings: hammer lean-back 4 frames → strike 2 frames → settle + 3-frame camera shake; bellows push with the whole body; the throw is clutch → release → hand holding in the air. Impression jolt: [5,−3] then [−2,1] px.

Score structure

  • Grid first: 60 BPM, switching to 90 BPM through a 3:2 metric modulation (60's triplet eighth = 90's eighth) for the forge. timeline.json is the single source of truth. tools/cuecheck.py checks score cues against picture events and both silences (demo: 25 cues, max offset 0 ms).
  • Score: low strings only (contrabass, cellos, spiccato, pizz; violas only after the bell) + wood and skin (log drum, woodblock, frame drum) + gran cassa / timpani + one tam-tam + a synthesised anvil (modal numpy: 3–5 inharmonic partials + hammer transient). No bell timbre anywhere before the bell rings: the village "has no voice", so the first bell is the climax. D minor, turning to D major in the bell's decay.
  • Foley of this film, all synthesised: knife into wood, chips, brayer tack, paper air, baren rasp, the wet peel; brass by size (pot = low, wide; keys and ring = high, pure); clay scrape and smash; bellows leather + air; molten bubbles, pour roar, hiss into the mould; the bell as a modal model (hum 0.5, prime 1, tierce 1.2, quint 1.5, nominal 2, 2.5, 3, 4 with their own decays and beating) + two valley echoes.
  • Two real silences: 29.7–32.3 s (−40 dB distant wind only; the first sound after it is the compass lid's click) and 44.3–47.3 s (digital silence, measured −91 dB; the first sound after it is the bell, the loudest moment of the film).
  • J/L cuts: fire crackle enters before the hands shot; outdoor wind carries into the workshop muffled; wind leads the peel into the belfry; the bell's tail runs under the end card.
  • Mix: music ducks ~8 dB under voice (6 dB after the bell) and 4 dB under key foley. Foley ducks too (−5 dB, −9 dB under a line that shares its beats with metal hits). Narrator: Kokoro am_onyx at 0.88, 4 lines.

Palette & props

  • Palette: ink #111111 · paper #EFE8D8 · the wood block itself #D8C29C (only in shots that show the block) · one colour plate "copper" #C8502A.
  • Print frame: 1920×1080, printed image area 36, 36, 1848×912. The bottom paper margin (132 px) holds captions. Fixed 3 px colour-plate misregistration.
  • Props and cast: the founder and his apprentice (parts rig, poses, expressions), the valley (mountains, village, empty tower, snow), the workshop, crucible, pour, sparks and mould, the bell, bellows, the villagers' brass gifts, the compass, sound rings. The demo's hands, the compass and the filter still were made with height-field sculpts + woodcutFilter.
  • Captions: IM Fell English 42 px, ink #111.

End card

Title carved mirrored into the block (IM Fell English SC), readable only once the first print is pulled. End card: a fresh print: title carved out of a black panel, a small bell keeping the film's last copper highlight, then A Woodcut Print, Lemo-Opuscar, LemoLab × Claude Opus 5.5 and credits in letterpress. The final knife cut under the title echoes the first sound of the film. 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/woodcut/demo/
  engine/        index.js (shape, cutAlong, flecks, rays, plate…), knife.js (strokes), hatch.js (regions, streamline hatching),
                 print.js (WebGL2 print/block compositor), filter.js (image → woodcut)
  stage.js       print frame, camera, caption          world.js   the valley (mountains, village, tower, snow)
  chars.js rig.js views.js   founder & apprentice (parts rig, poses, expressions)   hands.js   height-field sculpts → filter
  interior.js fx.js          workshop, crucible, pour, sparks, mould; bell, bellows, gifts, villagers, sound rings
  trans.js       paper curl / peel        shots.js shots2.js   the 17 shots        film.js main.js   assembly, captions, events
  timeline.json  tempo grid + every cue   music/score.py   original score   mix.py   foley + ambience + voice + ducking + bell
  tools/         cuecheck.py subs.py final_asr.py mux.sh      test.js   ?test=char|hands|pour|village|sheetA|sheetB|filter|spark
  1. Write timeline.json (grid + cue keys) and the cue table first; hand them to a music sub-agent; draw in parallel.
  2. Build the engine and one style frame before any story. Get the carve + print look right on a still.
  3. sh demo/build.sh rebuilds everything: TTS → whisper → score → events → cue check → mix → srt → render (4 workers ≈ 45 s for 1404 frames) → mux (−14 LUFS, grain 6, CRF 28) → whisper on the film → styleframe / poster / engine example.
  4. Review twice with 1–2 fps contact sheets from the film itself, plus full-size frames of every key action (peel, smash, crack, rays, bell pull-back, end card).

Pitfalls tied to this demo's props:

  • Snow on white snow is invisible. Add black snowbanks and wind-carved ground; flakes read on the sky and on dark masses.
  • The first bell turned into zig-zag chevrons from short hatch segments with a tonal sine wobble; fixed with long cuts and one broad highlight band.
  • Steam from the mould drawn on top read as flames; moved behind the subject.
  • Lazily cached strokes (X.coatFolds || []) broke determinism across render workers.
  • Metal gift hits masked the narrator (whisper heard "is had" for "it had"). Foley has to duck under voice too. Pan the offending hit away from centre.
  • File size: fine hatching + per-impression ink noise + grain 6 made a 58 s film 326 MB at CRF 19. Re-encode at CRF 28 with -tune grain: 93 MB, and no visible difference at a 1:1 crop.
  • The structure tensor NaN came from the box blur leaving tiny negative sums in flat areas.
  • still.mjs can fail when many renders run at once. Retry up to 3 times.

Engine reference

demo/engine/index.js is self-contained (it only imports /core/lib.js). It draws any path or shape as a carved relief and prints it. You always work on two canvases: a mask (fill black = uncut block; draw white = cut away) and an optional colour plate (alpha = plate density), then call the printer.

Function What it does Key options
canvas(w=1920, h=1080) → [canvas, ctx]
shape(polys, o) A carved solid: black silhouette + white halo + form-following hatching lit by light. Returns { draw(g, t), strokes, halo, reg, tone }. light (light(x,y,z)), sp spacing, wmax, lo/hi tone range, dir ('contour' / angle / dirRing(cx,cy,sy) / dirRadial(cx,cy) / fn), halo px, white (white part with black cuts), tone(reg)→(x,y)=>0..1 override, seg, gap, reveal:{t0,t1,key,speed}
cutAlong(path, o) Breaks a path into hand-cut knife strokes (outlines, rules, rope, cracks). w (number or (u,pt)=>w), kind `'k'
hatch(region, o) Streamline hatching of any Region (evenly spaced, direction and width fields). dir(x,y), tone(x,y), sp, wmax, seg, gap, kind, reveal
flecks(pts, o) Stab cuts (snow, sparks, chips). pts = [[x,y,r],…] angle, seed
rays(cx, cy, o) A burst of carved light. n, r0, r1:[a,b], w, jit, bend, a0/a1, reveal:{t0,t1,jit}
drawStrokes(g, strokes, {t, color}) Draws strokes onto the mask at time t (a reveal-timed stroke is carved progressively, knife tip and all). color:'#000' re-inks.
plate(c, polys, a) Paints the one colour plate. It shows only where the mask is cut white.
woodcutFilter(img, o) Image / video frame → woodcut. Luminance sets cut width, the structure tensor sets cut direction; returns strokes with reveal times. rect:[x,y,w,h], sp, black/white/gamma levels, res, region, `reveal:{t0,t1,mode:'light'
makePrinter(W,H).render(mask, plate, o) WebGL2 compositor → canvas. `mode:'print'
light(x,y,z), `Region(polys drawFn, {res,bbox}), mkStroke, spline, offsetPoly, ellipse, rect` helpers

Demo-level, reusable: demo/trans.js curl(g, front, back, xf, r) + peelState(u) (the paper peel); demo/hands.js Sculpt (capsule / blob / plank / creases → shade(light)) for close-ups.

Minimal example: a warm orange #D97757 four-point spark with a cursor tail, carved in and kept in its own colour. It is exactly ?test=spark in demo/test.js; the result is demo/stills/engine_example.jpg.

import * as WC from './engine/index.js';
const [mask, m] = WC.canvas(), [plate, c] = WC.canvas(), P = WC.makePrinter();
const star = []; for (let i = 0; i < 8; i++) { const a = i / 8 * Math.PI * 2 - Math.PI / 2, r = i % 2 ? 70 : 260; star.push([960 + Math.cos(a) * r, 500 + Math.sin(a) * r]); }
const spark = WC.shape([star], { light: WC.light(.5, -.6, .6), sp: 9, halo: 8,
  reveal: { t0: 0, t1: .8, key: (x, y) => Math.hypot(x - 960, y - 500) / 300 } });        // carved from the centre out
const tail = WC.cutAlong([[1030, 590], [1090, 680], [1110, 770]], { w: 24, kind: 'u', seg: [300, 400], reveal: { t0: .8, t1: 1.1, speed: 900 } });

function frame(g, t) {
  m.fillStyle = '#000'; m.fillRect(0, 0, 1920, 1080);                                       // the uncut block
  c.clearRect(0, 0, 1920, 1080);
  WC.drawStrokes(m, WC.rays(960, 500, { n: 36, r0: 300, r1: [420, 640], w: 10, seed: 2, reveal: { t0: .5, t1: 1.0 } }), { t });
  spark.draw(m, t);
  WC.drawStrokes(m, tail, { t });
  WC.plate(c, [star], t > .8 ? 1 : 0);                                                      // the one colour
  g.drawImage(P.render(mask, plate, { seed: 4, plate: '#D97757' }), 0, 0);
}

Filter on a photo or film frame (for re-drawing footage): const F = WC.woodcutFilter(video, { rect: [36, 36, 1848, 912], sp: 5, reveal: { t0, t1, mode: 'light' } }); m.fillStyle = '#000'; m.fillRect(…); WC.drawStrokes(m, F.strokes, { t }); then print. About 1,900 cuts and 140 ms for a 1080p frame. See demo/stills/filter_v1.jpg and ?test=filter&img=…. For a moving shot, re-run it on twos (12 fps) with the same seed so the cuts stay stable where the image does.