color-motion
GitHub用于在时间线上选择和动画化颜色,包括形状、文本渐变、色彩分级、3D LUT及抖动。适用于构建视觉风格、匹配镜头或修复画面模糊、褪色等问题。
Trigger Scenarios
Install
npx skills add nodetool-ai/nodetool --skill color-motion -g -y
SKILL.md
Frontmatter
{
"name": "color-motion",
"description": "Choose and animate colour on a NodeTool timeline, including shape and text gradients, colour grades, 3D LUTs, and dither. Use when building a look, matching shots, animating colour, or fixing muddy, washed-out, or banded frames."
}
Colour in Motion → palettes, grades and what can move
Choose colours for shapes and type, then grade the picture around them. Authored colours, gradient stops, and effect fields can move through animation style tracks as well as the preset grade channels.
timeline-edit-ops carries the op contract for the calls named here.
motion-direction decides whether colour is one of the channels this piece
moves at all.
Build a restrained palette
One primary, one accent, two or three neutrals. Get depth from lightness and chroma, not from more hues — extra hues fight for attention and read as amateur.
Reason in OKLCH while you choose (oklch(L C H): perceptual lightness, chroma,
hue angle) because holding L and C while rotating H gives hues of equal visual
weight, which HSL cannot do. Prefer hex in the document for static colours;
the shared parser also accepts named, RGB, and HSL colours in style tracks.
On dark plates make the accent pop with high lightness and moderate chroma rather than maximum saturation. A blown-out saturated accent vibrates on black.
Tint the neutrals a little toward the primary hue. Pure grey next to coloured elements reads as disconnected.
Where colour lives
| Surface | Field |
|---|---|
| Text | textStyle.color, plus stroke.color, shadow.color, background.color |
| Text, gradient | textStyle.fill — wins over color when set |
| Shape | shapeStyle.fill and stroke, or shapeStyle.fillStyle for a gradient |
| Captions | captionStyle.color, activeColor, outline, background |
| Transition | set_transition dipToColor color |
| Picture | set_effects — color, curves, levels, liftGammaGain, lut, stylize, generator |
A gradient fill is {type: "linear", angle, stops}, {type: "radial", stops} or
{type: "solid", color}. Stop offsets are 0..1, so a fill is independent of the
shape's size:
{"op": "add_shape_clip", "shape": {"kind": "rect", "x": 0, "y": 0,
"width": 1, "height": 1,
"fillStyle": {"type": "linear", "angle": 90, "stops": [
{"offset": 0, "color": "#0b0f1a"}, {"offset": 1, "color": "#1b2440"}]}}}
Move authored colour and the grade
Use styleTracks on a stored animation to interpolate authored colours or
gradient fields. Supported targets include text.color, shape.fill,
shape.stroke, text.fill.angle, text.fill.stops.0.color,
shape.fillStyle.angle, and shape.fillStyle.stops.0.color or .offset.
The target must name a field the clip already has. text.color is hidden by a
text gradient; shape.fill is hidden by shapeStyle.fillStyle. Colour
keyframes accept supported CSS colours, including hex, RGB, HSL, and names.
Effect color tracks can address effect.<effect-id>.colorA or .colorB on
generators, and .color on drop shadow, chroma key, or the color-using stylize
modes: lightRays, lensFlare, innerShadow, innerGlow, edgeHighlight,
and lightLeakOverlay. The glow renderer does not use glow.color.
The field must already exist on the effect. A string field such as mode is
not a color target.
For a document with a text gradient, this animation moves its first stop:
{"id": "colour-shift", "role": "in", "preset": "fade", "durationMs": 600,
"styleTracks": [{"target": "text.fill.stops.0.color", "keyframes": [
{"t": 0, "value": "#8bd5ff"}, {"t": 1, "value": "#ffffff"}]}]}
edit_timeline's animate_clip input does not accept styleTracks; it
discards that field. Author these animations in the whole document through
set_timeline_document, which validates before writing and snapshots the
previous document. See timeline-edit-ops for that call's contract. For a
targeted edit through animate_clip, use numeric grade presets or custom
curves. Three useful approaches:
- Animate the grade channels.
hue(degrees, adds),saturation(0..4, multiplies),brightness(−1..1, adds),contrast(0..4, multiplies),temperatureandtint(−1..1, add) are all animatable properties. Acustomcurve onsaturationfrom 0 to 1 is a colour bloom;colorFadeis that as a preset, andhueShiftis a full wheel perloopcycle. - Animate a style track. Move a shape or text colour, or a gradient stop, when the clip already carries that style.
- Cross-fade two clips. Overlap two shapes with different fills when the change also needs a distinct texture or layout.
Interpolating hue by rotation is not the same as interpolating between two
colours: hue takes the wheel's path, so blue to amber travels through green
unless you go the other way with a negative value.
Grade the picture
set_effects replaces the whole chain. Spatial, stylize, generator, and LUT
steps follow list order. The renderer aggregates legacy colour and blur fields
into later passes, so reordering two color effects does not create two
separate grades. Use a separate adjustment clip for a shared look that must
follow individual shot corrections.
Choose adjustments in this order, then judge the final render:
levels— set the black and white points first.colorwithbrightness,temperatureandtint— exposure and cast.curvesorcolorcontrast— the tonal range.colorsaturationandhue— midtone colour.liftGammaGain— separate shadow and highlight colour. Keep it subtle.lut— paste the contents of a 3D.cubetable intocubeand setintensityfor the mix. 1D tables are refused. A LUT's domain and table values may extend outside 0..1; the renderer clamps the final colour after interpolation.vignette, and anyglow, to finish the look.
Neutral values are the identity: brightness 0, contrast 1, saturation 1,
hue 0, gamma 1, lift [0,0,0], gain [1,1,1]. A lut effect requires
the complete .cube text.
Grade every shot in a sequence the same way, or they will not cut together. When one shot needs its own correction, correct it toward the others first and apply the shared look after.
Check the grade on frames and in motion
preview_timeline_frame uses Canvas 2D pixel passes for the supported effect
chain, including colour grading, LUTs, stylize modes, generators, and dither.
Compare a frame at the same timecode with the GPU render when judging a subtle
effect or a translucent edge. A missing scratch surface appears in the frame's
degraded report. The frame lists unsupported effect types in
effects_not_applied; validate_timeline reports them as unknown_effect.
Compositing blends stored encoded-sRGB channel values. It does not linearize them before combining layers. Judge a 50% blend by that output, rather than expecting a linear-light midpoint.
Contrast and legibility
validate_timeline reports text_illegible for type under 2.5% of frame height
or under a 3:1 contrast ratio against its own background plate or a full-frame
shape behind it. The check refuses to guess: a colour it cannot parse, a
translucent plate, gradient-filled type or a backdrop it cannot prove is behind
the text produces no finding at all. A silent pass is not a pass — look at
the frame.
Three fixes, cheapest first: a background scrim on the text style, a stroke
under the glyphs, or a shape clip at 40–60% black between the picture and the
text. The scrim goes on a track index between them, because the lowest index
draws on top.
Banding and muddy midpoints
A wide gradient across a dark frame can band. Add
{"type":"stylize","mode":"dither","amount":1,"seed":4}
after the gradient, with a fixed seed for repeatable frames. Dither reduces
visible 8-bit steps; it cannot recover precision already lost in source media
or guarantee that a video encoder keeps the noise. You can also shorten the
gradient span or bring its stops closer in lightness. A generator effect with
mode: "noise" or "fractal" makes a visible texture when that is the look,
not just a banding fix.
A gradient that reads grey in the middle has stops that pass through low chroma. Add a third stop at the midpoint, chosen with its chroma held up, rather than trusting the two-stop path.
Colour as a motion channel
Colour carries the same three-layer ranking motion does. The hero can hold the
only saturated accent in the frame while the bed sits desaturated under a color
effect at saturation 0.6 — a hierarchy the eye reads before any movement
starts. A flash emphasis (a brightness spike) is the loudest colour event
available; one per piece.
Before you finish
- One primary, one accent, two or three neutrals, neutrals tinted toward the primary.
- Static colours written in supported CSS syntax, preferably hex.
- The effect chain in grading order, and the same look on every shot in the cut.
- The look checked on preview frames and a rendered segment.
- Text legible over its actual backdrop, checked by looking.
Version History
-
97aad94
Current 2026-10-19 19:18
将操作契约引用从 motion-graphics 更正为 timeline-edit-ops,以对齐当前代码实现。
-
85ff020
2026-09-28 10:24
更新配色深度获取建议,明确使用OKLCH保持视觉权重一致;强调文档中统一使用Hex格式以确保解析兼容性。
- 9b979bc 2026-09-22 23:28


