FilmForge Motion
Paintings hold like film frames; no zooms, no drift. Movement comes from inside the frame: particles, fog, flicker,
shimmer, light, all rendered on the GPU for nothing. Paid video is kept for what code cannot do (real flight, a
transformation that is the film's climax). This skill fills each shot's motion field, applies the QA fixes,
exports the timeline for the renderer and renders the preview the director signs off.
Paths: scripts/, templates/ and config/ below are folders inside this skill (the base directory shown when it loads), put there by ./install.sh. Working from a clone of the repository, they are tools/, templates/ and config/ at its root.
The renderer is painted-film-pipeline (Python and GLSL; modules named below). All pixel values are 4K
(3840x2160); the renderer scales them by K = W / 3840, so a 1080p preview uses the same numbers.
Step 1. Read the shot
For each shot: its beat, its scene's mood and colour script, its length after retime, the music under it (phrases, hits in beatmap.json), what is already in the painting (fire, sea, sky, glass). Life must belong to the world of the painting: embers rise from a fire that is painted there; snow falls where the sky is.
Step 2. Choose its life (the vocabulary)
| Effect | Parameter (shot field) | Values |
|---|---|---|
| Drifting fog band | fog + fogc | (strength 0..1, y where it starts, y where it is full, drift speed); colour (r, g, b) 0..1 |
| Light sweep over highlights | sweep | (period s, band width px, intensity, 0) |
| Firelight flicker around a point | flick + fire | amplitude (0.07 to 0.45); (x, y, radius px) |
| Heat shimmer in a band | shim | (y full, y zero, 0, strength) |
| Camera shake | shake | (start s, length s, amplitude px) |
| Transition in / out | tin, tout | ("fade" or "white", seconds); default is a hard cut |
| Reframe | crop | (x0, y0, x1, y1) in source pixels |
| Particle tint | ashc | (r, g, b) |
| Particles | life | a recipe or a lambda over the particle systems below |
| Lightning | bolts | list of times in the shot |
| Glass crack | crack | (start s, length s) |
Particle systems (painted.py): ash(t, n, wind=(vx, vy), size=(min, max), alpha, seed) for ash, dust and snow;
flakes(...) for large soft flakes; sparkles(t, n, alpha, size) for glints and stars; streaks(t, n, speed) for
rain; embers(t, n, src=(x, y), spread, rise, life) for embers rising from a source. Recipes from the real film
(film_cut.py): SNOW(n, wind), EMB(n, alpha), GLINT, RAIN, STARS(n), GOLD. Full parameter meanings:
references/pipeline-modules.md; tested mood recipes: references/effects-cookbook.md.
Graphics (graphics.py, overlays.py): golden thread ribbons that draw on, sway with the music and snap
(T(points, t0, draw, w)); handwritten words written on left to right (W(text, t0, x, y, size)); place names that
form out of the scene's own material and blow away (V(...)); the title finale.
Step 3. Let the music drive it
graphics.Drive measures the score per frame (level and pulse). Bloom, ember flare, thread glow and text breathing
follow it, so a hit flares the embers without a keyframe. Put transitions and shakes on hits from beatmap.json
(shake start = the hit's time minus the shot's start).
Step 4. Write the motion field
In shotlist.yaml, per shot (example: a fire-forest shot from Simorgh, and The Lantern Moth's beam):
motion:
life: EMB(320)
flick: 0.09
fire: [1920, 1300, 2600]
shim: [2160, 300, 0, 0.8]
ashc: [0.62, 0.55, 0.5]
motion:
tin: [white, 0.4]
sweep: [3.0, 520, 0.35, 0]
life: GOLD
shake: [0.0, 0.4, 10]
A shot that needs nothing gets motion: none (the gate wants every shot decided).
Step 5. Apply the QA fixes
For every fix in qa.yaml: run its call (grade.py recolor or blue, a patch, a crop on the shot), write the _g
file, re-measure with ledger.py palette, then set done: true. The matched grade (plate levels) and the 4K
upscale are automatic in the renderer.
Step 6. Export, render, look
python3 scripts/shotlist.py export-cut shotlist.yaml --paintings art/film > code/timeline.py
prints the timeline as P_() lines for film_cut.py. Then, in the renderer:
W=1920 H=1080 python film_cut.py still out/frames 6.0 12.0 24.0 # stills at the story hits
python film_cut.py sheet out/sheet.jpg # one frame per shot
python film_cut.py video film/preview_1080p.mp4 audio/score.wav # the preview with the score
Watch the preview with sound. Check the stills at every hit. Then the director signs off the motion stage.
When to pay for video
Only when the motion is the story and code cannot fake it: flight (Simorgh's launch: real clips with speed ramps),
a birth or transformation at the climax. Cost it in budget.py (Kling 4.0: 16.25 credits per 5 s at 1080p, plus the
keyframe painting); paint the keyframes first and pass them through QA; draft cheaply where the model offers a draft
and finalise one take. Everything else is told in painted key poses that snap (poses: 2), the way anime holds a
pose.
Output
- every shot's
motionfield (ornone) code/timeline.pyfrom export-cut- the applied fixes (
_gfiles,done: truein qa.yaml) film/preview_1080p.mp4, stills at the hits, a one-frame-per-shot sheet
Gate checklist
- Every painted shot has a motion plan or
motion: none - Every QA fix applied and re-measured
- The preview watched with sound; stills at every hit checked
- Signed off
Repair table
| Symptom | Fix |
|---|---|
| It feels like a slideshow | Every hold needs one living layer tied to its world (particles, fog, flicker); vary them by scene |
| Effects look pasted on | Tint particles with ashc from the painting; emitters at painted sources; fog colour from the sky |
| Busy, noisy frames | Fewer particles (halve n), lower alpha; one effect family per scene |
| Shots from different generations do not cut together | The matched grade is off for that plate: check levels; one grade, grain and vignette for all |
| Effects ignore the music | Hits not used: place shakes, flares and transitions on beatmap hits |
| Text is hard to read | Move it to the painting's quiet area (text_area), size up, slow the write-on |
References
references/pipeline-modules.md: the renderer's modules and every parameterreferences/effects-cookbook.md: tested recipes per mood, from a finished film