--- name: motion-background description: Build an ambient backdrop on a NodeTool timeline using shape gradients, procedural generator effects, slow loops, or generated video. Use for title cards, hero beds, end cards, lower-third plates, and quiet loops behind a subject. featured: true --- # Motion Background → the bed A background that draws attention has failed. Slow, low contrast, looping with no visible join, and cheap enough that the picture in front of it still composites. `motion-graphics` carries the op contract for the calls named here. `color-motion` chooses the palette the bed is built from, and `motion-curves` writes the loop when no wrapping preset holds the seam. ## Pick the cheapest thing that reads | Look | Build it from | Cost | |---|---|---| | Flat or gradient wash | One full-frame `rect` shape with `fillStyle` linear or radial | Shape clips do not count against the video layer cap | | Soft mesh or conic wash | One full-frame shape with a `generator` effect, `mode: "meshGradient"` or `"conicGradient"` | One procedural effect, no model call | | Shaped colour blobs | Radial-filled `ellipse` clips, low opacity, blurred | More control over each blob's motion | | Noise, fractal, particles, light leak, grid | One full-frame shape with the matching `generator` mode | Procedural and seedable | | Drifting texture | An image clip with a `kenBurns` animation | Outside the video layer cap | | Smoke, ink, or clouds | A generated video bed, looped | One video layer and a model call | | Colour cycle | Any of the above plus a `hueShift` loop | An additional grade | At most **eight video layers** composite at once, resolved from the lowest track index. Text, shape and image clips are not counted, so a bed built from shapes costs nothing in that budget. Nine overlapping video clips means the bottom one silently does not draw, reported as `layer_cap_exceeded`. ## A procedural bed Add a full-frame shape and apply a generator effect to it. Modes are `noise`, `fractal`, `conicGradient`, `meshGradient`, `gradientField`, `particles`, `lightLeak`, and `gridPattern`. `colorA` and `colorB` set the two colours; `scale`, `amount`, `angle`, `time`, and `seed` shape the field. For movement, set `animate: true`, which samples the current timeline time into the effect. Keep a fixed `seed` for repeatable frames. This clock keeps moving; it does not make the procedural field wrap at the clip boundary. For a seamless loop, use a wrapping clip animation or match the endpoints in a rendered loop. ```json {"op": "set_effects", "target": "Bed", "effects": [ {"type": "generator", "mode": "meshGradient", "enabled": true, "colorA": "#10233c", "colorB": "#ec6b45", "scale": 6, "amount": 0.6, "seed": 9, "animate": true}]} ``` For a dark wash that bands, put a fixed-seed `stylize` `dither` after the generator or gradient in the effect list. Dither breaks up 8-bit steps but cannot restore precision lost in the source. `color-motion` covers this look. ## A mesh from shapes For individual moving blobs, stack radial fills over a base. Each ellipse is its own clip on an overlay track, at 30–60% opacity, with a `blur` effect wide enough that no edge reads: ```json {"op": "add_shape_clip", "shape": {"kind": "ellipse", "x": 0.05, "y": 0.1, "width": 0.6, "height": 0.7, "fillStyle": {"type": "radial", "stops": [ {"offset": 0, "color": "#5b8cff"}, {"offset": 1, "color": "#5b8cff00"}]}}, "durationMs": 12000} ``` ```json {"op": "set_effects", "target": "Ellipse", "effects": [{"type": "blur", "radius": 120}]} ``` Then give each blob a different `loop`: `float` with `amplitude` 0.02–0.05, `breathe` at `intensity` 0.03–0.08, and cycle lengths that do not divide each other — 9000, 13000, 17000ms — so the composite never visibly repeats. `blendMode` `screen` or `add` on a blob over a dark base builds light rather than covering it; `multiply` or `soft-light` deepens instead. That choice matters more than the fill colours. ## Loops that actually loop `float`, `breathe`, `rotate` and `hueShift` are built to wrap: they start and end at the same value, so `durationMs` is a period and the seam is invisible. A custom loop curve must do the same — the value at `t: 1` has to equal the value at `t: 0` or the bed jumps every cycle. `kenBurns` is the exception: it is a one-shot across the whole clip, holding at its end, so it does not repeat and its `durationMs` is ignored. On a long bed a drift from `kenBurns` finishes and then sits still. For continuous drift over a long clip use `float` with a long period instead. Periods that read as ambient: 8000–30000ms. Anything under about 5000ms reads as a screensaver, and text in front of it becomes hard to hold. ## Keep it behind - Background sits on the **highest** track index, because the lowest draws on top. A bed that covers the picture is a track-order mistake, not an opacity one. - Contrast: pull it down with a `color` effect at `saturation` 0.5–0.7 and `brightness` slightly negative before touching opacity. A desaturated bed reads as depth; a faded one reads as a mistake. - Amplitude: nothing in the bed should travel more than about 3% of the frame, or the eye starts tracking it. - Give the foreground a scrim if the bed's bright regions cross the type: a black `rect` shape at 40–60% opacity on a track index between the bed and the text. `validate_timeline` reports `text_illegible` for what it can measure, and says nothing about gradients — look at the frame. ## A generated bed For smoke, ink, or clouds that the procedural modes cannot draw, generate video. Use `find_model` with `text_to_video`, then `generate_video`, then `add_media_clip` with the returned `asset://` reference. The `find_model` result names a `prompting_skill` for the line it picked; load it, because each line wants the prompt shaped differently. Whatever the line, prompt for slow, even motion with no cuts and no subject; a bed with an event in it is a shot, not a bed. On a line that writes native audio (Seedance 2, Veo 3, MiniMax H3, Kling 2.6 and later) the bed arrives with a soundtrack: either brief it as room tone and keep it, or mute the clip under the track you meant to use — `video-audio-continuity` is the rule for which. A generated clip does not loop. Cover a longer sequence by duplicating it and crossfading the joins: overlap the copies by 800–1500ms on one track, which dissolves with no call at all, or `set_transition` `crossfade` on the incoming copy to pin the length. ## Reduced motion, and knowing when to stop A bed exists to sit under something. Before adding a second idea to it, check whether the first one is doing anything at all — a drift too slow to see is budget spent on nothing, and one too fast is the reason nobody read the headline. One idea, one amplitude, one period family. ## Check it - `preview_timeline_frame` with a `range` across a full cycle and `sheet: true`, which is how you see whether a loop breathes evenly or lurches. Sampling three timecodes will not show a seam. - The same sweep with the foreground on, to confirm the type still reads at every point in the cycle rather than only where you happened to look. - `render_timeline` at a low `preview_scale` while iterating. A bed is judged over seconds, and a still cannot tell you whether it is calm.