greenlight-motion 0.3.4 → 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 +42670 -40592
- 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,23 +10,46 @@
|
|
|
10
10
|
|
|
11
11
|
## Contents
|
|
12
12
|
|
|
13
|
-
- [Shape layers (`mediaType: "shape"`)](
|
|
14
|
-
- [
|
|
13
|
+
- [Shape layers (`mediaType: "shape"`)](#shape-layers-mediatype-shape)
|
|
14
|
+
- [Sizing (`shapeSizeUnit`)](#sizing-shapesizeunit)
|
|
15
|
+
- [Rounded rectangles — buttons, pills, cards](#rounded-rectangles--buttons-pills-cards)
|
|
16
|
+
- [Fill, opacity and framing on shapes](#fill-opacity-and-framing-on-shapes)
|
|
17
|
+
- [Editable paths (`shapeType: "path"`, `shapePath`)](#editable-paths-shapetype-path-shapepath)
|
|
18
|
+
- [Writing the handles](#writing-the-handles) — the tangent rule for every path in the app
|
|
19
|
+
- [Lines and strokes](#lines-and-strokes)
|
|
20
|
+
- [Shape background (`shapeBackground`)](#shape-background-shapebackground)
|
|
21
|
+
- [Clipping masks (`shapeClipMask` / `textClipMask` / `clipMaskInvert`)](#clipping-masks-shapeclipmask--textclipmask--clipmaskinvert)
|
|
22
|
+
- [Track matte (`trackMatte`)](#track-matte-trackmatte)
|
|
23
|
+
- [Card framing on every visual layer](#card-framing-on-every-visual-layer) — rounded corners, borders and shadows on video, image, text and sequences
|
|
24
|
+
- [Backdrop blur and glass](#backdrop-blur-and-glass)
|
|
25
|
+
- [Traced vectors (`shapeType: "vector"`)](#traced-vectors-shapetype-vector)
|
|
15
26
|
|
|
16
27
|
### Shape layers (`mediaType: "shape"`)
|
|
17
28
|
|
|
18
|
-
A solid-
|
|
29
|
+
A solid-colour rectangle / ellipse / arrow / triangle — or a custom SVG silhouette — with the full photo-layer option set (transforms, filters, keyframes, layer transitions, blend modes). Like text, the clip is synthetic (no `url`) and the LOOK lives on the timeline item:
|
|
19
30
|
|
|
20
|
-
```
|
|
31
|
+
```jsonc
|
|
21
32
|
// clip
|
|
22
33
|
{ "id": "shp1", "source": "LOCAL", "mediaType": "shape", "url": "", "proxyUrl": null, "name": "Shape", "duration": 5 }
|
|
23
34
|
// item fields (on the timeline item referencing shp1; defaults shown)
|
|
24
|
-
{ "shapeType": "rectangle"
|
|
35
|
+
{ "shapeType": "rectangle", "shapeFillColor": "#8b5cf6", "shapeSizeUnit": "pct", "shapeWidthPct": 40, "shapeHeightPct": 40, "shapeWidthPx": null, "shapeHeightPx": null, "shapeRadiusPct": 20, "shapeRadiusPx": null, "shapeSvgUrl": null, "shapePath": null, "shapeClipMask": false, "clipMaskInvert": false }
|
|
25
36
|
```
|
|
26
37
|
|
|
27
|
-
`shapeType` is one of `rectangle` | `ellipse` | `circle` | `arrow` | `triangle` | `path` | `line` | `custom
|
|
38
|
+
`shapeType` is one of `rectangle` | `ellipse` | `circle` | `arrow` | `triangle` | `path` | `line` | `custom` | `vector`; an unknown value loads as `rectangle`.
|
|
39
|
+
|
|
40
|
+
- **Triangles** take `shapeTriDirection` (`up`|`down`|`left`|`right`, default `up`) and `shapeTriApexPct` (0–100, default 50 — where the tip sits along the opposite edge; 0/100 gives a right triangle).
|
|
41
|
+
- **Arrows** take `shapeArrowHeadPct` (head length as % of width, 5–90, default 45) and `shapeArrowShaftPct` (shaft thickness as % of height, 2–100, default 42).
|
|
42
|
+
- **`custom`** renders a user-provided SVG silhouette: set `shapeSvgUrl` to an SVG **data URI** (`data:image/svg+xml;base64,…` or URL-encoded) — the SVG's alpha is the shape and it is recoloured with `shapeFillColor`, so it travels inside the project JSON with no media file. Stills and exports load every `shapeSvgUrl` in the project before the first frame, so an icon whose clip starts mid-timeline appears on its first frame (the live preview loads it on first display).
|
|
43
|
+
- **`vector`** is a traced image: see [Traced vectors](#traced-vectors-shapetype-vector).
|
|
44
|
+
- **`path`** and **`line`** are editable bezier outlines: see [Editable paths](#editable-paths-shapetype-path-shapepath) and [Lines and strokes](#lines-and-strokes).
|
|
45
|
+
|
|
46
|
+
The size is keyframable in the unit the shape uses: tracks `shapeWidthPx`/`shapeHeightPx` or `shapeWidthPct`/`shapeHeightPct` (a circle: `shapeRadiusPx` or `shapeRadiusPct`), each overriding the field of the same name — a track for the unit not in use does nothing. Shapes render in `/render-onserver` (not the fast `/render`). Put shapes on their own `unsnapped` channel above the footage, like captions.
|
|
47
|
+
|
|
48
|
+
#### Sizing (`shapeSizeUnit`)
|
|
28
49
|
|
|
29
|
-
|
|
50
|
+
`"pct"` (the default) reads `shapeWidthPct` as % of the composition **width** and `shapeHeightPct` as % of its **height** — two different bases, so equal percentages are only square on a square composition: `40`/`40` on 1920×1080 is a 768×432 oval. Both may exceed 100. `"px"` reads `shapeWidthPx`/`shapeHeightPx` as absolute composition pixels instead (a `null` px value falls back to the %), and may exceed the composition size.
|
|
51
|
+
|
|
52
|
+
**To draw a circle, size it in px, never in matching percentages**: either `shapeType: "circle"`, which takes a single radius (`shapeRadiusPx` with `shapeSizeUnit: "px"`; its `shapeRadiusPct` fallback is % of the *shorter* composition side, default 20, so it stays round but changes size with the aspect ratio), or an `ellipse` with `shapeSizeUnit: "px"` and equal `shapeWidthPx`/`shapeHeightPx`. Do the same for any shape that must keep its proportions across compositions (a square, a 1:1 badge).
|
|
30
53
|
|
|
31
54
|
#### Rounded rectangles — buttons, pills, cards
|
|
32
55
|
|
|
@@ -34,7 +57,9 @@ For anything with rounded corners — a button, pill, badge, tag, card, chat bub
|
|
|
34
57
|
|
|
35
58
|
- `borderRadiusUnit`: `"px"` (default; composition pixels — a fixed corner) or `"pct"` (% of the shape's **shorter** side, 0–50). `50` is a full pill/capsule at any aspect ratio. Any radius larger than half the shorter side clamps there, so `borderRadius: 9999` in px is also a pill.
|
|
36
59
|
- `borderRadius` is the radius for all four corners (default 0 = square). For different corners set `borderRadiusLinked: false` and `borderRadiusTL` / `borderRadiusTR` / `borderRadiusBR` / `borderRadiusBL`, in the same unit (a corner left out falls back to `borderRadius`) — e.g. a tab with only its top corners rounded.
|
|
37
|
-
- Animate it with the `borderRadius` property-keyframe track (`borderRadiusTL` … `borderRadiusBL` when unlinked). Values are in the item's `borderRadiusUnit`; `"0"` is square, so `0 → 50` in `pct` morphs a square box into a pill.
|
|
60
|
+
- **Animate it** with the `borderRadius` property-keyframe track (`borderRadiusTL` … `borderRadiusBL` when unlinked). Values are in the item's `borderRadiusUnit`; `"0"` is square, so `0 → 50` in `pct` morphs a square box into a pill. Changing `borderRadiusUnit` through the API does not convert existing keys, so rewrite them in the new unit.
|
|
61
|
+
- When the layer itself must scale (a tile that grows to fill the frame, a card that punches in on a `scale` track), set `borderRadiusScaleLock: true`. Every radius is then divided by the layer's scale (`zoom`, the `scale`/`scaleX`/`scaleY` tracks and its parents), so the corner keeps its size on screen instead of growing with the tile. Without it (the default) the corner scales with the layer.
|
|
62
|
+
- Only `rectangle` shapes take a `pct` radius. Every other layer (video, image, text, sequence) takes the radius in px, and its `borderRadius` tracks too (see [Card framing](#card-framing-on-every-visual-layer)); `shapeRadiusPct`/`shapeRadiusPx` are a `circle`'s size, not a corner amount.
|
|
38
63
|
- `borderWidth`/`borderColor`/`borderStyle` stroke the rounded outline, and `shadows` and `backdropBlur` follow it.
|
|
39
64
|
|
|
40
65
|
A pill button — the label is a text layer on the channel above, at the same `panX`/`panY`:
|
|
@@ -50,24 +75,25 @@ A pill button — the label is a text layer on the channel above, at the same `p
|
|
|
50
75
|
|
|
51
76
|
A 12 px-corner card is `"borderRadius": 12` with no unit field.
|
|
52
77
|
|
|
53
|
-
|
|
78
|
+
#### Fill, opacity and framing on shapes
|
|
54
79
|
|
|
55
|
-
**
|
|
80
|
+
- **Fill opacity comes from the alpha of `shapeFillColor`** — use an rgba/8-digit-hex value (e.g. `rgba(139,92,246,0.5)` or `#8b5cf680`) for a semi-transparent fill; there is no separate fill-opacity field.
|
|
81
|
+
- Position/scale/rotation come from the standard item fields (`panX`, `panY`, `zoom`, `rotateZ`, keyframes…). For a whole-layer fade use the standard item `opacity` (0–100; its keyframe track is 0–1).
|
|
82
|
+
- On shape layers the framing fields follow the **shape outline**, not the bounding box: `borderWidth`/`borderColor`/`borderStyle` stroke the outline (all four styles; not on `custom`/`vector` SVG shapes or stroked paths and lines, which use the [line stroke](#lines-and-strokes) instead), and drop `shadows` hug the silhouette (shadow `spread` and `inset` are ignored for shapes).
|
|
56
83
|
|
|
57
|
-
|
|
84
|
+
#### Editable paths (`shapeType: "path"`, `shapePath`)
|
|
58
85
|
|
|
59
|
-
|
|
60
|
-
- **Corner** — `hIn` and `hOut` both `{ "x": 0, "y": 0 }`.
|
|
61
|
-
- **Cusp with curvature on both sides** — break the mirror: give `hIn` and `hOut` directions that are not opposite.
|
|
62
|
-
- **Circular arc** — a quarter-arc wants handles of `0.5523 × radius` laid **perpendicular** to the anchor's radius, following the winding: `0.2761` in the ±0.5 box (that is the Circle preset). Longer handles bulge the arc (Squircle uses 1.34×), shorter ones flatten it.
|
|
86
|
+
A freeform bezier outline instead of a parametric one. `shapePath` is `{ "points": [ … ], "closed": true }`; each point is `{ "x", "y", "hIn": {"x","y"}, "hOut": {"x","y"}, "manual": true }`. Coordinates are **fractions of the shape's own box, centred on it** — `-0.5` is the left/top edge and `0.5` the right/bottom (+y down) — so `shapeWidthPct`/`shapeHeightPct` still size the whole path (points may go outside ±0.5; the render surface grows to fit). Anchors **and** handles are clamped to **±8** box units on load. Write the handles as described in [Writing the handles](#writing-the-handles).
|
|
63
87
|
|
|
64
|
-
|
|
88
|
+
`closed: false` (or `shapePathRenderMode: "stroke"`) draws the path as a **stroked line** with the [line stroke fields](#lines-and-strokes) instead of a filled shape; an open path cannot be a clipping mask.
|
|
65
89
|
|
|
66
|
-
**
|
|
90
|
+
**Ready-made outlines** come from `GET /api/video-editor/capabilities` → **`paths`**: `paths.shapePresets` holds 11 closed shapes (Circle, Rectangle, Rounded, Triangle, Diamond, Pentagon, Hexagon, Star, Burst, Blob, Squircle) and `paths.linePresets` 6 open ones (Straight, Arc, S-curve, Wave, Zigzag, Corner), each as a complete point list with handles already resolved and `manual: true` set — paste one and it renders exactly as published. Take the points from there rather than approximating a shape by hand; on a keyframed outline, write the resampled points into a key instead of the base field.
|
|
67
91
|
|
|
68
|
-
|
|
92
|
+
**Reusing another curve as an outline.** A motion path or camera path can become a shape outline: world-pixel curves are fitted into **this** shape's box with a single scale for both axes (keeping proportions rather than stretching to the box), centred on it, and flattened — `z` is dropped, since a shape draws its XY footprint, and y flips (world +y is up, the box's +y is down).
|
|
69
93
|
|
|
70
|
-
|
|
94
|
+
The outline is keyframable as a whole under the `shapePath` **property-keyframe track** — one of the track kinds whose `value` is a JSON array of points rather than a number or colour (the others are a layer mask's `msk:<mid>:path`, see [Layer Mask entry](video-effects.md#layer-mask-entry), and `motionPathPoints`, see [video-paths.md](video-paths.md#animated-motion-path-motionpathpoints)). Every key must hold the **same number of points**, since they morph one-to-one:
|
|
95
|
+
|
|
96
|
+
```jsonc
|
|
71
97
|
"propertyKeyframes": { "shapePath": [
|
|
72
98
|
{ "position": 0, "value": "[{\"x\":-0.5,\"y\":-0.5,\"hIn\":{\"x\":0,\"y\":0},\"hOut\":{\"x\":0,\"y\":0},\"manual\":true}, …]", "easing": "Smooth" },
|
|
73
99
|
{ "position": 100, "value": "[…same length…]", "easing": "Smooth" }
|
|
@@ -76,7 +102,20 @@ The outline is keyframable as a whole under the `shapePath` **property-keyframe
|
|
|
76
102
|
|
|
77
103
|
The track is only kept on load when the item's `shapeType` is `path` or `line`.
|
|
78
104
|
|
|
79
|
-
|
|
105
|
+
#### Writing the handles
|
|
106
|
+
|
|
107
|
+
One rule covers every bezier path in the app: `shapePath`, line outlines and Layer Mask paths (unit box, `x`/`y`), and `motionPath`, `cameraPath` and `motionPathPoints` keys (world pixels, `x`/`y`/`z` — every handle carries `z` too). `GET /api/video-editor/capabilities` → `paths.anchor` / `paths.spaces` / `paths.tangents` restate it.
|
|
108
|
+
|
|
109
|
+
`hIn`/`hOut` are the cubic-bezier control handles stored as **offsets from their own anchor**, not absolute control points — the segment A→B is the cubic `A → A+A.hOut → B+B.hIn → B`. **Write both handles on every anchor.** A missing or zero-length handle is a hard **corner**, and nothing fills one in: the auto-tangent pass runs only on a hand edit in the editor, while load, save, preview, export and `/render-onserver` draw the stored handles verbatim. An outline written as bare points renders as a straight-line **polygon**, not the smooth shape it looks like on paper. Set **`manual: true` on every anchor** too, or the next hand edit in the editor re-smooths the whole outline out from under the design.
|
|
110
|
+
|
|
111
|
+
- **Smooth anchor** — the editor's own Catmull-Rom tangent, componentwise: `hOut = (P[next] − P[prev]) / 6`, `hIn = −hOut`. On a `closed` path the neighbours wrap; on an open one the first anchor uses `hOut = (P[1] − P[0]) / 3` and the last `hIn = −(P[last] − P[last−1]) / 3`.
|
|
112
|
+
- **Corner** — `hIn` and `hOut` both `{ "x": 0, "y": 0 }` (plus `"z": 0` in world space).
|
|
113
|
+
- **Cusp with curvature on both sides** — break the mirror: give `hIn` and `hOut` directions that are not opposite.
|
|
114
|
+
- **Circular arc** — a quarter-arc wants handles of `0.5523 × radius` laid **perpendicular** to the anchor's radius, following the winding: `0.2761` in the ±0.5 box (that is the Circle preset). Longer handles bulge the arc (Squircle uses 1.34×), shorter ones flatten it.
|
|
115
|
+
|
|
116
|
+
#### Lines and strokes
|
|
117
|
+
|
|
118
|
+
A `line` is an editable path that renders as a **stroke** rather than a fill (its `closed` flag is ignored — a line is always open). A `path` is stroked the same way when `closed: false` or when `shapePathRenderMode` is `"stroke"` (default `"fill"`) — what you want for a signature, a route line or any figure where a fill would close the curve. Stroke fields, all on the item: `lineWidth` (px, 0–400, default 8), `lineCap` (`butt`|`round`|`square`, default `round`), `lineJoin` (`round`|`miter`|`bevel`, default `round`), `lineDash` (`solid`|`dashed`|`dotted`|`custom`, default `solid`). The stroke is painted with the shape's own fill, so `shapeFillColor` **and** a full `shapeBackground` (gradient / shader / pattern / image) both work on a line. The card `borderWidth`/`borderStyle` do **not** apply — the stroke is the outline.
|
|
80
119
|
|
|
81
120
|
**Trim Paths (`lineTrimStart` / `lineTrimEnd` / `lineTrimOffset`).** Draw only part of the outline, measured by arc length: `lineTrimStart` and `lineTrimEnd` are 0–100 (default 0 and 100), `lineTrimOffset` is −100…100 (default 0) and slides the span along the path, wrapping on a closed one. Keyframing `lineTrimEnd` 0 → 100 is the "write the line on" move. Trim applies to any stroked shape.
|
|
82
121
|
|
|
@@ -122,28 +161,52 @@ A tapered arrow that writes itself on:
|
|
|
122
161
|
- Flat fields: `lineWidth`, `lineTrimStart`, `lineTrimEnd`, `lineTrimOffset`, `lineDashOffset`, `lineDashSpeed`, `lineOutlineWidth`, `lineGhost`, and the custom dash pattern as `lineDashLength1`, `lineDashGap1`, `lineDashLength2`, `lineDashGap2`.
|
|
123
162
|
- `lineStroke` numbers: a track named `line` + card key + field, both capitalised — `lineBrushNibAngle`, `lineWarpZigzagRidges`, `lineCopiesRotate`, `lineStampsSpacing`, `linePaintRepeat` … — except these, which keep their own names: `lineTaperStartLength`, `lineTaperEndLength`, `lineTaperStartWidth`, `lineTaperEndWidth`, `lineProfileOffset`, `lineWidthWaveAmount`, `lineWidthWavePhase`, `lineWarpOffset`, `lineZigzagSize`, `lineWiggleSize`, `lineWarpWaveAmplitude`, `lineCopySpacing`, `lineEndsSize`, `lineStampOffset`, `lineGradientOffset`, `lineFadeIn`, `lineFadeOut`.
|
|
124
163
|
- `lineStroke` colours (key `value`s are CSS colours, interpolated as colours): `linePaintColor1`–`3` and `lineCopiesColor1`–`3` (the entries of `colors`), `linePaintCoreColor`, and `lineStampsColor` (only recolours stamps that have their own `color`).
|
|
164
|
+
|
|
125
165
|
A track overrides its field only while that card is on the layer; values use the field's units and are clamped to its range.
|
|
126
166
|
|
|
127
167
|
Running Glow on a stroked shape should set `glowTraceThin: 1` and a `glowEdgeWidth` near the line's own thickness (in @1080 px) — the default filled-shape tracing finds no contour on a thin stroke and the effect is invisible.
|
|
128
168
|
|
|
129
|
-
|
|
169
|
+
#### Shape background (`shapeBackground`)
|
|
130
170
|
|
|
131
|
-
|
|
171
|
+
Beyond the flat `shapeFillColor`, a shape can take a full **background** via `shapeBackground` — the same `backgroundConfig` object as the project background (solid, gradient, GLSL shader, pattern, or image; the schema, preset ids and shader rules are in [video-backgrounds.md](video-backgrounds.md)). It fills the shape's silhouette; when `shapeBackground` is `null` (the default) the shape falls back to the flat `shapeFillColor`. Shader and animated-gradient shape backgrounds render only in the Video Editor.
|
|
132
172
|
|
|
133
|
-
|
|
173
|
+
#### Clipping masks (`shapeClipMask` / `textClipMask` / `clipMaskInvert`)
|
|
134
174
|
|
|
135
|
-
|
|
175
|
+
A shape can act as a **clipping mask**: set `shapeClipMask: true` and the layers below the shape's channel show only where the shape has coverage (the fill itself isn't drawn; the fill's alpha sets mask strength, and the shape's transforms/keyframes move the mask). **Text layers** take the same treatment with `textClipMask: true` — the glyph coverage is the matte, so footage plays inside the letters, and typography plus the split/stagger animation fields animate it. While a text layer masks, its own text shadow (`textShadowX/Y/Blur/Spread`) and caption bar (`textBgEnabled`) are ignored, as is card framing (`borderWidth`, `shadows`) — that alpha would leak into the matte; use the `blur` filter to feather instead. Either kind accepts `clipMaskInvert: true` to flip the matte (knockout: the layers below are hidden inside the shape/letters and shown everywhere else). A mask affects **all** channels below it in the same stack, masks stack, and they composite both at the top level and inside a sequence's inner timeline. A frame with an active mask skips the global Depth of Field.
|
|
136
176
|
|
|
137
|
-
|
|
138
|
-
- **Border** — `borderWidth` + `borderColor` (default 0 / `#ffffff`) + `borderStyle` (`solid` | `dashed` | `dotted` | `double`, default `solid`). `borderWidth` and `borderColor` are also keyframe tracks (`propertyKeyframes.borderWidth` in px, `propertyKeyframes.borderColor` as CSS colours) — a focus ring that grows, an outline that turns red on an error; a track overrides its field for the frame.
|
|
139
|
-
- **Shadows** — `shadows`: an array of up to 4 layers, each `{ x, y, blur, spread, color, inset }` (CSS box-shadow semantics; `inset: true` draws an inner shadow; the first entry sits on top). Legacy single-shadow fields (`shadowX`, `shadowY`, `shadowBlur`, `shadowSpread`, `shadowColor`) are still read when `shadows` is absent and migrate on load.
|
|
177
|
+
#### Track matte (`trackMatte`)
|
|
140
178
|
|
|
141
|
-
|
|
179
|
+
The After Effects analog, distinct from clipping masks: it scopes to **one consumer layer** instead of the whole stack below, and supports luma. Set on the consumer item: `trackMatte: { sourceId: "<other item id>", mode: "alpha" | "alphaInverted" | "luma" | "lumaInverted" }` — the consumer then shows only where the source layer has alpha coverage (`alpha`) or brightness (`luma`; computed premultiplied, so transparent source areas contribute nothing), inverted variants flip it. The **source layer is hidden from output** while at least one active consumer references it; its transforms/keyframes still animate the matte.
|
|
142
180
|
|
|
143
|
-
-
|
|
181
|
+
- The source must be another **visual** item (not audio/camera/adjustment) at the **same editing level**; the reference is by item id, not stack position; several consumers may share one source.
|
|
182
|
+
- A mutual pair (A mattes with B while B mattes with A) is auto-broken on save.
|
|
183
|
+
- If the source clip isn't under the playhead the matte is EMPTY — `alpha`/`luma` hide the consumer, inverted modes show it untouched.
|
|
184
|
+
- The matte clips the consumer's **finished** look (after its filters, glow, overlay effects and motion blur), matching AE. The **source is read finished too**: its own `effects` chain, `layerEffects`, rig warp and motion blur shape the matte, so an `extract` (histogram key) or `alphaChoker` on the source layer is the normal way to author a matte from footage rather than from a silhouette.
|
|
185
|
+
- Works at the top level and inside a sequence's inner timeline alike (inside a sequence only the layer's `effects` chain shapes the matte — glow, motion blur and the other Layer Effects are top-level stages); grids strip it on grouping.
|
|
186
|
+
|
|
187
|
+
#### Card framing on every visual layer
|
|
188
|
+
|
|
189
|
+
Every visual layer (video/image/shape/text/sequence) supports **card framing** item fields, all in canvas px:
|
|
190
|
+
|
|
191
|
+
- **Rounded corners** — `borderRadius` (uniform, default 0); for per-corner radii set `borderRadiusLinked: false` and use `borderRadiusTL`, `borderRadiusTR`, `borderRadiusBR`, `borderRadiusBL` (each falls back to `borderRadius`). On media layers the radius is px; only `rectangle` shapes can also take a % of the shorter side ([Rounded rectangles](#rounded-rectangles--buttons-pills-cards)). It is keyframable on every layer: `propertyKeyframes.borderRadius` (the four corner tracks while unlinked) overrides the field for the frame, so a full-frame video can round into a card. The radius is drawn before the layer's scale, so a scaled picture's corners grow with it; `borderRadiusScaleLock: true` divides every radius by the layer's scale and keeps the corner's on-screen size. Border and shadows follow the rounded outline either way. A picture tile that grows 4× with a 24 px corner the whole way:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{ "zoom": 0.25, "borderRadius": 24, "borderRadiusScaleLock": true,
|
|
195
|
+
"propertyKeyframes": { "scale": [
|
|
196
|
+
{ "position": 0, "value": "1", "easing": "AE Smooth" },
|
|
197
|
+
{ "position": 100, "value": "4", "easing": "AE Smooth" }
|
|
198
|
+
] } }
|
|
199
|
+
```
|
|
200
|
+
- **Border** — `borderWidth` + `borderColor` (default 0 / `#ffffff`) + `borderStyle` (`solid` | `dashed` | `dotted` | `double`, default `solid`). `borderWidth` and `borderColor` are also keyframe tracks (`propertyKeyframes.borderWidth` in px, `propertyKeyframes.borderColor` as CSS colours) — a focus ring that grows, an outline that turns red on an error; a track overrides its field for the frame.
|
|
201
|
+
- **Shadows** — `shadows`: an array of up to 4 layers, each `{ x, y, blur, spread, color, inset }` (CSS box-shadow semantics; `inset: true` draws an inner shadow; the first entry sits on top). The single-shadow fields (`shadowX`, `shadowY`, `shadowBlur`, `shadowSpread`, `shadowColor`) are read when `shadows` is absent and are converted on load.
|
|
144
202
|
|
|
145
203
|
These bake into `/render-onserver` like everything else.
|
|
146
204
|
|
|
147
|
-
|
|
205
|
+
#### Backdrop blur and glass
|
|
206
|
+
|
|
207
|
+
- **Backdrop Blur** (shape layers ONLY — matte / frosted glass) — `backdropBlur` (px, 0–200, default 0 = off) blurs whatever is rendered **behind** the shape, inside its outline; the shape's own fill then draws over the blur, so a semi-transparent fill (`shapeFillColor: "rgba(255,255,255,0.25)"`) reads as frosted glass and a tinted one as smoked glass. `backdropSaturation` (%, 0–300, default 100) re-saturates the blurred backdrop (boost it for the iOS/macOS glass look). Both are keyframable as the `backdropBlur` / `backdropSaturation` tracks. The blur covers the shape's full coverage regardless of the fill's alpha (the plate alpha is normalised by the fill's peak alpha × `opacity`), fades with a gradient fill, and is ignored on clip-mask shapes and on every non-shape layer. A fully opaque fill hides the blur entirely — always pair `backdropBlur` with a semi-transparent `shapeFillColor`. (The editor thins a solid fill to 35 % when `backdropBlur` first goes above 0 and remembers the previous paint in `backdropGlassFill`; setting `backdropBlur` back to 0 there restores it unless the fill was edited since. The API does neither: set `backdropBlur: 0` AND the fill you want.)
|
|
208
|
+
- **Glass** (shape layers only: `rectangle`, `ellipse`, `circle`) — the `glass` Layer Effect turns the shape into a pane of real glass that refracts, frosts, tints and shadows everything rendered **behind** it; see [Glass entry](video-effects.md#glass-entry).
|
|
209
|
+
|
|
210
|
+
#### Traced vectors (`shapeType: "vector"`)
|
|
148
211
|
|
|
149
|
-
**
|
|
212
|
+
The editor's **Image to vector** tracer makes a `vector` shape: the traced artwork is drawn from `shapeSvgUrl` in its own colours (a `custom` SVG is recoloured with `shapeFillColor`; a `vector` one is not), contain-fitted in the shape's box. The layer keeps where it came from: `shapeVectorSourceUrl` (the bitmap it was traced from), `shapeVectorFileName` (that file's name, ≤ 240 chars), `shapeVectorPathCount` (how many sub-paths the trace produced), `shapeVectorTrace` (the tracer settings, so the trace can be re-run at a different threshold) and `shapeVectorEditablePath` (an editable outline: converting the layer to a `path` in the editor uses it, drawn as a stroke). Preserve all five when you edit such a layer, or the user loses the ability to re-trace or convert it.
|
|
@@ -10,36 +10,38 @@
|
|
|
10
10
|
|
|
11
11
|
## Contents
|
|
12
12
|
|
|
13
|
-
- [When to use them](
|
|
14
|
-
- [How every recipe works](
|
|
15
|
-
- [Writing a recipe onto a layer](
|
|
16
|
-
- [The catalogue](
|
|
13
|
+
- [When to use them](#when-to-use-them)
|
|
14
|
+
- [How every recipe works](#how-every-recipe-works)
|
|
15
|
+
- [Writing a recipe onto a layer](#writing-a-recipe-onto-a-layer)
|
|
16
|
+
- [The catalogue](#the-catalogue) and [Recipes](#recipes)
|
|
17
17
|
|
|
18
18
|
## When to use them
|
|
19
19
|
|
|
20
|
-
**Prefer these
|
|
20
|
+
**Prefer these 22 recipes whenever text should feel energetic** — a hook, a title card, a promo or social
|
|
21
21
|
line, a beat-synced word, a call to action, a reveal; anything the user calls punchy, bouncy, poppy, playful,
|
|
22
22
|
dynamic, kinetic, lively, "with energy" or "make it pop". They are the app's own Kinetic presets: tuned
|
|
23
23
|
in-and-out motion that a hand-built animator rarely matches, so start from one instead of composing animators
|
|
24
24
|
from scratch, then adjust.
|
|
25
25
|
|
|
26
26
|
Not for calm or readable-first text: captions under speech, tutorials, long paragraphs, lower thirds. Use a
|
|
27
|
-
plain Time fade or rise there (Fade Up
|
|
27
|
+
plain Time fade or rise there ([Classic Fade Up](video-text-presets.md#classic-fade-up) or
|
|
28
|
+
[Classic Rise (Mask)](video-text-presets.md#classic-rise-mask)) — a swinging paragraph is hard to read.
|
|
28
29
|
|
|
29
30
|
Pick by the text:
|
|
30
31
|
|
|
31
|
-
- **2–4 short lines** (a
|
|
32
|
-
- **One line of
|
|
32
|
+
- **2–4 short stacked lines** (one or a few words each): a *words* recipe; Swing Pop is the signature one.
|
|
33
|
+
- **One line of up to ~5 words** (a hook, a title, a call to action): a *words* recipe. A longer line or a caption
|
|
34
|
+
under speech reads cheap with this much motion; use a calm preset there ([video-design.md](video-design.md#text-animation)).
|
|
33
35
|
- **One short word or title, up to ~20 letters**: a *letters* recipe.
|
|
34
36
|
|
|
35
37
|
Preferred recipes: when the user has not named one, choose from these first, as long as the recipe fits the text by
|
|
36
38
|
the rules above: [Swing Pop](#swing-pop), [Pendulum](#pendulum), [Fan Deal](#fan-deal) and [Whip](#whip)
|
|
37
|
-
(
|
|
39
|
+
(words; they read well stacked); [Word Swing](#word-swing) (words); [Letter Vortex](#letter-vortex)
|
|
38
40
|
(letters).
|
|
39
41
|
|
|
40
42
|
## How every recipe works
|
|
41
43
|
|
|
42
|
-
Each recipe is **two Time animators**
|
|
44
|
+
Each recipe is exactly **two Time animators** on one text layer, and nothing else:
|
|
43
45
|
|
|
44
46
|
- the **in** animator (`"animate": "in"`): every unit arrives from its effects, `stagger` seconds apart,
|
|
45
47
|
from the layer's start;
|
|
@@ -48,33 +50,36 @@ Each recipe is **two Time animators** (some add a third, a quick fade) on one te
|
|
|
48
50
|
|
|
49
51
|
There are no keyframes, so a recipe fits any layer length: changing the layer's duration keeps the entrance at
|
|
50
52
|
the start and the exit at the end. One side takes `(units − 1) × stagger + duration` seconds; keep the layer
|
|
51
|
-
longer than the in and out sides together plus the time the text should rest (
|
|
53
|
+
longer than the in and out sides together plus the time the text should rest (word recipes on 2–4 lines: 2.5 s or more;
|
|
52
54
|
letter recipes on long text need more).
|
|
53
55
|
|
|
54
|
-
`"around": "all"` turns every unit around the centre of the **whole** text, not its own centre, so outer
|
|
55
|
-
and letters travel on arcs — Swing
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
`"around": "all"` turns every unit around the centre of the **whole** text, not its own centre, so outer words
|
|
57
|
+
and letters travel on arcs — Word Swing's and Letter Vortex's look. `textAnimatorPivotX` / `textAnimatorPivotY`
|
|
58
|
+
(−100…100) move each unit's turning point: Pendulum hangs each word from its top edge (`textAnimatorPivotY: -100`),
|
|
59
|
+
Fan Deal fans each word from its bottom edge, Door Swing hinges each word on its left edge, Domino stands each word
|
|
60
|
+
on its bottom-left corner.
|
|
58
61
|
|
|
59
|
-
The curves do the energy: `Overshoot` lands past rest and settles, `Elastic` and `Bounce Out` wobble or bounce,
|
|
60
|
-
`Swing` dips before it springs, and `Anticipate` (the usual exit) winds back the other way, then snaps through
|
|
62
|
+
The curves do the energy: `Overshoot` lands past rest and settles, `Elastic` and `Bounce Out` wobble or bounce, and `Anticipate` (the usual exit) winds back the other way, then snaps through
|
|
61
63
|
in the last frames.
|
|
62
64
|
|
|
63
65
|
## Writing a recipe onto a layer
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- **
|
|
69
|
-
|
|
67
|
+
Follow [Writing a recipe onto a layer](video-text-presets.md#writing-a-recipe-onto-a-layer) in text-presets.md (replace or
|
|
68
|
+
add, ids, scaling lengths to the font size). Kinetic recipes have no keyframes, so they fit any layer length as
|
|
69
|
+
written. In short:
|
|
70
|
+
- **Replace**: delete the old `textAnimators` and every `txa:` track, and set both pivots to the recipe's values (0
|
|
71
|
+
when it has none). Recipes reuse ids like `a1`/`p1`, so a leftover track or pivot would drive the new animator.
|
|
72
|
+
- **Lengths are for a 100 px font.** Multiply every `position` `x` / `y` / `z`, `blur` `amount`, `rotate3d`
|
|
73
|
+
`perspective` and the layer's `textAnimatorPerspective` by the layer's font size in px / 100: `textFontSize`, or in pct mode
|
|
74
|
+
`textFontSizePct × videoSize.h / 100`. Angles, scales, opacities and timings stay as written.
|
|
70
75
|
- Faster or slower: divide every `stagger` and `duration` by the same factor (2 = twice as fast).
|
|
71
76
|
- Colour, font and size are the layer's own; the recipes only move the text.
|
|
72
77
|
- To check it, render a still at the middle of the entrance and one in the middle of the layer
|
|
73
|
-
(
|
|
78
|
+
(rendering.md): mid-entrance the text is moving, mid-layer it rests exactly as typed.
|
|
74
79
|
|
|
75
80
|
## The catalogue
|
|
76
81
|
|
|
77
|
-
<!-- KINETIC:BEGIN (generated
|
|
82
|
+
<!-- KINETIC:BEGIN (generated from the app's presets; do not edit by hand) -->
|
|
78
83
|
|
|
79
84
|
| Recipe | What it does | Unit | Turns around | In · Out curve |
|
|
80
85
|
|---|---|---|---|---|
|
|
@@ -82,14 +87,14 @@ in the last frames.
|
|
|
82
87
|
| [Word Swing](#word-swing) | Words pop out of the middle of the text in random order and swing round into place; at the end they wind up and pop back | words | centre of the whole text | Overshoot · Anticipate |
|
|
83
88
|
| [Letter Vortex](#letter-vortex) | Letters spiral out of the centre of the text one by one; at the end they spin back into it | letters | centre of the whole text | Overshoot · Anticipate |
|
|
84
89
|
| [Center Burst](#center-burst) | The middle words burst out first and the others follow, wobbling into place; at the end they shrink away from the outside in | words | each word's centre | Elastic · Back In |
|
|
85
|
-
| [Pendulum](#pendulum) | Words hang from their top edge and swing in like a pendulum until they settle; at the end they swing away | words | each word's
|
|
90
|
+
| [Pendulum](#pendulum) | Words hang from their top edge and swing in like a pendulum until they settle; at the end they swing away | words | each word's top edge | Elastic · Anticipate |
|
|
86
91
|
| [Spin Pop](#spin-pop) | Each word spins a full turn in place as it grows in; at the end each spins back out | words | each word's centre | Back Out · Back In |
|
|
87
92
|
| [Domino](#domino) | Words stand up on their bottom-left corner and drop flat with a bounce, one after another; at the end they fall over | words | each word's bottom-left corner | Bounce Out · Ease In |
|
|
88
93
|
| [Flip Pop](#flip-pop) | Words flip up into view in 3D, past flat and back; at the end they tip back and flip away | words | each word's centre | Overshoot · Anticipate |
|
|
89
94
|
| [Card Turn](#card-turn) | Words turn to face you like cards, one by one, overshooting the turn; at the end they turn away | words | each word's centre | Overshoot · Anticipate |
|
|
90
95
|
| [Slam Zoom](#slam-zoom) | Words slam down from huge, punch past their size and settle; at the end they fly back at the camera | words | each word's centre | Overshoot · Expo In |
|
|
91
96
|
| [Orbit](#orbit) | Words turn in from a third of a turn away and ease into place; at the end they turn off | words | each word's centre | Smooth Overshoot · Anticipate |
|
|
92
|
-
| [Fan Deal](#fan-deal) | Words fan up from their bottom edge like cards dealt from a hand, last word first; at the end they fan away | words | each word's
|
|
97
|
+
| [Fan Deal](#fan-deal) | Words fan up from their bottom edge like cards dealt from a hand, last word first; at the end they fan away | words | each word's bottom edge | Overshoot · Anticipate |
|
|
93
98
|
| [Whip](#whip) | Words whip in from the left leaning into the move, snap past and settle; at the end they lean back and whip off to the right | words | each word's centre | Overshoot · Anticipate |
|
|
94
99
|
| [Rubber Letters](#rubber-letters) | Letters spring out of thin slivers and wobble like rubber; at the end they stretch tall and squash flat | letters | each letter's centre | Elastic · Anticipate |
|
|
95
100
|
| [Pop Letters](#pop-letters) | Letters pop up in random order, each with a little twist; at the end they swell and pop away at random | letters | each letter's centre | Overshoot · Anticipate |
|
|
@@ -97,7 +102,9 @@ in the last frames.
|
|
|
97
102
|
| [Coin Spin](#coin-spin) | Letters spin in like coins, one after another, and rock to a stop; at the end they spin away | letters | each letter's centre | Overshoot · Anticipate |
|
|
98
103
|
| [Converge](#converge) | Words shrink down from far bigger, the outer ones first, and settle; at the end they burst outward | words | each word's centre | Expo Out · Expo In |
|
|
99
104
|
| [Roll In](#roll-in) | Letters roll in from the right like wheels and rock back into place; at the end they roll off to the left | letters | each letter's centre | Overshoot · Back In |
|
|
100
|
-
| [Door Swing](#door-swing) | Words swing open in 3D on a hinge at their left edge, past flat and back; at the end they swing shut | words | each word's
|
|
105
|
+
| [Door Swing](#door-swing) | Words swing open in 3D on a hinge at their left edge, past flat and back; at the end they swing shut | words | each word's left edge | Overshoot · Anticipate |
|
|
106
|
+
| [Title Push 3D](#title-push-3d) | Words fly in from depth one by one, overshoot toward the camera and settle as the line glides over to make room; at the end they rush past the camera | words | each word's centre | Back Out · Ease In |
|
|
107
|
+
| [Perspective Type](#perspective-type) | Lines drive in one after another on a plane tilted toward the camera, which then swings flat; at the end they sink back into depth | lines | each line's centre | AE Smooth · Ease In |
|
|
101
108
|
|
|
102
109
|
## Recipes
|
|
103
110
|
|
|
@@ -446,4 +453,48 @@ Words swing open in 3D on a hinge at their left edge, past flat and back; at the
|
|
|
446
453
|
}
|
|
447
454
|
```
|
|
448
455
|
|
|
456
|
+
### Title Push 3D
|
|
457
|
+
|
|
458
|
+
Words fly in from depth one by one, overshoot toward the camera and settle as the line glides over to make room; at the end they rush past the camera.
|
|
459
|
+
|
|
460
|
+
```json
|
|
461
|
+
{
|
|
462
|
+
"textAnimators": [
|
|
463
|
+
{ "aid": "a1", "name": "Fly in",
|
|
464
|
+
"range": {"basedOn": "words", "drive": "time", "animate": "in", "around": "word", "stagger": 0.2, "duration": 0.75, "curve": "Back Out"},
|
|
465
|
+
"effects": [{"pid": "p1", "type": "position", "x": 0, "y": 35, "z": -2600}, {"pid": "p2", "type": "rotate3d", "x": -50, "y": 0, "perspective": 600}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
|
|
466
|
+
{ "aid": "a2", "name": "Push",
|
|
467
|
+
"range": {"basedOn": "lines", "drive": "time", "animate": "in", "around": "all", "stagger": 0, "duration": 1.6, "curve": "Sine Out"},
|
|
468
|
+
"effects": [{"pid": "p4", "type": "position", "x": 240, "y": 0}] },
|
|
469
|
+
{ "aid": "a3", "name": "Rush out",
|
|
470
|
+
"range": {"basedOn": "words", "drive": "time", "animate": "out", "around": "word", "order": "backward", "stagger": 0.05, "duration": 0.55, "curve": "Ease In"},
|
|
471
|
+
"effects": [{"pid": "p5", "type": "position", "x": 0, "y": 0, "z": 900}, {"pid": "p6", "type": "opacity", "opacity": 0}] }
|
|
472
|
+
],
|
|
473
|
+
"textAnimatorCamera": "layer",
|
|
474
|
+
"textAnimatorPerspective": 1300
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Perspective Type
|
|
479
|
+
|
|
480
|
+
Lines drive in one after another on a plane tilted toward the camera, which then swings flat; at the end they sink back into depth.
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"textAnimators": [
|
|
485
|
+
{ "aid": "a1", "name": "Lines in",
|
|
486
|
+
"range": {"basedOn": "lines", "drive": "time", "animate": "in", "around": "line", "stagger": 0.5, "duration": 0.5, "curve": "AE Smooth"},
|
|
487
|
+
"effects": [{"pid": "p1", "type": "position", "x": 0, "y": 140}, {"pid": "p2", "type": "scale", "scale": 55}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
|
|
488
|
+
{ "aid": "a2", "name": "Tilted plane",
|
|
489
|
+
"range": {"basedOn": "lines", "drive": "time", "animate": "in", "around": "all", "delay": 1.2, "stagger": 0, "duration": 0.7, "curve": "Natural"},
|
|
490
|
+
"effects": [{"pid": "p4", "type": "rotate3d", "x": 32, "y": 25, "perspective": 300}, {"pid": "p5", "type": "rotation", "angle": -6}, {"pid": "p6", "type": "scale", "scale": 150}, {"pid": "p7", "type": "position", "x": 16, "y": -4}] },
|
|
491
|
+
{ "aid": "a3", "name": "Sink out",
|
|
492
|
+
"range": {"basedOn": "lines", "drive": "time", "animate": "out", "around": "line", "order": "backward", "stagger": 0.08, "duration": 0.5, "curve": "Ease In"},
|
|
493
|
+
"effects": [{"pid": "p8", "type": "position", "x": 0, "y": 0, "z": -800}, {"pid": "p9", "type": "opacity", "opacity": 0}] }
|
|
494
|
+
],
|
|
495
|
+
"textAnimatorCamera": "layer",
|
|
496
|
+
"textAnimatorPerspective": 300
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
449
500
|
<!-- KINETIC:END -->
|