Silkscreen Travel Poster — our demo

One example among many. Don't reuse its story, arc, shots, props or timings.

Demo: The Long Way Home 繞遠路回家 (50 s) · silkscreen-poster.mp4 · subtitles silkscreen-poster.srt · source in demo/

Story & structure

A traveller wants to get "as far from home as the world allows". Every city is one full-bleed screen print with its landmark as the hero, and a ticket stub on the right counts the distance from home: Beijing 0, Istanbul 7,060, Paris 8,220, Buenos Aires 19,270 km. No point on Earth is more than about 20,015 km from Beijing, so Buenos Aires is almost the far side of the world. The turn: on a round world, past that point every road leads home. The ending: the same Temple of Heaven print is pulled again in dusk inks, its loose layers snap into register, and the stub reads FROM HOME 0 KM · TRAVELLED 39,630 KM, almost once around the Earth. Narrated in English (Kokoro bf_emma), Chinese and English subtitles.

The signature moves, each pushed further than the time before:

Beijing Istanbul / Paris Buenos Aires Beijing again
Layout full-bleed print, city name in the sky, ticket stub printed last same stub template, bigger number the stub's number fills the screen until the weave shows the stub gains TRAVELLED; the end card is a wider ticket
Colour accent first, one ink per pull, bare paper as marble a diagonal sweep prints the next city behind the blade La Boca's houses in one split-fountain pull the same geometry re-inked for dusk, then night
Registration normal 2 px Paris: layers loosen along a crane up the tower layers drift tens of units, paper gaps show one snap to zero, the only perfect register in the film

Shots

24 fps, 108 BPM waltz (beat 0.556 s, bar 1.667 s, 30 bars). The camera never leaves the print: z ≥ 1, the view clamped inside the art.

# Time (s) Move Picture Why
1 0–3.33 close-up inside the print, stepping back one level per pull (log zoom) sun alone, then gold sky with paper rings, then stepped rose bands the hook: colour arrives in the first second
2 3.33–5.56 to the full frame; the screen lifts at 4.44 (the frame's bottom bar sweeps up through the picture) woods, three blue roofs, marble terraces (paper), cypresses; red drums and gold finial last a picture built from layers
3 5.56–10 slow push toward the stub BEIJING 北京 in the sky; DAY 1 · FROM HOME 0 KM · TEMPLE OF HEAVEN BUILT 1420 the words and the narration land together
4 10–14.17 a cypress silhouette crosses the screen (full cover for about one frame), then Istanbul prints on sixteenth notes Hagia Sophia across the water, a ferry; 7,060 KM · BUILT 537 faster by subdividing the beat, same tempo
5 14.17–17.78 diagonal sweep prints Paris behind the blade; crane up the tower on a 1920×1620 print Eiffel Tower, mansard roofs; layers loosen with parallax; 8,220 KM · BUILT 1889 a city change is a colour change
6 17.78–21.94 split-fountain sweep (six inks in one bead); lateral truck over a 2600-wide print La Boca houses, the Obelisco as bare paper; 19,270 KM · BUILT 1936 the far side, everything loose
7 21.94–27.78 screen-space push into the stub's number (×5.9); the screen comes down, mesh visible music thins out; 26.86–27.78 wind only the turn needs silence
8 27.78–33.33 clamp jolt; night inks swept on while still close; pull out; long truck over a 5200-wide night sea moon, stars as bare paper; counters running the way home in one breath
9 33.33–36.67 sweep to the Temple of Heaven at dusk; layers float with paper gaps counters land: 0 KM · 39,630 KM back where it began
10 36.67–42.22 snap to register on the downbeat; slow push "Beijing again. Zero kilometres." the only perfect register
11 42.22–50 night re-ink sweep; the end card prints at 46.2 (panel) and 46.6 (type) gold hall, moon; THE LONG WAY HOME 繞遠路回家 the ending echoes the opening

Transitions are made in the medium: a squeegee pull, the screen lifting and coming down, a foreground silhouette, a diagonal sweep that prints the next poster, the clamp. No fades, no bare cuts.

Score structure

Palette & props

One palette per print, ≤ 6 inks plus a lit-face ink; paper #F3EAD8. The ticket stub is always the print's near ink; its text inks are picked by contrast (pickInk).

Print sky1 sky2 far mid near accent lit face
Beijing dawn #F5C978 #E8866A #9A88AE #22447C #1B1F34 #D8432B #5D86C4
Istanbul #F6DFB2 #E9A27A #8DB9BE #C9705A #1F2D3A #F2C14E #6E8BA3
Paris #F4E6CC #A9C1DE #C3B3D2 #5B6E9C #252B42 #E4553F #EDC77E
Buenos Aires #F8E7B0 #82C7D8 #B9A9C6 #3B5B7A #232B3A #E2543D #F7E2A6
Night sea #2C3B69 #1C2650 #46598A #203A63 #111A2F #F2D28A #3F6395
Beijing dusk #F2A766 #A25A84 #6E5C8E #26386E #151A2C #E4502F #5578B4
Beijing night #2A3762 #18204A #3A4778 #1F2D5C #0F1426 #F2BE4E #3D5A97

La Boca's split-fountain bead: #D9483B #F2B63D #2F86B5 #4E9C5E #E07B4A #8E5BA6.

Landmarks are stylised silhouettes, built from paths in engine/scenes.js, not traced from photographs; no logos, no brands, no maps. Bare paper does the bright work: the marble terraces and the walkway in Beijing, the Obelisco, the stars.

Titles & text

End card

At 46.2 s a wider ticket (560×600) is pulled over the right side of the night print, then its type: THE LONG WAY HOME / 繞遠路回家, SILKSCREEN TRAVEL POSTER / 絲印旅行海報 and LemoLab × Claude Opus 5.5. It holds to the end (3 s). The sign-off belongs to this library's demo only; a user's film carries no LemoLab credit.

Build

sh styles/silkscreen-poster/demo/build.sh (or LIB=<library> sh demo/build.sh) rebuilds everything: fonts (tools/fetch_fonts.sh), narration if missing (core/tts/tts.py → demo/out/voice/) and the speech-to-text check, subtitles (tools/cues.py → voice/cues.json and ../silkscreen-poster.srt), events, score (music/compose.py), mix (mix.py), picture (--workers 2), master (−14 LUFS), readcheck, poster.jpg and demo/stills/styleframe.jpg. sh demo/build.sh 28 36 renders a span. About one minute on Apple silicon.

Facts on the stubs (distances by the haversine formula, landmark years) and their sources: demo/FACTS.md. Third-party material: demo/CREDITS.

Rules the build follows: - A knocked-out shape (knock) is filled even-odd together with its parent, so its sub-paths must not overlap; to cut one layer out of another use cut, which clips with the inverse. - Every ellipse added to a Path2D starts with its own moveTo and ends with closePath, or the arcs join into stray wedges. - Stub type, header type and end card type pick inks by contrast against near with a light fallback, so night palettes stay legible. - The ticket stub and the push into it live in screen space (OZ), so a crane or a truck over a tall or wide print doesn't move them. - Bars, beats and every hit come from one timeline in film.js; events.json drives the score and the foley.

Engine reference

engine/silk.js prints any shape in this style: it only needs a Path2D in whatever units the current transform uses.

Function What it does
makeTextures(paperHex, seed) paper, mottle and pinhole tiles (once)
paperSheet(ctx, T, x, y, w, h) paper stock
ink(ctx, part, o) one ink layer. part = { path, knock?, paint? } (knock = knocked-out holes, paint(ctx) = custom fill such as text). o = { color, reg:[dx,dy], clip, sheen, trap }
wipeRegion(box, dir, p, seed) / wipeFront(...) the ragged squeegee front as a clip path, and where the blade is
squeegee(ctx, a, b, lead, o) blade from a to b moving along lead; o.beads=[{t0,t1,color}] (split fountain), roll, handle 0–1, scale
screenFrame(ctx, box, o) hinged screen with mesh, emulsion and tape; lift 0–1
inkFinish(ctx, T, x, y, w, h) mottle and pinholes over the sheet
bandSteps(x, w, y0, y1, n) stepped colour bands (the gradient substitute)
ridgeLine, polyPath, circlePath, ringPath, rectPath, unionPath shape helpers

engine/scenes.js: temple, istanbul, paris, boca, sea return { w, h, header, depth, parts: [{ pull, ink, path, knock?, cut?, lit? }] } (a print of any size; pull names the squeegee pass, depth drives parallax and paper gaps). engine/poster.js: drawArt(ctx, scene, { pal, T, prog, off, sep, header }) prints a scene (partial pulls, loose layers, split fountain via ink: 'fountain'); drawStub, drawEndCard, pickInk; drawPrint is the opening's Beijing print with the stub in art units. film.js: the timeline, cameras (camS art camera inside a screen-space zoom OZ), sweep() (a diagonal pull that prints the next print behind the blade), treeWipe(), subtitles, window.TEXTS.

Minimal example: a warm-orange #D97757 four-point spark with a cursor tail, printed in two inks and knocked out of a sky, then pulled in by the squeegee:

import { makeTextures, paperSheet, ink, wipeRegion, wipeFront, squeegee, inkFinish, polyPath, rectPath, circlePath } from './engine/silk.js';
const T = makeTextures('#F2E8D2');
paperSheet(ctx, T, 0, 0, 800, 600);
const spark = polyPath([[400,180],[430,270],[520,300],[430,330],[400,420],[370,330],[280,300],[370,270]]);
const tail = rectPath(540, 285, 160, 30);
const p = 0.7;                                                    // 70 % of the pull done
const clip = wipeRegion([0, 0, 800, 600], 'right', p, 3);
ink(ctx, { path: rectPath(0, 0, 800, 600), knock: circlePath(400, 300, 150) }, { color: '#1C2A33', clip, sheen: 1 });   // sky with a knocked-out halo
ink(ctx, { path: spark }, { color: '#D97757', reg: [2, -1], clip, sheen: 1, trap: 1 });
ink(ctx, { path: tail }, { color: '#D97757', reg: [2, -1], clip, sheen: 1 });
const x = wipeFront([0, 0, 800, 600], 'right', p);
squeegee(ctx, [x + 10, -20], [x + 10, 620], [1, 0], { color: '#D97757', roll: x / 30 });
inkFinish(ctx, T, 0, 0, 800, 600);

A new place is a new scene in scenes.js (its parts in print order, one palette in film.js); the story and timeline stay yours to write.