greenlight-motion 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -10
- package/dist/bundle-inputs.json +23 -1
- package/dist/greenlight-motion-script.mjs +549 -80
- package/dist/greenlight-motion.js +1078 -747
- package/dist/greenlight-motion.mjs +44077 -41999
- package/dist/script-inputs.json +3 -1
- package/package.json +1 -1
- package/schema/capabilities.json +1 -1
- package/skill/SKILL.md +10 -10
- package/skill/references/scene-script.md +2 -2
- package/skill/references/video-3d.md +107 -100
- package/skill/references/video-backgrounds.md +89 -41
- package/skill/references/video-camera.md +158 -83
- package/skill/references/video-design.md +39 -7
- package/skill/references/video-effects.md +936 -620
- package/skill/references/video-grid.md +24 -24
- package/skill/references/video-keyframes.md +145 -52
- package/skill/references/video-particles.md +48 -29
- package/skill/references/video-paths.md +69 -65
- package/skill/references/video-projects.md +159 -89
- package/skill/references/video-shapes.md +94 -31
- package/skill/references/video-text-kinetic.md +78 -27
- package/skill/references/video-text-presets.md +738 -17
- package/skill/references/video-text.md +171 -79
- package/skill/references/video-transitions.md +72 -17
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
## Contents
|
|
12
12
|
|
|
13
|
-
- [Grid layers (`mediaType: "grid"`) — Grid Builder](
|
|
13
|
+
- [Grid layers (`mediaType: "grid"`) — Grid Builder](#grid-layers-mediatype-grid--grid-builder)
|
|
14
14
|
|
|
15
15
|
### Grid layers (`mediaType: "grid"`) — Grid Builder
|
|
16
16
|
|
|
@@ -24,7 +24,7 @@ The grid owns **only PRS** (position / rotation / scale). A member keeps its own
|
|
|
24
24
|
ungrouping restores every layer exactly as it was. Everything else about a member (text, colours,
|
|
25
25
|
filters, effects, opacity, timing) is still its own.
|
|
26
26
|
|
|
27
|
-
```
|
|
27
|
+
```jsonc
|
|
28
28
|
// clip — one channel per cell, each holding exactly one item at freeStart 0
|
|
29
29
|
{
|
|
30
30
|
"id": "grid1", "source": "REMOTE", "mediaType": "grid", "url": "", "proxyUrl": null,
|
|
@@ -45,12 +45,13 @@ filters, effects, opacity, timing) is still its own.
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
The layout rule lives on the **parent timeline item** as `gridConfig` (every field is optional —
|
|
48
|
-
missing ones take the default shown, and out-of-range values are clamped on load)
|
|
48
|
+
missing ones take the default shown, and out-of-range values are clamped on load). Strip the `//`
|
|
49
|
+
comments before sending:
|
|
49
50
|
|
|
50
|
-
```
|
|
51
|
+
```jsonc
|
|
51
52
|
"gridConfig": {
|
|
52
53
|
"version": 1,
|
|
53
|
-
"mode": "
|
|
54
|
+
"mode": "rect", // rect (default) | current | radial | spiral | wave | scatter | sphere | path
|
|
54
55
|
|
|
55
56
|
// shared
|
|
56
57
|
"order": "stack", // stack | reverse | name | random — which member takes which cell
|
|
@@ -100,11 +101,11 @@ missing ones take the default shown, and out-of-range values are clamped on load
|
|
|
100
101
|
"scatterDepth": 0, // px of random z spread
|
|
101
102
|
|
|
102
103
|
// sphere
|
|
103
|
-
"sphereRadius":
|
|
104
|
+
"sphereRadius": 420, "sphereSpinY": 0, "sphereTiltX": 0,
|
|
104
105
|
|
|
105
106
|
// path — same point shape as item.motionPath (world pixels, centre origin).
|
|
106
107
|
// Omit it and the grid rides the ITEM's own motionPath instead, which is what
|
|
107
|
-
// the
|
|
108
|
+
// the editor does.
|
|
108
109
|
"path": { "points": [{ "x": -400, "y": 0, "z": 0 }, { "x": 400, "y": 0, "z": 0 }], "closed": false },
|
|
109
110
|
"pathSpacing": "arc", // arc (equal distance along the curve) | segment
|
|
110
111
|
"pathTrimStart": 0, // % — the usable stretch of the curve
|
|
@@ -142,7 +143,7 @@ missing ones take the default shown, and out-of-range values are clamped on load
|
|
|
142
143
|
// amount a FULLY effected cell gets, added on top of the layer's own value.
|
|
143
144
|
// transform: scale % | opacity % | rotate ° | z px | lookAt % (proximity only)
|
|
144
145
|
// filter: blur px | brightness % | contrast % | saturate % | hue ° | grayscale % | sepia %
|
|
145
|
-
"fx": { "scale": 60, "blur": 8 }
|
|
146
|
+
"fx": { "scale": 60 }, // the default; e.g. { "scale": 60, "blur": 8 } also blurs the effected cells
|
|
146
147
|
|
|
147
148
|
// per-cell reveal — NOT keyframes: a pure function of this layer's own progress
|
|
148
149
|
"revealPreset": "none", // none | fade | pop | rise | spin | flyCenter | flyEdge | flyRandom
|
|
@@ -168,7 +169,7 @@ missing ones take the default shown, and out-of-range values are clamped on load
|
|
|
168
169
|
"layoutMix": 0, // 0…layouts.length-1 — keyframe `gr:layoutMix` to morph; integer = exactly that slot (single layout evaluated); a Hold key hard-cuts
|
|
169
170
|
"morphStagger": 0.35, // 0–0.9 — spreads the move across cell ranks (0 = all cells slide together)
|
|
170
171
|
"morphStaggerBy": "index", // index | ring | distance | random — ranked on the layout being LEFT
|
|
171
|
-
"morphEase": "Smooth"
|
|
172
|
+
"morphEase": "Smooth", // per-cell ramp: a name from easings.keyframe.transitionValues
|
|
172
173
|
"morphArc": 60, // px — bulges each cell's travel outward mid-move (negative implodes); exactly zero at both endpoints
|
|
173
174
|
"morphSpin": 0 // whole turns added over the blend; integers land back on themselves
|
|
174
175
|
}
|
|
@@ -182,7 +183,7 @@ Rules:
|
|
|
182
183
|
remaining cells renumber. The member stays inside the grid.
|
|
183
184
|
- **Cap: 64 members.** Every cell costs a channel state with two meshes and two `<video>` elements.
|
|
184
185
|
- **Grid parameters are keyframable** as `gr:<key>` tracks in the parent item's `propertyKeyframes`
|
|
185
|
-
(numeric only) — e.g. `"gr:radius": [{ "position": 0, "value": "5" }, { "position": 100, "value": "40" }]`
|
|
186
|
+
(numeric only; `cols`, `rings`, `seed` and `morphSpin` are rounded to integers) — e.g. `"gr:radius": [{ "position": 0, "value": "5" }, { "position": 100, "value": "40" }]`
|
|
186
187
|
opens a ring. Only the keys the CURRENT `mode` reads have any effect; see the field list above.
|
|
187
188
|
With `layouts` present, a keyframed geometry key overrides BOTH sides of the blend — use slot
|
|
188
189
|
values for static differences between layouts and `gr:` tracks for animation over time.
|
|
@@ -210,23 +211,22 @@ Rules:
|
|
|
210
211
|
cell box letterboxes/pillarboxes with fully TRANSPARENT bars under `contain` (and under
|
|
211
212
|
`none`/`stretch` where spare quad shows); `cover` fills the cell and crops. A member's own
|
|
212
213
|
`backgroundColor` still paints its plate when set.
|
|
213
|
-
- **A grid is REAL 3D.** Its cells are filmed by the project camera itself
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
blur on the grid item no longer sees the grid's own transform. `mirrorH/V` flip the picture,
|
|
225
|
-
not the arrangement.
|
|
214
|
+
- **A grid is REAL 3D.** Its cells are filmed by the project camera itself, so the whole item
|
|
215
|
+
transform (`panX/Y/Z`, `rotateX/Y/Z`, `zoom`, and their keyframes) moves the ARRANGEMENT in
|
|
216
|
+
three dimensions, camera pans produce real parallax between cells at different z, a dolly
|
|
217
|
+
travels *through* the arrangement (into a sphere's interior), and the 3D world view shows the
|
|
218
|
+
member meshes in space. `orient: "screen"` aims each card at the lens's true position at any
|
|
219
|
+
angle and follows camera roll. Cells clip against the real frustum like ordinary layers; there
|
|
220
|
+
is no `overscan` field (one is dropped on save).
|
|
221
|
+
- **The grid item's look applies to the flattened result:** its filters, opacity, blend mode,
|
|
222
|
+
masks and transitions work as on any layer, but motion blur on the grid item does not see the
|
|
223
|
+
grid's own transform — animate the camera or the layout for blurred motion. `mirrorH/V` flip the
|
|
224
|
+
picture, not the arrangement.
|
|
226
225
|
- **PRO at render.** Free to build and preview; a project containing a non-empty grid needs a PRO
|
|
227
226
|
licence to render, the same policy as PRO motion templates and Running Glow.
|
|
228
227
|
- **Grouping existing layers is non-destructive** when the grid is created with `"mode": "current"`
|
|
229
|
-
+ `"fit": "none"` — nothing moves or resizes until a layout type is chosen.
|
|
228
|
+
+ `"fit": "none"` — nothing moves or resizes until a layout type is chosen. Write both
|
|
229
|
+
explicitly: a missing `mode` is `rect`, which rearranges the members at once. That pairing is also
|
|
230
230
|
the natural slot 0 of a morph: `layouts: [{ "mode": "current" }, { "mode": "radial", … }]` plus a
|
|
231
231
|
`gr:layoutMix` track travels the layers from where they are into a ring. A grid built empty has
|
|
232
232
|
no arrangement to preserve, so `rect` is the sensible start there.
|
|
@@ -10,18 +10,30 @@
|
|
|
10
10
|
|
|
11
11
|
## Contents
|
|
12
12
|
|
|
13
|
-
- [Keyframe animations (per-property tracks)](
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
13
|
+
- [Keyframe animations (per-property tracks)](#keyframe-animations-per-property-tracks)
|
|
14
|
+
- [Easings](#easings)
|
|
15
|
+
- [The easing catalogue](#the-easing-catalogue)
|
|
16
|
+
- [Animatable tracks](#animatable-tracks)
|
|
17
|
+
- [Prefixed track namespaces](#prefixed-track-namespaces)
|
|
18
|
+
- [Preview timing: keys between frames](#preview-timing-keys-between-frames)
|
|
19
|
+
- [Keys cut by a split (`easeSlice`)](#keys-cut-by-a-split-easeslice)
|
|
20
|
+
- [Hidden tracks (`hiddenPropertyKeyframes`)](#hidden-tracks-hiddenpropertykeyframes)
|
|
21
|
+
- [Track expressions (`propertyKeyframeFx`)](#track-expressions-propertykeyframefx)
|
|
22
|
+
- [The custom expression language](#the-custom-expression-language)
|
|
23
|
+
- [Looping the keyframes](#looping-the-keyframes)
|
|
24
|
+
- [Glide (inertia without spring-back)](#glide-inertia-without-spring-back)
|
|
25
|
+
- [Reading inside a sequence](#reading-inside-a-sequence)
|
|
26
|
+
- [Layer parenting and null layers (`keyframeParenting`)](#layer-parenting-and-null-layers-keyframeparenting)
|
|
17
27
|
|
|
18
28
|
### Keyframe animations (per-property tracks)
|
|
19
29
|
|
|
20
|
-
Every timeline item can animate its visual properties over the clip's duration. Each property gets its **own independent track** of keys under `propertyKeyframes` — an opacity fade on a different rhythm than a slow zoom, a rotation eased with a bounce while the scale stays smooth
|
|
30
|
+
Every timeline item can animate its visual properties over the clip's duration. Each property gets its **own independent track** of keys under `propertyKeyframes` — an opacity fade on a different rhythm than a slow zoom, a rotation eased with a bounce while the scale stays smooth. Tracks are always separate; there is no merged, all-properties-per-key form.
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
- **`position` is 0–100, percent of the clip's played duration** (not seconds).
|
|
33
|
+
- **`value` is a string**: `"1.25"`, `"#ff0000"`, `"rgba(0,0,0,0.5)"`. Write strings everywhere, including progress tracks.
|
|
34
|
+
- **`easing`** shapes the segment **after** its key (see [Easings](#easings)).
|
|
35
|
+
- A **single-key track holds its value** for the whole clip.
|
|
36
|
+
- No two keys of one track may share a `position`; omit a track with zero keys.
|
|
25
37
|
|
|
26
38
|
```json
|
|
27
39
|
{
|
|
@@ -34,26 +46,87 @@ Positions are always **0–100, percent of the clip's played duration** (not sec
|
|
|
34
46
|
}
|
|
35
47
|
```
|
|
36
48
|
|
|
37
|
-
|
|
49
|
+
**Transform origin** is one item-level field, `keyframeTransformOrigin` (`"center"`, `"top"`, `"bottom"`, `"left"`, `"right"`, a corner such as `"bottom left"`, or `"x% y%"`): the point every rotation (static and keyed, 2D and 3D) and the keyed scale turn about, while `panX`/`panY`/`panZ` stay the layer's resting centre — see [Turning a flat layer in 3D](video-3d.md#turning-flat-layers-in-3d--pivot-and-stacking).
|
|
50
|
+
|
|
51
|
+
`propertyKeyframes` is baked by **`/render-onserver`** and the desktop render; the fast `/render` ignores keyframes entirely.
|
|
52
|
+
|
|
53
|
+
#### Easings
|
|
54
|
+
|
|
55
|
+
Easing names come from `GET /api/video-editor/capabilities` → `easings.keyframe.values`. **No easing carries past its key**: whatever its shape in between (including the overshooting `Back`, `Elastic` and `Overshoot` curves), the value lands exactly on the key at the key's time. For motion that continues past a key, add an expression instead — `spring` for a real spring that overshoots and settles in seconds, `glide` for a move that lands soft and keeps drifting (see [Track expressions](#track-expressions-propertykeyframefx)).
|
|
56
|
+
|
|
57
|
+
Pick the curve by intent:
|
|
58
|
+
|
|
59
|
+
| Intent | Easing |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| Cinematic move: slow start, soft stop (a good default) | `AE Smooth` |
|
|
62
|
+
| Arrive: up to speed early, then a long brake into the key | `AE In (arrive)` |
|
|
63
|
+
| Leave: a long slow start, then speeds away | `AE Out (leave)` |
|
|
64
|
+
| Hard decelerate: entrances, landings | `Power` — `cubic-bezier(0.23, 1, 0.32, 1)` |
|
|
65
|
+
| Slow wind-up, hard hit, long tail: a scene change on the beat, a logo hit, a screen swipe | `Punch` — `cubic-bezier(0.7, 0, 0.1, 1)` |
|
|
66
|
+
| Full speed at once, then a long soft stop: UI panels, drawers, a camera landing | `Apple Out` — `cubic-bezier(0.32, 0.72, 0, 1)` |
|
|
67
|
+
| Arrives fast, glides slowly through the middle, leaves fast | `Smooth Glide` — `cubic-bezier(0.2111, 0.7721, 0.8541, 0.112)` |
|
|
68
|
+
| Jumps in, all but stops, jumps out | `Glide Easing Stop` — `cubic-bezier(0, 0.9125, 1, -0.0338)` |
|
|
69
|
+
| Classic MotionFast template motion | `Natural` (punchy ease-in-out), `Slow Down` (pure decelerate), `Overshoot` (shoots past, settles slowly), `Impulse` (dips backwards before launching), `Swing` (wind-up, then a springy arrival) |
|
|
70
|
+
| Constant speed: progress tracks (`motionPathT`, `camPathT`), dense per-frame data | `Linear` |
|
|
71
|
+
| Step to the next key (a hard cut) | `Hold` |
|
|
72
|
+
| Your own curve | `Custom` + the key's `bezier: [x1, y1, x2, y2]` (write both together, or it falls back to `Smooth`) |
|
|
73
|
+
|
|
74
|
+
The `AE …` values are the After Effects-style bezier curves shared with MotionFast. AE names a curve by the side of the key it sits on, which is why `AE In (arrive)` brakes into its key and `AE Out (leave)` speeds away from the one before.
|
|
75
|
+
|
|
76
|
+
Aliases load and map to another name's curve (`easings.keyframe.retired`: `Power2 Out` → `Ease Out`, `Power3 Out` → `Cubic Out`, `Power4 Out` → `Power`, `Circ Out` → `Slow Down`, `Cubic Smooth` → `Natural` …). Write the names they map to.
|
|
77
|
+
|
|
78
|
+
#### The easing catalogue
|
|
79
|
+
|
|
80
|
+
`GET /api/video-editor/capabilities` → `easings` carries both vocabularies with the actual curve behind every name, so a look can be matched or carried into another tool without guessing. `easings.keyframe` covers keyframe and transition easings: `values` (all of them), `transitionValues` (the subset a transition accepts — `Custom` and `Hold` need per-key data and are rejected there), `retired` (alias → the value it maps to; accepted on load and by the API) and `curves[]`. `easings.motionTemplate` covers the camelCase names a template's ease field takes (`aeSmooth`, `power2`, …). In `curves[]`, `kind` says which field IS the curve — `bezier` → `cubicBezier` `[x1,y1,x2,y2]` plus a ready-to-paste `css`, `sampled` → `samples`, the curve at t = 0, 0.01 … 1 (values past 0..1 are real overshoot; as CSS that is `linear(<samples>)`). `gsap` names the ease that plays, and `keyframeEquivalent` / `motionTemplateEquivalent` link the same curve across the two vocabularies — only where the curves genuinely match, so treat a missing link as "these are different curves that happen to share a name" (template `smooth` is not keyframe `Smooth`; template `power2` is keyframe `Ease Out`). A `sampled` entry's `cubicBezier` (when marked `bezierIsApproximate`) is only the closest cubic — `bezierMaxError` says how far off, up to ~0.13 for `Expo Smooth`; match against the samples, not the handles. Two easings carry no curve: `Custom` reads the key's own `bezier`, and `Hold` steps to the next key.
|
|
38
81
|
|
|
39
|
-
|
|
82
|
+
#### Animatable tracks
|
|
40
83
|
|
|
41
|
-
|
|
84
|
+
A track on an item without the matching feature is a harmless no-op, and a track whose target no longer exists is dropped on load.
|
|
85
|
+
|
|
86
|
+
| Group | Tracks and units |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| Transform | `opacity` (**0–1** here; the static item field `opacity` is 0–100), `scale`, `scaleX`, `scaleY` (multipliers, 1 = unchanged, that MULTIPLY the item's base `zoom`/`scaleX`/`scaleY` — a squash-and-stretch is `scaleX` up while `scaleY` goes down), `rotate` (Z), `rotateX`, `rotateY` (degrees), `translateX`, `translateY`, `translateZ` (px), `xPercent`, `yPercent` (% of the canvas), `letterSpacing` (px), `clipPath` (%), `borderWidth` (px — the card border, or the outline on shape layers), `borderRadius` — or `borderRadiusTL`/`TR`/`BR`/`BL` while `borderRadiusLinked: false` — (the corner radius on every layer: px on media, the item's `borderRadiusUnit` on `rectangle` shapes; see [card framing](video-shapes.md#card-framing-on-every-visual-layer)) |
|
|
89
|
+
| Motion path | `motionPathT` (0–100 % progress), `motionPathOffsetX/Y/Z` (px from a live path), `motionPathPoints` (non-scalar: the path itself) — see [video-paths.md](video-paths.md#object-motion-paths-motionpath--motionpatht) |
|
|
90
|
+
| Text | `textPathSpread`, `textPathSpacing`, `textPathTrimStart`, `textPathTrimEnd`, `textPathOffsetY` ([Text on Path](video-paths.md#text-on-path-textpathenabled); no-op elsewhere), `textCounterProgress` (0–100 %, [video-text.md](video-text.md)) |
|
|
91
|
+
| Colour (CSS colour strings) | `color`, `backgroundColor`, `textColor` (text fill), `textBgColor` (caption bar), `textOutlineColor` (unset falls back to the text colour), `shapeFill` (a shape's background paint while its type is solid or gradient), `borderColor`. `textColor` and `shapeFill` keys may hold a CSS gradient (`linear-gradient(90deg, #fff 0%, #8b5cf6 100%)`); gradients blend stop by stop, solid↔gradient included |
|
|
92
|
+
| Filters | `blur` (px), `brightness`, `contrast`, `saturate` (%, 100 = neutral), `hue-rotate` (degrees), `grayscale`, `sepia`, `invert` (%, 0 = neutral), `effectAmount` (0–1), `lutAmount` (%) — each overrides the item's static `filter*` field while its track exists |
|
|
93
|
+
| Shape layers | `shapeWidthPx`/`shapeHeightPx` or `shapeWidthPct`/`shapeHeightPct` (the unit the shape uses; `shapeRadiusPx`/`shapeRadiusPct` on a circle), the radius tracks (under Transform) in the item's `borderRadiusUnit` on `rectangle` shapes, `backdropBlur` (px), `backdropSaturation` (%), the stroke tracks (`lineWidth`, `lineTrimStart/End/Offset`, …) and the non-scalar `shapePath` — see [video-shapes.md](video-shapes.md) |
|
|
94
|
+
| Layer Effects | `glowAmount` … `glowSourceLevel` (Running Glow), `depthStrength`, `depthFocus`, `glass*`, `mskAmount`, and each effect's own tracks (`mblur*`, `trail*`, `af*`, `rep*`, `ds*`, …) — listed per effect in [video-effects.md](video-effects.md#greenlight-dash--video-editor-layer-effects-and-overlay-effects) |
|
|
95
|
+
| Camera clips | their own registry: `camZoom`, `camX`, `camY`, `camRoll`, `camDolly`, `camTiltX`, `camTiltY`, `camPathT`, `camPathOffsetX/Y/Z`, `camFollowTarget` and the depth-of-field fields — see [video-camera.md](video-camera.md#camera-properties) |
|
|
96
|
+
| Playback rate | `speed` — retimes the clip rather than restyling it; see Speed curves (GreenLight Dash only) |
|
|
97
|
+
|
|
98
|
+
#### Prefixed track namespaces
|
|
99
|
+
|
|
100
|
+
These keys animate things outside the static registry, in the same `propertyKeyframes` object and with the same key shape:
|
|
42
101
|
|
|
43
102
|
| Track key | Animates | Notes |
|
|
44
103
|
|---|---|---|
|
|
45
|
-
| `fx:<eid>:<paramKey>` | An Overlay Effect numeric param, plus synthetic `mix` | `<eid>` is the effect's stable id in `item.effects`. Number params only. Running Glow / 3D Depth
|
|
46
|
-
| `mp:<paramKey>` | A **motion layer's numeric template option** (`number`/`range` field) | `<paramKey>` is the template's `fields[].key`. |
|
|
47
|
-
| `mpc:<paramKey>` | A **motion layer's colour template option** (`color` field) | Separate prefix on purpose —
|
|
48
|
-
| `gr:<key>` | A **grid layer's layout parameter** | Only the keys the current layout mode reads; see
|
|
49
|
-
| `rig:<pid>:<chan>` | One **Rig (Puppet) pin's pose channel** | Offsets from the pin's rest. See
|
|
50
|
-
| `msk:<mid>:<chan>` | One **layer mask's** geometry channel | `x`/`y`/`w`/`h`/`rot`/`feather`/`expand`/`opacity`/`grow` are numeric; **`msk:<mid>:path` is NON-SCALAR** — its value is a JSON point list, like `shapePath`. See
|
|
51
|
-
| `txa:<aid>:<chan>` | One **text animator's** range, wiggle or effect value | `<chan>` is `start`/`end`/`offset`/`amount`/`smooth`/`easeHigh`/`easeLow
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
|
|
56
|
-
|
|
104
|
+
| `fx:<eid>:<paramKey>` | An Overlay Effect numeric param, plus synthetic `mix` | `<eid>` is the effect's stable id in `item.effects`. Number params only. Running Glow / 3D Depth keep their named tracks above. See [video-effects.md](video-effects.md#greenlight-dash--video-editor-layer-effects-and-overlay-effects). |
|
|
105
|
+
| `mp:<paramKey>` | A **motion layer's numeric template option** (`number`/`range` field) | `<paramKey>` is the template's `fields[].key`. Which options accept a track: motion.md (GreenLight Dash only). |
|
|
106
|
+
| `mpc:<paramKey>` | A **motion layer's colour template option** (`color` field) | Separate prefix on purpose — a colour under `mp:` is silently dropped. |
|
|
107
|
+
| `gr:<key>` | A **grid layer's layout parameter** | Only the keys the current layout mode reads; see [video-grid.md](video-grid.md). |
|
|
108
|
+
| `rig:<pid>:<chan>` | One **Rig (Puppet) pin's pose channel** | Offsets from the pin's rest. See [video-effects.md](video-effects.md#greenlight-dash--video-editor-layer-effects-and-overlay-effects). |
|
|
109
|
+
| `msk:<mid>:<chan>` | One **layer mask's** geometry channel | `x`/`y`/`w`/`h`/`rot`/`feather`/`expand`/`opacity`/`grow` are numeric; **`msk:<mid>:path` is NON-SCALAR** — its value is a JSON point list, like `shapePath`. See [Layer Mask entry](video-effects.md#layer-mask-entry). |
|
|
110
|
+
| `txa:<aid>:<chan>` | One **text animator's** range, wiggle or effect value | `<chan>` is `start`/`end`/`offset`/`amount`/`smooth`/`easeHigh`/`easeLow` (under `drive: "time"`/`"voice"` only `amount`), `wig.max`/`wig.min`/`wig.corr`, or `<pid>.<field>` for a numeric effect field except `scramble` `rate`/`seed` and `caret` `width`/`blink`/`hideAfter`. Numeric, in the field's units. See [Advanced Animations](video-text.md#advanced-animations--effects-over-a-moving-range-of-characters-words-or-lines). |
|
|
111
|
+
| `ph:<n>` | One **text placeholder counter's** progress | The placeholder names its track with `kf=<n>`. See [video-text.md](video-text.md). |
|
|
112
|
+
| `sc:<id>:<chan>` / `scc:<id>:col` | A **3D scene rig** channel (numeric / colour) | Rig clips carry only these. See [video-3d.md](video-3d.md). |
|
|
113
|
+
| `px:<param>` | A **particle layer's proximity-effector parameter** | `range`, `falloff`, `depth` plus any key present in `proximity.fx`. See [video-particles.md](video-particles.md). |
|
|
114
|
+
| `cc:<path>` | One number of a **video/image item's `colorCorrection`** | `<path>` addresses the value inside the object: `mix`, `wb.temp`, `tone.exposure`, `levels.<rgb\|r\|g\|b>.<inB\|gamma\|inW\|outB\|outW>`, `color.saturation`, `wheels.<shadows\|midtones\|highlights>.<x\|y\|l>`, `hsl.<hue\|sat\|lum>.<0-7>`, `detail.<details\|sharpen>`. Values are clamped to the field's range. See [video-effects.md](video-effects.md#greenlight-dash--video-editor-layer-effects-and-overlay-effects). |
|
|
115
|
+
|
|
116
|
+
Tracks whose target no longer exists are dropped on load (an effect removed from the chain, a motion layer swapped to another template), with one exception: a `px:` track whose driven property has been removed from `proximity.fx` is kept, so a shaped curve survives the property being toggled off and on. Nothing reads it while the property is gone.
|
|
117
|
+
|
|
118
|
+
#### Preview timing: keys between frames
|
|
119
|
+
|
|
120
|
+
The editor evaluates every track at the exact playhead time. While playing, the preview redraws at the project's `data.previewFps` — `60` by default, or `30`, `120` or `"project"` (the project's own frame rate) — while renders always sample the export frame rate. So on a 30 fps project the preview also shows the instants **between** export frames, which no render contains:
|
|
121
|
+
|
|
122
|
+
- A key, a `Hold` step or an item end placed between export frames can look different in preview than in the render (a one-frame pop in one and not the other).
|
|
123
|
+
- To see exactly the frames the export will have, set `"previewFps": "project"` at the `data` root (the editor stores the field only when it is not 60), or check stills.
|
|
124
|
+
|
|
125
|
+
**Smooth cursors and hands.** Per-frame keys (a baked cursor or hand path, tracked data written as one `Linear` key per frame) are exact only at the frames they were baked for; between them the preview draws a straight line from key to key. When such a layer follows something that moves on an eased curve — a parent null, the card it points at — the eased motion is evaluated on its curve at the in-between instant while the per-frame layer cuts a straight chord, so it slips off its target between frames in preview. Hang the layer off what it follows (`keyframeParenting`) and bake its keys in that parent's space, so the eased motion comes from the parent at every instant. Where one item hands a cursor or hand over to another (one layer ends, the next takes over), give both a key at the hand-over time with the same value and place the cut on a frame boundary, so neither the preview nor the render shows a jump.
|
|
126
|
+
|
|
127
|
+
#### Keys cut by a split (`easeSlice`)
|
|
128
|
+
|
|
129
|
+
Splitting an item cuts its tracks at the split point: each half keeps its own keys, both get a key there holding the value shown at that instant, and positions are re-expressed as % of each half. Part of an easing is not itself an easing, so on each side the key that starts the cut segment keeps its `easing` and gains `easeSlice: { of, from, to, bezier? }`: the segment plays only the `from`–`to` part (fractions of the original segment) of that easing, which is what lets the two halves replay the uncut animation exactly. Copy `easeSlice` unchanged when you rewrite a track, or the halves stop matching. It applies only while `of` equals the key's `easing` (and, for `Custom`, its `bezier` equals the key's), so changing the easing switches it off; delete it to give the key the whole easing again. Do not write one yourself: it only reproduces the original curve together with the exact values the split wrote on the keys either side. A split can also add keys where an easing turns back or crosses its end value (Bounce, Elastic or Back on a shape or mask path, which clamp instead of overshooting); they belong to the same animation.
|
|
57
130
|
|
|
58
131
|
### Hidden tracks (`hiddenPropertyKeyframes`)
|
|
59
132
|
|
|
@@ -69,7 +142,7 @@ A track can be parked instead of deleted. `hiddenPropertyKeyframes` has the same
|
|
|
69
142
|
|
|
70
143
|
### Track expressions (`propertyKeyframeFx`)
|
|
71
144
|
|
|
72
|
-
An AE-style expression layered on top of a track's keyframed value. Stored per track in the item-level map `propertyKeyframeFx: { "<trackKey>": { "type": "...", "params": { ... } } }`; the track key must exist in `propertyKeyframes` (orphans are dropped on load) and must be **numeric** (
|
|
145
|
+
An AE-style expression layered on top of a track's keyframed value. Stored per track in the item-level map `propertyKeyframeFx: { "<trackKey>": { "type": "...", "params": { ... } } }`; the track key must exist in `propertyKeyframes` (orphans are dropped on load) and must be **numeric** (colour and path tracks are ignored). The catalogue with parameter ranges and defaults is `GET /api/video-editor/capabilities` → `keyframeExpressions`.
|
|
73
146
|
|
|
74
147
|
```json
|
|
75
148
|
{
|
|
@@ -78,36 +151,52 @@ An AE-style expression layered on top of a track's keyframed value. Stored per t
|
|
|
78
151
|
}
|
|
79
152
|
```
|
|
80
153
|
|
|
81
|
-
`elastic`, `bounce` and `spring` **reshape the move itself** — they are not rings appended after a key. Each keyframe transition is treated as a step of size `value[i] − value[i−1]` that starts at the previous key, so the layer launches there, overshoots the target and settles into it as **one continuous motion**.
|
|
154
|
+
`elastic`, `bounce` and `spring` **reshape the move itself** — they are not rings appended after a key. Each keyframe transition is treated as a step of size `value[i] − value[i−1]` that starts at the previous key, so the layer launches there, overshoots the target and settles into it as **one continuous motion**. So the key's own `easing` is superseded on a track carrying one of these (use `amp` to mix back toward it), and they work on the commonest track of all — two keys at 0 and 100 — where a post-key ring would have nowhere to play. `elastic` and `bounce` are normalized to each segment's own duration (the shape is identical for a 0.3 s and a 3 s move); `spring` runs in seconds instead.
|
|
82
155
|
|
|
83
|
-
- **`elastic`** (`amp` % strength 0–300, `freq` wobbles **across the move** 0.3–5, `decay` 0.5–15) — launches, crosses the target at `1/(4·freq)` of the move, overshoots by
|
|
84
|
-
- **`spring`** (`amp` % strength 0–300, `freq` 0.1–10 Hz, `damping` ratio 0.05–1) — a real damped spring in **seconds**, released at rest from the key before: `x = a + (b−a)·(1 − e^(−ζωt)·(cos ω_d t + ζω/ω_d·sin ω_d t))`, `ω = 2π·freq`, `ω_d = ω·√(1−ζ²)`, `t` = seconds since the move's first key. It peaks at `1 + e^(−ζπ/√(1−ζ²))` of the step and settles in ≈ `4/(ω·ζ)` s whatever the gap to the next key, so a short move and a long one ring at the same rate (Elastic stretches instead); the move's END key only says when the next step starts. A ring not settled carries into the next step. Presets (`keyframeExpressions.types[].presets`, they set `freq` + `damping` only): `snappy` 1.6 Hz / 0.55 — ~
|
|
85
|
-
- **`bounce`** (`amp` % strength, `elasticity` bounciness 5–80%, `bounces` 1–10) — falls onto the target and rebounds back the way it came in decaying hops.
|
|
86
|
-
- **`wiggle`** (`amp` in the property's own units, `freq` Hz) — deterministic smooth jitter **added around** the keyed value across the whole clip (
|
|
87
|
-
- **`glide`** (`fast` response 0.01–3 s, `slow` tail length 0.05–8 s, `weight` tail share 0–100 %, `delay` tail delay 0–3 s) — **softens** the keyed motion (its easing included): a move lands soft and keeps creeping on in the direction it travelled, never past the key and never back.
|
|
88
|
-
- **`custom`** (`code`, a string ≤4000 chars) — your own expression; **its result becomes the property value**, so start from `value` to build on the keyframed motion. Full vocabulary in `GET /api/video-editor/capabilities` → `keyframeExpressions.customLanguage`.
|
|
156
|
+
- **`elastic`** (`amp` % strength 0–300, `freq` wobbles **across the move** 0.3–5, `decay` 0.5–15) — launches, crosses the target at `1/(4·freq)` of the move, overshoots by roughly `e^(−decay/(2·freq))` (a little more in practice: the defaults 100/1.1/5 overshoot ~13 % and have settled to <1 % by the key). A leftover wobble carries into the next segment as follow-through. Lower `freq` for a soft landing with a single late overshoot; raise it for a fast arrival with several wobbles.
|
|
157
|
+
- **`spring`** (`amp` % strength 0–300, `freq` 0.1–10 Hz, `damping` ratio 0.05–1) — a real damped spring in **seconds**, released at rest from the key before: `x = a + (b−a)·(1 − e^(−ζωt)·(cos ω_d t + ζω/ω_d·sin ω_d t))`, `ω = 2π·freq`, `ω_d = ω·√(1−ζ²)`, `t` = seconds since the move's first key. It peaks at `1 + e^(−ζπ/√(1−ζ²))` of the step and settles in ≈ `4/(ω·ζ)` s whatever the gap to the next key, so a short move and a long one ring at the same rate (Elastic stretches instead); the move's END key only says when the next step starts. A ring not settled carries into the next step. Presets (`keyframeExpressions.types[].presets`, they set `freq` + `damping` only): `snappy` 1.6 Hz / 0.55 — ~12 % overshoot, settled in 0.7 s: buttons, icons, pop-ins (the defaults); `gentle` 1 Hz / 0.8 — 1.5 % overshoot: mascots, big cards, the camera. `damping` 1 never overshoots. Leave each step ~1 s before the next key so the ring has settled. Example: `{"type":"spring","params":{"amp":100,"freq":1.6,"damping":0.55}}`.
|
|
158
|
+
- **`bounce`** (`amp` % strength 0–300, default 100; `elasticity` bounciness 5–80 %, default 45; `bounces` 1–10, default 5) — falls onto the target and rebounds back the way it came in decaying hops. The fall plus every hop are scaled to fit the move exactly, so it always settles **on** the key and needs no gravity or hop-duration knob. Higher bounciness spends more of the move hopping (so it lands earlier); it never passes the target.
|
|
159
|
+
- **`wiggle`** (`amp` 0–2000 in the property's own units — **no default: set it, or the wiggle is 0**; `freq` 0.1–20 Hz, default 3) — deterministic smooth jitter **added around** the keyed value across the whole clip (additive: the base motion stays intact), seeded per item and track so copies don't move in sync. A single-key (constant) track plus wiggle is the cheapest handheld or idle-motion recipe.
|
|
160
|
+
- **`glide`** (`fast` response 0.01–3 s, `slow` tail length 0.05–8 s, `weight` tail share 0–100 %, `delay` tail delay 0–3 s) — **softens** the keyed motion (its easing included): a move lands soft and keeps creeping on in the direction it travelled, never past the key and never back. See [Glide](#glide-inertia-without-spring-back).
|
|
161
|
+
- **`custom`** (`code`, a string ≤ 4000 chars) — your own expression; **its result becomes the property value**, so start from `value` to build on the keyframed motion. Full vocabulary in `GET /api/video-editor/capabilities` → `keyframeExpressions.customLanguage`.
|
|
89
162
|
|
|
90
|
-
|
|
163
|
+
Expressions ride the normal keyframe pipeline, so they render identically in preview and `/render-onserver`.
|
|
164
|
+
|
|
165
|
+
#### The custom expression language
|
|
166
|
+
|
|
167
|
+
An AE-flavoured language that the editor interprets — never `eval`s — so an expression in a shared project cannot execute code. One expression, no statements or assignment; a syntax error or non-numeric result silently leaves the keyframed value untouched.
|
|
91
168
|
|
|
92
169
|
| | |
|
|
93
170
|
|---|---|
|
|
94
|
-
| Variables | `value` (the keyframed value), `time` / `frame` / `progress` / `duration` (all **within the clip**, not the project), `fps`, `index
|
|
171
|
+
| Variables | `value` (the keyframed value), `time` / `frame` / `progress` / `duration` (all **within the clip**, not the project), `fps`, `index` (the layer's channel index), `PI`, `E` |
|
|
95
172
|
| Operators | `+ - * / %`, `^` / `**`, unary `-`, `! < > <= >= == !=`, `&& \|\|`, `a ? b : c`, `//` and `/* */` comments |
|
|
96
173
|
| Math | `abs ceil floor round sqrt exp log sin cos tan asin acos atan atan2 pow min max sign hypot` — bare or as `Math.*` |
|
|
97
174
|
| Helpers | `clamp(v,min,max)`, `lerp(a,b,t)`, `linear(t,tMin,tMax,a,b)` (and the 3-arg `linear(t,a,b)`), `ease`/`easeIn`/`easeOut`, `degrees`/`radians` |
|
|
98
175
|
| Motion | `wiggle(freq, amount, octaves?)` (jitters around `value`), `noise(t, octaves?)`, `random()` / `random(max)` / `random(min,max)` |
|
|
99
176
|
| Loops | `loopOut(type?, numKeyframes?)`, `loopIn(…)`, `loopOutDuration(type, seconds)`, `loopInDuration(…)` — type is `cycle` (default), `pingpong`, `offset` or `continue` |
|
|
100
177
|
| Track | `valueAtTime(t)` — this property's own value at another clip-local time; `numKeys` |
|
|
101
|
-
| Glide | `glide(fast?, slow?, weight?, delay?)` —
|
|
178
|
+
| Glide | `glide(fast?, slow?, weight?, delay?)` — see [Glide](#glide-inertia-without-spring-back). Defaults 0.10 s / 0.6 s / 0.25 / 0 |
|
|
102
179
|
| Layers | `layer("Name").<trackKey>`, `thisLayer.<trackKey>`, `thisComp.layer("Name")`, and `layer("Seq").layer("Inner").<trackKey>` to read inside a sequence/grid |
|
|
103
180
|
|
|
104
181
|
`random()` and `wiggle()` are **deterministic** — seeded from the layer id and property name, so preview and every re-render agree, and two copies of a layer don't jitter in sync. Layer references read the other layer's animated value at the same instant; only layers sharing a frame with this clip can be named, and the name is the layer's custom name (or a text layer's content, else the clip name). A *real* property the target doesn't animate falls back to that layer's static value, but a name that is not a property at all is an error (`"Logo" has no property "scalevalue"`) rather than a silent 0. Mutual references stop after two hops. Division by zero yields 0 rather than Infinity.
|
|
105
182
|
|
|
106
|
-
|
|
183
|
+
```json
|
|
184
|
+
{ "propertyKeyframeFx": {
|
|
185
|
+
"scale": { "type": "custom", "params": { "code": "value + wiggle(2, 0.05)" } },
|
|
186
|
+
"rotate": { "type": "custom", "params": { "code": "loopOut(\"offset\")" } },
|
|
187
|
+
"opacity": { "type": "custom", "params": { "code": "layer(\"My Sequence\").layer(\"Inner Star\").opacity" } }
|
|
188
|
+
} }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Looping the keyframes
|
|
107
192
|
|
|
108
|
-
|
|
193
|
+
`loopOut()` repeats this track's own animation past its last key — the AE function, same four types: `cycle` (start the segment again), `pingpong` (backwards, then forwards), `offset` (each pass stacked on the end of the last, so the value accumulates) and `continue` (no repeat — carry on at the end key's velocity). `loopIn(…)` mirrors it before the first key. The second argument counts keyframes of the looped segment back from the edge (`0`/omitted = the whole track, `1` = the last segment); `loopOutDuration(type, seconds)` takes seconds instead. Every type is continuous at the boundary key, so a loop never pops. Two keys plus `loopOut("pingpong")` is the cheapest infinite shuttle.
|
|
109
194
|
|
|
110
|
-
Glide
|
|
195
|
+
#### Glide (inertia without spring-back)
|
|
196
|
+
|
|
197
|
+
Glide replaces the "move, dead stop, move" look. It runs the track's own keyframed motion through a filter whose weights are all positive — a fast response (`fast` seconds) plus a slow tail (`slow` seconds) carrying `weight` of each move, optionally starting `delay` seconds later. A move therefore lands soft and keeps creeping on in the direction it travelled; it never passes the key and never comes back (a spring or Elastic would, and that reads as wrong-way secondary motion).
|
|
198
|
+
|
|
199
|
+
Set it as its own expression type on each track it should soften (here Floaty); put the same entry on X and Y for a 2D path:
|
|
111
200
|
|
|
112
201
|
```json
|
|
113
202
|
"propertyKeyframeFx": {
|
|
@@ -116,23 +205,27 @@ Glide is its own expression type, set on each track it should soften (here Float
|
|
|
116
205
|
}
|
|
117
206
|
```
|
|
118
207
|
|
|
119
|
-
|
|
208
|
+
The four tuned landings (`keyframeExpressions.types[].presets`):
|
|
120
209
|
|
|
121
|
-
|
|
210
|
+
| Preset | `fast` / `slow` / `weight` / `delay` | Feel |
|
|
211
|
+
| --- | --- | --- |
|
|
212
|
+
| `soft` (the defaults) | 0.1 / 0.6 / 25 / 0 | lands soft and keeps sliding a little |
|
|
213
|
+
| `floaty` | 0.1 / 1 / 40 / 0 | a heavier tail, a longer landing |
|
|
214
|
+
| `trailing` | 0.1 / 0.45 / 25 / 0.1 | the last quarter of the move starts 0.1 s late, like an AE null chain |
|
|
215
|
+
| `endless` | 0.1 / 2 / 14 / 0 | never visibly stops before the next move |
|
|
122
216
|
|
|
123
|
-
**
|
|
217
|
+
- **Choosing the energy:** a longer `slow` and a larger `weight` mean more drift (soft → floaty → never stops). Keep `fast` near 0.1 s so most of the move still tracks its keys.
|
|
218
|
+
- **Glide reshapes keyed motion only:** a constant (single-key) track has nothing to glide, so give an idle layer `wiggle` instead.
|
|
219
|
+
- **Inside a `custom` expression** the same filter is `glide(fast, slow, weight, delay)` with `weight` as 0–1 (`glide(0.10, 1.0, 0.40)` = Floaty), for mixing into a longer expression; the two land on identical frames.
|
|
220
|
+
- **Camera:** key ONE framing per shot (a camera with ~100 keys per track jitters and can't be edited), give every segment a leave-fast/land-soft curve spanning the whole gap, and put `glide` on the position/dolly/zoom tracks (Endless) and the tilt/roll tracks (Floaty) — the shot never sits still and the rotation trails the move as secondary motion.
|
|
221
|
+
- **On a rig,** put the glide on the null's tracks: the expression is part of the null's keyed value, so every child hung off it (see [Layer parenting](#layer-parenting-and-null-layers-keyframeparenting)) inherits the same drift.
|
|
222
|
+
- **In and Out pass** (the editor's Glide presets): one track, `0 %` → off-frame on the entry side (`xPercent`/`yPercent` ±100) eased `Smooth Glide`, last key at **`80 %`** → ∓150 (`Linear`), plus `{"type":"glide","params":{"fast":0.1,"slow":0.6,"weight":25,"delay":0}}`. The glide tail lags in seconds, so ±100 → ∓100 at 100 % would still be on screen at the layer's end; these keys make it leave (−130…−145 % over 1.5–4 s layers) with the slow middle near the centre.
|
|
124
223
|
|
|
125
|
-
|
|
126
|
-
{ "propertyKeyframeFx": {
|
|
127
|
-
"scale": { "type": "custom", "params": { "code": "value + wiggle(2, 0.05)" } },
|
|
128
|
-
"rotate": { "type": "custom", "params": { "code": "loopOut(\"offset\")" } },
|
|
129
|
-
"opacity": { "type": "custom", "params": { "code": "layer(\"My Sequence\").layer(\"Inner Star\").opacity" } }
|
|
130
|
-
} }
|
|
131
|
-
```
|
|
224
|
+
#### Reading inside a sequence
|
|
132
225
|
|
|
133
|
-
|
|
226
|
+
A sequence or grid layer hides its own timeline, and `.layer()` chains into it: `layer("My Sequence").layer("Inner Star").opacity`. The inner clock follows the container's placement, trim **and speed**, so the value is what that inner layer genuinely shows at this instant — a 2× sequence trimmed 1 s in resolves accordingly. Chain again for nested containers (two levels deep, then it stops).
|
|
134
227
|
|
|
135
|
-
|
|
228
|
+
A flat `keyframes: []` array (an all-properties-per-key shape) animates nothing: the editor and renderer read only `propertyKeyframes`, so move any animation you want to keep into it.
|
|
136
229
|
|
|
137
230
|
### Layer parenting and null layers (`keyframeParenting`)
|
|
138
231
|
|
|
@@ -144,13 +237,13 @@ A layer can inherit another layer's **position, rotation and scale** (PRS), Afte
|
|
|
144
237
|
|
|
145
238
|
- **`parentItemId`** is the parent's timeline item `id` (the entry in `channels[].items`), not its `clipId`: one clip can back several items, and each item moves on its own.
|
|
146
239
|
- **`type`** — `additive` (default): the child's own PRS (static fields, PRS tracks, its motion path) is an offset in the parent's space, so the parent's rotation and scale swing and scale that offset. A child at `panX: 20` under a parent rotated 90° with `zoom: 2` sits 40 % of the frame width straight below the parent's centre, itself rotated 90° and doubled. `full-copy`: the child takes the parent's PRS outright; its own PRS is kept but ignored.
|
|
147
|
-
- **Inherited:** `panX/panY/panZ`, `rotateX/rotateY/rotateZ`, `zoom`, `scaleX/scaleY`, their tracks (`translateX/Y/Z`, `xPercent/yPercent`, `rotate`, `rotateX/Y`, `scale`, `scaleX/Y`) and a live motion path. **Not inherited:** opacity, mirror, filters, effects, colours, masks, timing, and the parent's `motionFollow` (a tracked-point link). Fade or recolour a group on each child, and give every layer that should ride a motion track its own `motionFollow`.
|
|
240
|
+
- **Inherited:** `panX/panY/panZ`, `rotateX/rotateY/rotateZ`, `zoom`, `scaleX/scaleY`, their tracks (`translateX/Y/Z`, `xPercent/yPercent`, `rotate`, `rotateX/Y`, `scale`, `scaleX/Y`), the motion-path tracks and a live motion path. **Not inherited:** opacity, mirror, filters, effects, colours, masks, timing, and the parent's `motionFollow` (a tracked-point link). Fade or recolour a group on each child, and give every layer that should ride a motion track its own `motionFollow`.
|
|
148
241
|
- **Chains compose** (null → null → layer): use nested nulls to separate motions, e.g. one null travels, a child null spins, the layers hang off the spinner.
|
|
149
242
|
- **Same level only.** The parent is an item in the same channel list: top-level channels, or the same sequence's inner channels. A reference to a missing item, or one that closes a cycle, is dropped on load.
|
|
150
243
|
- **Time.** A parent is sampled at the same absolute timeline time as the child, never at the child's progress, so parent and child may start, trim and last differently. Outside its own span a parent holds its first/last pose, so give a rig's null a span that covers every child. A track's `position` is still 0–100 % of the parent clip's own duration.
|
|
151
244
|
- **Parentable types** (parent and child): `image`, `video`, `text`, `shape`, `motion`, `sequence`, `grid`, `adjustment`, `null`.
|
|
152
245
|
|
|
153
|
-
**Null layers** (`mediaType: "null"`) are the handle for a rig: a transform with no picture, never drawn in any render. The clip is `{ "id": "nullClip", "source": "LOCAL", "mediaType": "null", "url": "", "proxyUrl": null, "name": "Null", "duration": 6 }`. The item takes the ordinary PRS fields, `propertyKeyframes`, a `motionPath` and its own `keyframeParenting`. `nullColor` / `nullSize` only style the editor's preview marker. Effects, masks, blend, opacity and filters do nothing on a null.
|
|
246
|
+
**Null layers** (`mediaType: "null"`) are the handle for a rig: a transform with no picture, never drawn in any render. The clip is `{ "id": "nullClip", "source": "LOCAL", "mediaType": "null", "url": "", "proxyUrl": null, "name": "Null", "duration": 6 }`. The item takes the ordinary PRS fields, `propertyKeyframes`, a `motionPath` and its own `keyframeParenting`. `nullColor` / `nullSize` only style the editor's preview marker. Effects, masks, blend, opacity and filters do nothing on a null. (The camera solve's "nulls" are shape layers instead — see tracking-data.md (GreenLight Dash only); either can be a parent.)
|
|
154
247
|
|
|
155
248
|
Orbit: two dots circle the frame centre once, upright. The null spins, and each child counter-rotates by the same amount to stay level (drop the child's `rotate` track to let it turn with the orbit).
|
|
156
249
|
|
|
@@ -165,7 +258,7 @@ Orbit: two dots circle the frame centre once, upright. The null spins, and each
|
|
|
165
258
|
{ "id": "iNull", "clipId": "cNull", "freeStart": 0, "trimStart": 0, "trimEnd": 6,
|
|
166
259
|
"propertyKeyframes": { "rotate": [ { "position": 0, "value": "0", "easing": "Linear" }, { "position": 100, "value": "360", "easing": "Linear" } ] } } ] },
|
|
167
260
|
{ "id": "chDots", "name": "Dots", "visible": true, "unsnapped": true, "items": [
|
|
168
|
-
{ "id": "iDotA", "clipId": "cDot", "freeStart": 0, "trimStart": 0, "trimEnd": 6, "shapeType": "ellipse", "
|
|
261
|
+
{ "id": "iDotA", "clipId": "cDot", "freeStart": 0, "trimStart": 0, "trimEnd": 6, "shapeType": "ellipse", "shapeSizeUnit": "px", "shapeWidthPx": 64, "shapeHeightPx": 64, "panX": 25,
|
|
169
262
|
"keyframeParenting": { "parentItemId": "iNull", "type": "additive" },
|
|
170
263
|
"propertyKeyframes": { "rotate": [ { "position": 0, "value": "0", "easing": "Linear" }, { "position": 100, "value": "-360", "easing": "Linear" } ] } } ] }
|
|
171
264
|
]
|