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.
@@ -6,10 +6,13 @@
6
6
 
7
7
  # GreenLight Dash — Video Editor Layer Effects and Overlay Effects
8
8
 
9
- > Part of the `greenlight-dash` skill. Read `video-projects.md` first for the project, clip, channel, item, timing and versioned-write contracts; read [video-keyframes.md](video-keyframes.md) when animating effects.
9
+ > Part of the `greenlight-dash` skill. Read [video-projects.md](video-projects.md) first for the project, clip, channel, item, timing and versioned-write contracts; read [video-keyframes.md](video-keyframes.md) when animating effects. Effect details live in [effects-overlay.md](#greenlight-dash--overlay-effects-reference), [effects-layer.md](#greenlight-dash--layer-effects-reference), [effects-warps.md](#greenlight-dash--warp-layer-effects), [effects-depth.md](#greenlight-dash--depth-effects-and-real-3d-on-models), [masks.md](#greenlight-dash--layer-masks) and [color-correction.md](#greenlight-dash--colour-correction-and-luts); use the [Effect index](#effect-index) to pick one.
10
10
 
11
11
  ## Contents
12
12
 
13
+ - [Effect index](#effect-index)
14
+ - [Overlay Effects index](#overlay-effects-index)
15
+ - [Layer Effects index](#layer-effects-index)
13
16
  - [Preferred and avoided effects](#preferred-and-avoided-effects)
14
17
  - [Shockwave only on request](#shockwave-only-on-request)
15
18
  - [Effects in an intro](#effects-in-an-intro)
@@ -21,43 +24,135 @@
21
24
  - [Regular-layer Overlay Effects example](#regular-layer-overlay-effects-example)
22
25
  - [Adjustment-layer Overlay Effects example](#adjustment-layer-overlay-effects-example)
23
26
  - [Overlay Effect keyframes](#overlay-effect-keyframes)
27
+ - [Installed effects (`local:<id>`)](#installed-effects-localid)
24
28
  - [Custom Overlay Effect (GLSL)](#custom-overlay-effect-glsl)
25
29
  - [Layer Effects (`item.layerEffects`)](#layer-effects-itemlayereffects)
26
- - [Running Glow entry](#running-glow-entry)
27
- - [Build Depth Map entry](#build-depth-map-entry)
28
- - [3D Depth entry](#3d-depth-entry)
29
- - [Depth Scan entry](#depth-scan-entry)
30
- - [Motion Blur entry](#motion-blur-entry)
31
- - [Posterize Time entry](#posterize-time-entry)
32
- - [Motion Trail entry](#motion-trail-entry)
33
- - [Grow Fill entry](#grow-fill-entry)
34
- - [Rig (Puppet) entry](#rig-puppet-entry)
35
- - [Repetile entry](#repetile-entry)
36
- - [Region Distort entry](#region-distort-entry)
37
- - [Bend entry](#bend-entry)
38
- - [Corner Pin entry](#corner-pin-entry)
39
- - [Mesh Warp entry](#mesh-warp-entry)
40
- - [Bezier Warp entry](#bezier-warp-entry)
41
- - [Droste entry](#droste-entry)
42
- - [Glass entry](#glass-entry)
43
- - [Chroma Key entry](#chroma-key-entry)
44
- - [Layer Mask entry](#layer-mask-entry)
45
- - [Mockup Frame entry](#mockup-frame-entry)
46
30
  - [Complete regular-item example](#complete-regular-item-example)
47
- - [Adjustment-layer LUT](#adjustment-layer-lut)
48
- - [Color Correction (`item.colorCorrection`)](#color-correction-itemcolorcorrection)
49
31
  - [Render and editing rules](#render-and-editing-rules)
50
32
 
33
+ ## Effect index
34
+
35
+ Find an effect by id here, then open only its page. Item fields that are not effects: colour correction (`colorCorrection`) and adjustment LUTs (`lut`) are in [color-correction.md](#greenlight-dash--colour-correction-and-luts); a shape's `backdropBlur` and `shapeBackground` are in [video-shapes.md](video-shapes.md) and [video-backgrounds.md](video-backgrounds.md).
36
+
37
+ ### Overlay Effects index
38
+
39
+ Flags: **PRO** renders only with a PRO license (free to preview); **3D** renders in real 3D on a `model3d` layer when the entry carries `"real3d": true` ([Real 3D on 3D model layers](#real-3d-on-3d-model-layers)); **avoid** and **on request** follow [Preferred and avoided effects](#preferred-and-avoided-effects). "Registry only" means capabilities is the whole reference.
40
+
41
+ | `id` | Name | Group | Scope | Flags | Section |
42
+ |---|---|---|---|---|---|
43
+ | `glow` | Deep Glow | Glow | layer + adjustment | | [Deep Glow (`glow`)](#deep-glow-glow) |
44
+ | `bloom` | Bloom | Glow | layer + adjustment | 3D | [Bloom (`bloom`)](#bloom-bloom) |
45
+ | `auraBlur` | Aura Blur | Glow | layer + adjustment | 3D | registry only |
46
+ | `neonGlow` | Neon Glow | Glow | layer only | PRO · 3D | [Neon Glow](#neon-glow) |
47
+ | `linesDraw` | Lines Draw | Glow | layer only | PRO · 3D · avoid | [Lines Draw](#lines-draw) |
48
+ | `lightRays` | Light Rays | Light | layer + adjustment | 3D | [Light Rays](#light-rays) |
49
+ | `lightSweep` | Light Sweep | Light | layer + adjustment | | [Light Sweep](#light-sweep) |
50
+ | `gradientBeam` | Gradient Motion Beam | Gradient | layer + adjustment | 3D | [Gradient Motion Beam type](#gradient-motion-beam-type) |
51
+ | `gradientSweep` | Gradient Sweep | Gradient | layer + adjustment | PRO · 3D | [Gradient Sweep](#gradient-sweep) |
52
+ | `gradientGlow` | Gradient Glow | Gradient | layer + adjustment | PRO | [Gradient Glow](#gradient-glow) |
53
+ | `fill` | Fill | Color | layer + adjustment | 3D | [Fill](#fill) |
54
+ | `copyColor` | Copy Color Profile | Color | layer + adjustment | | [Copy Color Profile](#copy-color-profile) |
55
+ | `curves` | Curves | Color | layer + adjustment | 3D | [Curves](#curves) |
56
+ | `colorama` | Colorama (Gradient Map) | Color | layer + adjustment | 3D | registry only |
57
+ | `posterize` | Posterize | Color | layer + adjustment | | [Posterize](#posterize) |
58
+ | `doubleExposure` | Double Exposure | Color | layer + adjustment | PRO | [Double Exposure](#double-exposure) |
59
+ | `filmStock` | Film Stock | Color | layer + adjustment | PRO | [Film Stock](#film-stock) |
60
+ | `alphaChoker` | Alpha Choker + Feather | Matte | layer only | | registry only |
61
+ | `gooey` | Gooey (liquid merge) | Matte | layer + adjustment | | [Gooey](#gooey) |
62
+ | `extract` | Extract (Histogram Key) | Matte | layer + adjustment | | [Extract](#extract) |
63
+ | `halftone` | Halftone / Engraving | Print | layer + adjustment | 3D | registry only |
64
+ | `ascii` | ASCII | Print | layer + adjustment | 3D | [ASCII](#ascii) |
65
+ | `crtMatrix` | CRT Pixel Matrix | Display | layer + adjustment | 3D | registry only |
66
+ | `pixelate` | Mosaic | Display | layer + adjustment | 3D | [Mosaic](#mosaic) |
67
+ | `shapeRulers` | Shape Rulers | Graphic | layer only | 3D | [Shape Rulers](#shape-rulers) |
68
+ | `longShadow` | Long Shadow / Faux Extrude | Graphic | layer only | | [Long Shadow](#long-shadow) |
69
+ | `outlineBands` | Animated Outline Bands | Graphic | layer + adjustment | | [Outline Bands](#outline-bands) |
70
+ | `edgeDetect` | Edge Detect | Graphic | layer + adjustment | 3D | [Edge Detect](#edge-detect) |
71
+ | `gridLines` | Grid | Graphic | layer + adjustment | | [Grid](#grid) |
72
+ | `strip` | Strip | Graphic | layer + adjustment | | [Strip](#strip) |
73
+ | `fractalNoise` | Fractal Noise | Pattern | layer + adjustment | 3D | [Fractal Noise](#fractal-noise) |
74
+ | `kaleidoscope` | Kaleidoscope / Polar Repeat | Pattern | layer + adjustment | | registry only |
75
+ | `fisheye` | Fisheye | Lens | layer + adjustment | | registry only |
76
+ | `opticsCompensation` | Optics Compensation | Lens | layer + adjustment | | [Optics Compensation](#optics-compensation) |
77
+ | `vignette` | Vignette | Lens | layer + adjustment | | registry only |
78
+ | `radialBlur` | Radial Blur | Lens | layer + adjustment | | registry only |
79
+ | `radialFastBlur` | Radial Fast Blur | Lens | layer + adjustment | | [Radial Fast Blur](#radial-fast-blur) |
80
+ | `sharpen` | Sharpen | Lens | layer + adjustment | | registry only |
81
+ | `fastBlur` | Fast Blur | Lens | layer + adjustment | | [Fast Blur](#fast-blur) |
82
+ | `lensPrism` | Lens Prism | Optical | layer + adjustment | avoid | [Lens Prism](#lens-prism) |
83
+ | `sdfLensBlur` | SDF Lens Blur | Optical | layer + adjustment | avoid | [SDF Lens Blur](#sdf-lens-blur) |
84
+ | `echo` | Echo | Motion | layer + adjustment | | [Echo](#echo) |
85
+ | `timeshift` | TimeShift | Motion | layer + adjustment | | [TimeShift](#timeshift) |
86
+ | `fastCameraWiggle` | Fast Camera Wiggle | Motion | layer + adjustment | | [Fast Camera Wiggle](#fast-camera-wiggle) |
87
+ | `rgbSplit` | RGB Split | Glitch | layer + adjustment | 3D | registry only |
88
+ | `glitch` | Digital Glitch | Glitch | layer + adjustment | 3D | registry only |
89
+ | `scanlines` | Scanlines | Glitch | layer + adjustment | | registry only |
90
+ | `grain` | Film Grain | Glitch | layer + adjustment | avoid | registry only |
91
+ | `flashBlink` | Flash Blink | Glitch | layer + adjustment | 3D | registry only |
92
+ | `vhs` | VHS | Glitch | layer + adjustment | | registry only |
93
+ | `pixelStretch` | Pixel Stretch | Glitch | layer + adjustment | 3D | [Pixel Stretch](#pixel-stretch) |
94
+ | `mirror` | Mirror | Distortion | layer + adjustment | | registry only |
95
+ | `waveWarp` | Wave Warp | Distortion | layer + adjustment | | registry only |
96
+ | `warpStyles` | Warp Styles | Distortion | layer + adjustment | | [Warp Styles](#warp-styles) |
97
+ | `shockwave` | Shockwave | Distortion | layer + adjustment | on request | [Shockwave only on request](#shockwave-only-on-request) |
98
+ | `waveLines` | Wave Lines | Distortion | layer + adjustment | | registry only |
99
+ | `waveRipple` | Wave Ripple | Distortion | layer + adjustment | | registry only |
100
+ | `turbulentDisplace` | Turbulent Displace | Distortion | layer + adjustment | | registry only |
101
+ | `vectorFieldWarp` | Vector Field Warp | Distortion | layer + adjustment | | registry only |
102
+ | `objectDither` | Dither Print | Object | layer only | 3D | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
103
+ | `objectIsolines` | Field Isolines | Object | layer only | | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
104
+ | `objectWindow` | Portrait Window | Object | layer only | | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
105
+ | `objectSlitScan` | Slit Scan | Object | layer only | 3D | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
106
+ | `objectCurtain` | Spectral Curtain | Object | layer only | 3D | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
107
+ | `objectGrid` | Grid System | Object | layer only | | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
108
+ | `objectContour` | Contour Studio | Object | layer only | 3D | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
109
+ | `objectNodes` | Node Field | Object | layer only | | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
110
+ | `objectLinks` | Link Graph | Object | layer only | | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
111
+ | `objectFacets` | Facet Plates | Object | layer only | | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
112
+ | `objectMeasure` | Measure Rig | Object | layer only | | [Object effects (subject-tracked)](#object-effects-subject-tracked) |
113
+ | `custom` | Custom GLSL | — | layer + adjustment | classic renderer only | [Custom Overlay Effect (GLSL)](#custom-overlay-effect-glsl) |
114
+ | `local:<id>` | Installed effect | — | per package | | [Installed effects](#installed-effects-localid) |
115
+
116
+ ### Layer Effects index
117
+
118
+ "Not model3d" means the effect is skipped on 3D model layers. The depth rows are wider than capabilities reports; see [effects-depth.md](#greenlight-dash--depth-effects-and-real-3d-on-models).
119
+
120
+ | `id` | What it does | Works on | PRO | Section |
121
+ |---|---|---|---|---|
122
+ | `runningGlow` | Running light that traces the layer's outline | video, image, text, shape, motion, sequence, grid, model3d | PRO | [Running Glow entry](#running-glow-entry) |
123
+ | `motionBlur` | Smears the layer along its own transform motion | video, image, text, shape, motion, sequence, grid; not model3d | | [Motion Blur entry](#motion-blur-entry) |
124
+ | `motionTrail` | The layer drawn again at its past poses or past frames | video, image, text, shape, motion, sequence, grid; not model3d or camera-baked 3D plates | PRO | [Motion Trail entry](#motion-trail-entry) |
125
+ | `posterizeTime` | The layer's clock steps at `ptFps` (stop-motion look) | video, image, text, shape, motion, sequence, grid, model3d, particles | | [Posterize Time entry](#posterize-time-entry) |
126
+ | `autoFill` | Grow Fill: geodesic "infection" fill through the alpha | video, image, text, shape, motion, sequence, grid, model3d (real 3D) | PRO | [Grow Fill entry](#grow-fill-entry) |
127
+ | `rig` | Rig (Puppet Pin) mesh warp with pins and IK limbs | video, image, text, shape, motion | PRO | [Rig (Puppet) entry](#rig-puppet-entry) |
128
+ | `repetile` | Repeats the layer into a staggered grid or layout of tiles | video, image, text, shape, motion, sequence, grid; not model3d or camera-baked 3D plates | | [Repetile entry](#repetile-entry) |
129
+ | `glass` | The shape becomes a glass pane over the layers behind it | `rectangle` / `ellipse` / `circle` shapes | | [Glass entry](#glass-entry) |
130
+ | `chromaKey` | Keys out a backdrop of any colour | video, image, sequence, grid (camera-baked 3D plates too) | | [Chroma Key entry](#chroma-key-entry) |
131
+ | `mockupFrame` | Draws a device chassis around the layer | video, image, text, shape, motion, sequence, grid; not model3d or camera-baked 3D plates | | [Mockup Frame entry](#mockup-frame-entry) |
132
+ | `regionDistort` | Rescales only what is inside a band | video, image, text, shape, motion, sequence, grid; not model3d or camera-baked 3D plates | | [Region Distort entry](#region-distort-entry) |
133
+ | `bend` | AE's CC Bend It: rigid ends, an arc between two handles | as `regionDistort` | | [Bend entry](#bend-entry) |
134
+ | `cornerPin` | The layer box mapped onto any quad (perspective) | as `regionDistort` | | [Corner Pin entry](#corner-pin-entry) |
135
+ | `meshWarp` | Catmull-Rom surface over a lattice of up to 5×5 points | as `regionDistort` | | [Mesh Warp entry](#mesh-warp-entry) |
136
+ | `bezierWarp` | Four cubic edges driving a Coons patch | as `regionDistort` | | [Bezier Warp entry](#bezier-warp-entry) |
137
+ | `droste` | The layer recursed into itself (Escher's Print Gallery) | as `regionDistort` | PRO | [Droste entry](#droste-entry) |
138
+ | `pryzm` | The layer seen through fluted / rippled / diamond glass | as `regionDistort` | | [Pryzm entry](#pryzm-entry) |
139
+ | `depthMap` | Build Depth Map: the shared depth source; renders nothing | image, video | | [Build Depth Map entry](#build-depth-map-entry) |
140
+ | `depth3d` | 2.5D parallax from the project camera | image, video | | [3D Depth entry](#3d-depth-entry) |
141
+ | `depthScan` | LiDAR-style slice through depth | image, video, model3d | | [Depth Scan entry](#depth-scan-entry) |
142
+ | `fresnelRim` | Rim light that hugs the subject's edges | image, video, model3d | | [Fresnel Rim entry](#fresnel-rim-entry) |
143
+ | `layerMask` | Animatable vector masks; on an adjustment item they scope the grade | video, image, text, shape, motion, adjustment | | [Layer Mask entry](#layer-mask-entry) |
144
+
51
145
  ## Preferred and avoided effects
52
146
 
53
147
  When the user has not named an effect, choose from these first:
54
148
 
55
- - Overlay Effects (`effects[].id`): `ascii`, `bloom`, `colorama`, `crtMatrix`, `doubleExposure`, `echo`, `edgeDetect`, `extract`, `fastBlur`, `fastCameraWiggle`, `fill`, `filmStock`, `gradientBeam`, `gradientSweep`, `halftone`, `lightRays`, `pixelate`, `radialFastBlur`, `rgbSplit`, `shapeRulers`, `timeshift`.
56
- - Layer Effects (`layerEffects[]`): `runningGlow`, `motionBlur`, `motionTrail`, `autoFill`, `repetile`, `regionDistort`, `cornerPin`, `rig`, `droste`, `glass`, `chromaKey`, `layerMask`.
149
+ - Overlay Effects (`effects[].id`): `ascii`, `bloom`, `colorama`, `crtMatrix`, `doubleExposure` (PRO at render), `echo`, `edgeDetect`, `extract`, `fastBlur`, `fastCameraWiggle`, `fill`, `filmStock` (PRO at render), `gradientBeam`, `gradientGlow` (PRO at render), `gradientSweep` (PRO at render), `halftone`, `lightRays`, `pixelate`, `radialFastBlur`, `rgbSplit`, `shapeRulers`, `timeshift`.
150
+ - Layer Effects (`layerEffects[]`): `runningGlow` (PRO at render), `motionBlur`, `motionTrail` (PRO at render), `autoFill` (PRO at render), `repetile`, `regionDistort`, `cornerPin`, `rig` (PRO at render), `droste` (PRO at render), `glass`, `chromaKey`, `layerMask`.
151
+ - **Inside a sequence** (a layer on a sequence's own timeline), every layer effect runs exactly as at the top level: Running Glow, Motion Trail (all three methods), Motion Blur (both modes), Layer Mask, Glass, a shape's `backdropBlur` field, Depth Scan, Fresnel Rim, Chroma Key, Grow Fill (`autoFill`), Rig, Repetile, Mockup Frame, Region Distort, Corner Pin, Droste, Pryzm, Bend and Mesh / Bezier Warp, plus Overlay Effects, blend modes and track mattes. The sequence's own camera clip defocuses all of them. In a grid cell, Motion Blur and Motion Trail are skipped: the cell layout replaces the layer's own motion.
57
152
 
58
153
  Leave these out unless the user asks for them: `grain` (Film Grain), `lensPrism`, `linesDraw`, `sdfLensBlur`.
59
154
 
60
- A preferred effect still has to suit the shot: use one only when the shot calls for it, never because it is on this list. The rules below (Shockwave only on request, no turbulence in an intro) still apply.
155
+ A preferred effect still has to suit the shot: use one only when the shot calls for it, never because it is on this list. The two sections below still apply.
61
156
 
62
157
  ## Shockwave only on request
63
158
 
@@ -65,7 +160,7 @@ Do not add the `shockwave` Overlay Effect (an `effects[]` entry with `id: "shock
65
160
 
66
161
  ## Effects in an intro
67
162
 
68
- Keep Shockwave and turbulence distortions out of intro animations (the opening title, a logo reveal, the first shot of a film). That means the `shockwave` Overlay Effect, `turbulentDisplace`, `vectorFieldWarp` with `turbulence` above 0, and particle systems built on the `shockwave` preset, a `burst` force emitter or a strong `forces.turbulence`. The intro is where the viewer reads the name and forms a first impression. A ripple or noise warp smears those letters while they are being read, and viewers know it as a stock template move, so the opening looks generic instead of designed. Build the entrance from clean motion: layer transitions (`transitionInType`, see [video-transitions.md](video-transitions.md)), eased position/scale/opacity keyframes, masks and wipes, blur-to-sharp, text reveal presets. Later in a film these effects are fine when the user asks for them or the content calls for one (heat haze, water, an impact on a beat); the `shockwave` Overlay Effect still waits for the user to ask (see [Shockwave only on request](#shockwave-only-on-request)).
163
+ Keep the `shockwave` effect and turbulence distortions out of intro animations (the opening title, a logo reveal, the first shot of a film). Turbulence means `turbulentDisplace`, `vectorFieldWarp` with `turbulence` above 0, and particle systems built on the `shockwave` preset, a `burst` force emitter or a strong `forces.turbulence`. The intro is where the viewer reads the name and forms a first impression. A ripple or noise warp smears those letters while they are being read, and viewers know it as a stock template move, so the opening looks generic instead of designed. Build the entrance from clean motion: layer transitions (`transitionInType`, see [video-transitions.md](video-transitions.md)), eased position/scale/opacity keyframes, masks and wipes, blur-to-sharp, text reveal presets. Later in a film turbulence is fine when the user asks for it or the content calls for it (heat haze, water, an impact on a beat).
69
164
 
70
165
  ## One effect for many small layers
71
166
 
@@ -100,12 +195,12 @@ One glow over the composite reads slightly differently from many. Per-layer glow
100
195
 
101
196
  ## Storage contract
102
197
 
103
- The product has two distinct effect sections. Do not infer the field from the selected clip type:
198
+ The product has two distinct effect families. Do not infer the field from the clip type:
104
199
 
105
- | Product panel | Item field | Items | Meaning |
200
+ | Family | Item field | Items | Meaning |
106
201
  |---|---|---|---|
107
202
  | **Overlay Effects** | `effects` | regular visual items **and** adjustment items | ordered GPU-effect chain |
108
- | **Layer Effects** | `layerEffects` | regular visual items (+ `layerMask` on adjustment items) | typed structural effects: Running Glow, Motion Blur, Grow Fill, Rig (Puppet), Repetile, Region Distort, Bend, Corner Pin, Droste, Chroma Key, Layer Mask, plus the Depth trio (`depthMap` / `depth3d` / `depthScan`, authored from the DEPTH EFFECTS section) |
203
+ | **Layer Effects** | `layerEffects` | regular visual items (+ `layerMask` on adjustment items) | typed per-layer effects: see [Layer Effects](#layer-effects-itemlayereffects) |
109
204
 
110
205
  `effects` uses the same entry schema everywhere. The item type determines the input:
111
206
 
@@ -113,9 +208,9 @@ The product has two distinct effect sections. Do not infer the field from the se
113
208
  - On a regular visual item with a transition, the render order is **source layer → Overlay Effects → transition → composite**. Outgoing and incoming clips are effected independently before the transition combines or moves them.
114
209
  - On an adjustment item, Overlay Effects process the composited channels below the adjustment channel. The coordinate space is the full frame.
115
210
 
116
- `layerEffects` is **not** the Overlay Effects registry. It contains the Layer Effects panel entries described later in this file.
211
+ `layerEffects` is **not** the Overlay Effects registry. It holds the Layer Effects entries; see [Layer Effects](#layer-effects-itemlayereffects).
117
212
 
118
- Do not write the obsolete direct fields `glowEnabled`, `glow*`, `depthEnabled`, `depth*`, or `depthMap`. They are not loaded. Running Glow and 3D Depth data belongs inside `layerEffects` entries. Neon Glow (`neonGlow`) is an OVERLAY effect (`item.effects`); a legacy `layerEffects` entry for it migrates automatically on load.
213
+ Do not write the direct fields `glowEnabled`, `glow*`, `depthEnabled`, `depth*`, or `depthMap`. They are not loaded. Running Glow and 3D Depth data belongs inside `layerEffects` entries. Neon Glow (`neonGlow`) is an OVERLAY effect (`item.effects`); a `layerEffects` entry for it migrates automatically on load.
119
214
 
120
215
  ## Overlay Effects (`item.effects`)
121
216
 
@@ -125,7 +220,7 @@ The chain:
125
220
  - runs in array order;
126
221
  - renders in preview, WebCodecs export, sequences, and `/render-onserver`;
127
222
  - supports stable instance ids, Mix, parameters, active ranges, custom GLSL, and keyframes;
128
- - drops unknown effect ids on load.
223
+ - drops ids that are neither in the registry, nor `custom`, nor `local:<id>` on load.
129
224
 
130
225
  ### Overlay Effect entry
131
226
 
@@ -152,11 +247,11 @@ The chain:
152
247
  - `mix` is 0–100; 0 is an exact bypass.
153
248
  - `params` contains effect-specific values. Preserve keys you are not intentionally changing.
154
249
  - Optional `start` / `end` are clip-local seconds. Omit both for the whole clip.
155
- - Optional `clip` is meaningful when `effects` is on a regular layer: true cuts output to the layer silhouette, false allows bloom/blur/displacement outside it. Omit it for the registry default.
250
+ - Optional `clip` matters only on a regular layer: `true` cuts the output to the layer's silhouette, `false` lets glow, blur, echoes or displacement spill past it. **Omit it.** Each effect's `defaultClip` in capabilities is already right: effects that light or smear outward default to `false`, and self-clipping effects (Fill, Posterize, Copy Color Profile) would multiply semi-transparent edges by their alpha a second time if you set `true`. Set it only to get the opposite on purpose; the [effect notes](#greenlight-dash--overlay-effects-reference) mention `clip` only where that is worth doing.
156
251
  - `code` is accepted only for `id: "custom"` and is capped at 8192 characters.
157
- - Optional `real3d: true` — on a `model3d` item only: the effect renders INSIDE the model's 3D pass instead of on its flat picture (see **Real 3D on 3D model layers** below). Write it only when true; an entry without it keeps the flat version, which is how every project saved before it still looks.
252
+ - Optional `real3d: true` — on a `model3d` item only: the effect renders INSIDE the model's 3D pass instead of on its flat picture (see [Real 3D on 3D model layers](#real-3d-on-3d-model-layers)). Write it only when true; an entry without it renders the flat version.
158
253
 
159
- Numeric params are clamped to registry ranges. Colors use CSS color strings, selects use numeric option values, `type: "easing"` params use one of their `options` easing names (an unknown name falls back to the default), gradients use CSS gradient strings, and the Curves effect's `curves` param is a four-channel point-list object (see Curves below).
254
+ Numeric params are clamped to registry ranges. Colors use CSS color strings, selects use numeric option values, `type: "easing"` params use one of their `options` easing names (an unknown name falls back to the default), gradients use CSS gradient strings, and the Curves effect's `curves` param is a four-channel point-list object (see [Curves](#curves)).
160
255
 
161
256
  ### Current effect registry
162
257
 
@@ -170,22 +265,271 @@ GET /api/video-editor/capabilities
170
265
  - `effects.layer[]` contains every typed Layer Effect with defaults, fields, supported media types, requirements, and PRO status.
171
266
  - `effects.limits.overlayEffectsPerItem` is the current chain limit.
172
267
 
173
- Use `params: {}` for registry defaults, or start from `defaultParams` and change only requested values. Never invent an id, parameter key, select value, range, scope, or keyframe capability from this document; the runtime-generated endpoint is authoritative.
268
+ Use `params: {}` for registry defaults, or start from `defaultParams` and change only requested values. Never invent an id, parameter key, select value, range, scope, or keyframe capability from this document; the runtime-generated endpoint is authoritative. One exception: it under-reports the Depth effects. Video layers are supported too, and Depth Scan and Fresnel Rim also run on `model3d`; `scnSpace` is a valid Depth Scan field (see [effects-depth.md](#greenlight-dash--depth-effects-and-real-3d-on-models)).
269
+
270
+ Glass is a Layer Effect on a shape, not an Overlay Effect: see [Glass entry](#glass-entry). What each Overlay Effect's values do is in [effects-overlay.md](#greenlight-dash--overlay-effects-reference).
271
+
272
+ ### Regular-layer Overlay Effects example
273
+
274
+ ```json
275
+ {
276
+ "id": "title-item",
277
+ "clipId": "title-clip",
278
+ "trimStart": 0,
279
+ "trimEnd": 4,
280
+ "effects": [
281
+ {
282
+ "id": "halftone",
283
+ "eid": "title-halftone",
284
+ "enabled": true,
285
+ "mix": 70,
286
+ "params": {},
287
+ "clip": true
288
+ },
289
+ {
290
+ "id": "glow",
291
+ "eid": "title-glow",
292
+ "enabled": true,
293
+ "mix": 85,
294
+ "params": { "glowRadius": 240, "exposure": 1.2, "glowThreshold": 50, "tintAmount": 100, "tintInner": "#69e7ff", "tintOuter": "#69e7ff" },
295
+ "clip": false
296
+ }
297
+ ]
298
+ }
299
+ ```
300
+
301
+ ### Adjustment-layer Overlay Effects example
302
+
303
+ An adjustment clip is synthetic. Put its channel after the channels it should affect, but before the camera channel:
304
+
305
+ ```json
306
+ {
307
+ "clips": [
308
+ { "id": "adj-clip", "source": "LOCAL", "mediaType": "adjustment", "url": "", "proxyUrl": null, "name": "Look", "duration": 6 }
309
+ ],
310
+ "channels": [
311
+ { "id": "footage", "name": "Footage", "visible": true, "items": ["..."] },
312
+ {
313
+ "id": "look-channel",
314
+ "name": "Look",
315
+ "visible": true,
316
+ "unsnapped": true,
317
+ "items": [{
318
+ "id": "look-item",
319
+ "clipId": "adj-clip",
320
+ "freeStart": 0,
321
+ "trimStart": 0,
322
+ "trimEnd": 6,
323
+ "effects": [
324
+ { "id": "vignette", "eid": "look-vignette", "enabled": true, "mix": 65, "params": {} },
325
+ { "id": "bloom", "eid": "look-bloom", "enabled": true, "mix": 100, "params": { "threshold": 0.7 } }
326
+ ]
327
+ }]
328
+ }
329
+ ]
330
+ }
331
+ ```
332
+
333
+ Channel order defines adjustment reach. `cameraLocked` changes camera behavior, not effect reach.
334
+
335
+ ### Overlay Effect keyframes
336
+
337
+ Overlay Effects use `fx:<eid>:<parameter>` tracks:
338
+
339
+ ```json
340
+ {
341
+ "propertyKeyframes": {
342
+ "fx:title-glow:mix": [
343
+ { "position": 0, "value": "0", "easing": "AE Smooth" },
344
+ { "position": 25, "value": "100", "easing": "AE Smooth" }
345
+ ],
346
+ "fx:title-glow:glowRadius": [
347
+ { "position": 0, "value": "80", "easing": "Smooth" },
348
+ { "position": 100, "value": "440", "easing": "Linear" }
349
+ ]
350
+ }
351
+ }
352
+ ```
353
+
354
+ `mix` and every number param whose capabilities entry says `keyframable: true` take an `fx:<eid>:<key>` track; colours, gradients, selects and static numbers (rates, seeds, capacities, sample counts) do not. Remove an effect's `fx:<eid>:*` tracks when removing the effect.
355
+
356
+ ### Installed effects (`local:<id>`)
357
+
358
+ Effect packages an agent built or the user imported (custom-effects skill (GreenLight Dash only))
359
+ apply exactly like the registry effects above, in the same `effects` list and both scopes:
360
+
361
+ ```json
362
+ { "id": "local:chromatic-drift", "eid": "fx-drift-1", "enabled": true, "mix": 100,
363
+ "params": { "amount": 6, "angle": 0, "speed": 0.5, "tint": "#ffffff", "tintMix": 0 } }
364
+ ```
365
+
366
+ - Discover them in `GET /api/video-editor/capabilities` → `effects.local`: each entry carries its
367
+ params (keys, ranges, defaults, which are keyframable), `scopes`, the `renderer` it was written
368
+ for and `installation.status`. Number params animate with `fx:<eid>:<param>` tracks, like any
369
+ effect.
370
+ - Use one whose `installation.status.state` is `ok`. `error` means it does not draw (the message
371
+ says why); `unchecked` means no open editor has checked it yet.
372
+ - An installed effect draws on the engines it was written for: `renderer` is `webgl`, `webgpu` or
373
+ `both`, against `GET /api/video-editor/engine`. On another engine it is skipped.
374
+ - An entry whose package is not installed on this machine is kept in the project as stored and
375
+ skipped, so a project moves between machines without losing it; installing the package (or a
376
+ bundle restore or web sync that carries it) brings it back. A plain board ZIP import installs no
377
+ packages.
378
+
379
+ ### Custom Overlay Effect (GLSL)
380
+
381
+ ```json
382
+ {
383
+ "id": "custom",
384
+ "eid": "custom-wave",
385
+ "enabled": true,
386
+ "mix": 100,
387
+ "params": {},
388
+ "code": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n uv.x += sin(uv.y * 40.0 + iTime * 3.0) * 0.01;\n fragColor = texture2D(iChannel0, uv);\n}"
389
+ }
390
+ ```
391
+
392
+ - `code` is **GLSL**, Shadertoy-shaped: define `void mainImage(out vec4 fragColor, in vec2 fragCoord)`.
393
+ The framework wraps it, seeds `fragColor` with the source pixel and applies Mix afterwards, so do
394
+ not blend against the source yourself. The shader is compiled when the effect is created, and a
395
+ body with no `mainImage` is rejected there.
396
+ - **Classic renderer only.** The body is compiled at runtime and no WebGPU renderer compiles GLSL, so
397
+ when the app's engine is WebGPU (`GET /api/video-editor/engine`) the effect is skipped with a reason
398
+ rather than drawn.
399
+ - Do not add `custom` entries; the app does not offer them. Use a registry effect or an
400
+ [installed effect](#installed-effects-localid). This section is the contract for the `custom` entries
401
+ a project may carry.
402
+ - Shadertoy names in scope: `iChannel0` (the isolated layer, or the below-stack composite on an
403
+ adjustment layer), `iResolution` (vec3), `iTime`, `iProgress`, plus `iTimeDelta`, `iFrameRate`,
404
+ `iFrame`, `iMouse` (always zero), `iDate`, `iChannelResolution[4]` and `iChannelTime[4]` for
405
+ compatibility. There is no `iChannel1..3` — there is only one bound texture.
406
+ - The app's own uniforms, same values: `uTexture`, `uResolution` (vec2), `uTime`, `uProgress`, `uMix`,
407
+ `uQuality`, `uParams[28]`, `uColor`, `uColors[5]`.
408
+ - `fxLocal(uv)`, `fxFrame(p)`, `fxDir(v)`, `fxRes()`, `fxAspect()`, `fxCoord(uv)` — geometry in the
409
+ MOVING layer box. Without these an effect centres itself on the frame, so it stands still while the
410
+ shape slides through it. On adjustment layers they reduce to frame space.
411
+ - Redeclaring any of those names in your own code is safe — the duplicate declaration is dropped from
412
+ the prelude, which is what lets a shader pasted from elsewhere compile unchanged.
413
+ - `iTime`/`uTime` is clip-local seconds and restarts at 0 inside an active range (`start`/`end`).
414
+ - Keep output premultiplied-alpha safe near transparent edges.
415
+
416
+ ## Layer Effects (`item.layerEffects`)
417
+
418
+ Layer Effects are an array of typed entries in `item.layerEffects`. The [Layer Effects index](#layer-effects-index) lists every id, the layers it works on, PRO status and its page.
419
+
420
+ - At most one entry of each id. Each entry has a stable `eid`, an `enabled` flag and a `params` object.
421
+ - Some entries carry data next to `params`, never inside it: `points` / `points3d` (Grow Fill), `pins` / `chains` (Rig), `masks` (Layer Mask), `depthMap` (Build Depth Map).
422
+ - Their numbers keyframe as same-name `propertyKeyframes` tracks (`glowAmount`, `depthStrength`, `repReveal`…), not `fx:` tracks; Rig uses `rig:<pid>:<chan>` and Layer Mask `msk:<mid>:<chan>`. Each entry's section lists which fields keyframe.
423
+ - The depth effects (`depthMap`, `depth3d`, `depthScan`, `fresnelRim`) share one depth map; see [effects-depth.md](#greenlight-dash--depth-effects-and-real-3d-on-models).
424
+
425
+ ### Complete regular-item example
426
+
427
+ ```json
428
+ {
429
+ "id": "photo-item",
430
+ "clipId": "photo-clip",
431
+ "effects": [
432
+ { "id": "vignette", "eid": "photo-vignette", "enabled": true, "mix": 25, "params": {} }
433
+ ],
434
+ "layerEffects": [
435
+ {
436
+ "id": "runningGlow",
437
+ "eid": "photo-runner",
438
+ "enabled": true,
439
+ "params": { "glowAmount": 80, "glowMaskMode": 1 }
440
+ },
441
+ {
442
+ "id": "depthMap",
443
+ "eid": "photo-depthmap",
444
+ "enabled": true,
445
+ "params": { "dmInvert": 0 },
446
+ "depthMap": { "w": 384, "h": 216, "data": "data:image/png;base64,..." }
447
+ },
448
+ {
449
+ "id": "depth3d",
450
+ "eid": "photo-depth",
451
+ "enabled": true,
452
+ "params": { "depthStrength": 120, "depthFocus": 45 }
453
+ },
454
+ {
455
+ "id": "depthScan",
456
+ "eid": "photo-scan",
457
+ "enabled": true,
458
+ "params": { "scnColorMode": 2, "scnWidth": 1.4, "scnSplit": 3, "scnBrightness": 140 }
459
+ }
460
+ ]
461
+ }
462
+ ```
463
+
464
+ ## Render and editing rules
465
+
466
+ Follow the versioned-write contract in [video-projects.md](video-projects.md#update-video-project): `GET` the project right before the edit, keep unrelated fields, `PUT` the whole `data` with `expected_version`, and on 409 reload and reapply only your edit. For effects also:
174
467
 
175
- Glass is not an Overlay Effect: an Overlay Effect only restyles its own layer, while glass has to bend the layers behind it. It is the `glass` Layer Effect on a shape — see [Glass entry](#glass-entry).
468
+ 1. Write Overlay Effects only to `effects` and Layer Effects only to `layerEffects`. On an image or video layer a Depth effect without a `depthMap` entry renders nothing, so add the source alongside it.
469
+ 2. Keep every `eid` stable across edits and reorders; keyframe tracks reference it.
470
+ 3. When you delete an effect, delete its tracks too (`fx:<eid>:*`, or its same-name, `rig:` or `msk:` tracks).
471
+ 4. Render with `/render-onserver` or the desktop renderer, never the fast `/render` (see rendering.md (GreenLight Dash only)).
472
+
473
+ # GreenLight Dash — Overlay Effects reference
474
+
475
+ > Part of the `greenlight-dash` skill. Read [video-effects.md](#greenlight-dash--video-editor-layer-effects-and-overlay-effects) first: the entry schema, the `clip` rule, `fx:` keyframes and the id index live there.
476
+
477
+ ## Contents
176
478
 
177
- #### Gradient Motion Beam type
479
+ - [Gradient Motion Beam type](#gradient-motion-beam-type)
480
+ - [Gradient Sweep](#gradient-sweep)
481
+ - [Gradient Glow](#gradient-glow)
482
+ - [Fractal Noise](#fractal-noise)
483
+ - [Deep Glow (`glow`)](#deep-glow-glow)
484
+ - [Bloom (`bloom`)](#bloom-bloom)
485
+ - [Echo](#echo)
486
+ - [TimeShift](#timeshift)
487
+ - [Fill](#fill)
488
+ - [Shape Rulers](#shape-rulers)
489
+ - [Copy Color Profile](#copy-color-profile)
490
+ - [Double Exposure](#double-exposure)
491
+ - [Film Stock](#film-stock)
492
+ - [Fast Blur](#fast-blur)
493
+ - [Radial Fast Blur](#radial-fast-blur)
494
+ - [Grid](#grid)
495
+ - [Strip](#strip)
496
+ - [ASCII](#ascii)
497
+ - [Mosaic](#mosaic)
498
+ - [Posterize](#posterize)
499
+ - [Light Sweep](#light-sweep)
500
+ - [Light Rays](#light-rays)
501
+ - [Long Shadow](#long-shadow)
502
+ - [Outline Bands](#outline-bands)
503
+ - [Lens Prism](#lens-prism)
504
+ - [Pixel Stretch](#pixel-stretch)
505
+ - [Edge Detect](#edge-detect)
506
+ - [Fast Camera Wiggle](#fast-camera-wiggle)
507
+ - [SDF Lens Blur](#sdf-lens-blur)
508
+ - [Optics Compensation](#optics-compensation)
509
+ - [Warp Styles](#warp-styles)
510
+ - [Curves](#curves)
511
+ - [Extract](#extract)
512
+ - [Neon Glow](#neon-glow)
513
+ - [Lines Draw](#lines-draw)
514
+ - [Gooey](#gooey)
515
+ - [Object effects (subject-tracked)](#object-effects-subject-tracked)
516
+
517
+ Effect-by-effect notes for the Overlay Effects chain (`item.effects`). Ranges, defaults and option lists come from `GET /api/video-editor/capabilities`; these notes explain what the values do and how to combine them. Effects not listed here are documented by the registry alone (see the [Overlay Effects index](#overlay-effects-index)).
518
+
519
+ About `clip`: omit it, as [Overlay Effect entry](#overlay-effect-entry) explains; a section mentions `clip` only where setting it changes something you may want.
520
+
521
+ ## Gradient Motion Beam type
178
522
 
179
523
  `gradientBeam.params.type` selects how the existing effect travels:
180
524
 
181
525
  - `0` — **Directional** (default): the original edge-to-edge sweep. `angle` controls its direction.
182
526
  - `1` — **From Center**: an expanding radial beam. Its origin is the full view center on an adjustment item and the layer's own center on a regular item; `angle` is ignored.
183
527
 
184
- Existing entries that omit `type` remain Directional. Both types share the same palette, speed, blur, width, hold, intensity, blend mode, and frost parameters.
528
+ Omitting `type` means Directional. Both types share the same palette, speed, blur, width, hold, opacity, blend mode, and frost parameters.
185
529
 
186
- `gradientBeam.params.layerMask` (0–100 %, default `100`, keyframable) shows the original layer outside the beam. At `100` the layer is untouched (the behaviour of every entry that omits it). At `0` the beam is the layer's mask: the layer is visible only where the beam passes, so a sweep becomes a travelling reveal. Coverage follows the beam's shape (`width`, `blur`, `hold`, `speed`), not `opacity` — an `opacity` of `0` with `layerMask: 0` is a pure reveal with no colour cast. Leave `clip` unset (the effect clips to the layer's shape by default) so the mask follows the silhouette. Overlay `mix` still crossfades the whole effect, mask included.
530
+ `gradientBeam.params.layerMask` (0–100 %, default `100`, keyframable) shows the original layer outside the beam. At `100` (the default) the layer is untouched. At `0` the beam is the layer's mask: the layer is visible only where the beam passes, so a sweep becomes a travelling reveal. Coverage follows the beam's shape (`width`, `blur`, `hold`, `speed`), not `opacity` — an `opacity` of `0` with `layerMask: 0` is a pure reveal with no colour cast. The effect clips to the layer's shape by default, so the mask follows the silhouette. Overlay `mix` still crossfades the whole effect, mask included.
187
531
 
188
- #### Gradient Sweep
532
+ ## Gradient Sweep
189
533
 
190
534
  `gradientSweep` (**Gradient Sweep**, group Gradient, both scopes, **PRO** — free to preview, gated at render like Neon Glow) sweeps one light band with a 5-stop palette ramp across the layer's alpha — the Gradient Sweep Text motion template generalised to any layer. `params.mode` selects what the band does:
191
535
 
@@ -193,9 +537,20 @@ Existing entries that omit `type` remain Directional. Both types share the same
193
537
  - `1` — **Silhouette**: the text template's look on any alpha — band + faint `coolFill` interior + outline; the layer's own pixels are never shown, and everything fades out after the pass.
194
538
  - `2` — **Sheen**: the layer stays visible; the band crosses it as a glare (`blend`: `0` Add / `1` Screen / `2` Normal). `dimOutside` darkens the layer away from the band, easing on/off over `sheenFade` seconds, timed so full opacity returns exactly when the band lands.
195
539
 
196
- Timing is a pure function of the clip-local clock: the sweep starts at the clip start (or at the entry's Active Range `start`, which rebases the clock) and crosses in `duration` seconds with `easing` (`0` Sine / `1` Linear / `2` Smooth / `3` Ease Out). `loop: 1` repeats with `loopGap` seconds of rest. `angle` is −180…180° (direction and tilt in one), `bandWidth`/`coreWidth` size the ramp, `bandStrength` scales it, `glow`/`glowRadius` add bloom that spreads past the silhouette (the effect defaults to **not** clipping — leave `clip` unset). Palette: `colorCore`/`colorWarm`/`colorHot`/`colorCool`/`colorDeep`. On an adjustment item the band sweeps the full frame and Reveal uncovers the whole below-stack.
540
+ Timing is a pure function of the clip-local clock: the sweep starts at the clip start (or at the entry's Active Range `start`, which rebases the clock) and crosses in `duration` seconds with `easing` (`0` Sine / `1` Linear / `2` Smooth / `3` Ease Out). `loop: 1` repeats with `loopGap` seconds of rest. `angle` is −180…180° (direction and tilt in one), `bandWidth`/`coreWidth` size the ramp, `bandStrength` scales it, `glow`/`glowRadius` add bloom that spreads past the silhouette (the effect does not clip by default). Palette: `colorCore`/`colorWarm`/`colorHot`/`colorCool`/`colorDeep`. On an adjustment item the band sweeps the full frame and Reveal uncovers the whole below-stack.
197
541
 
198
- #### Fractal Noise
542
+ ## Gradient Glow
543
+
544
+ `gradientGlow` (**Gradient Glow**, group Gradient, both scopes, **PRO** — free to preview, gated at render like Neon Glow) recolours a layer with a flowing four-colour cycle and lights the area around it. A brightness ramp across the layer box (or the layer's own luminance), plus a white inner rim along the shape's edges, is mapped through the cyclic palette `color1`…`color4`; a wide blurred copy of the result is laid under it as a halo. The layer's position and scale are never touched, so it combines with any motion the item already has.
545
+
546
+ - Motion: each cycle sweeps the palette by `travel` degrees (−720…720, default 400) over `1.833 / speed` seconds (`speed` 0.05–3, default 0.6) with `easing` `0` Countdown (a fast burst that settles) / `1` Smooth / `2` Linear, then holds for `hold` seconds (0–6). `phase` (0–360°) offsets the palette. `travel` 0 freezes the colours — a keyframed `phase` then drives them by hand. The clock is the item's own (or the entry's Active Range), so the motion is identical in preview and export.
547
+ - Colour source: `colorBy` `0` Shape Ramp — the ramp runs at `angle` (a CSS angle: 0° up, 90° right, measured in the layer box) from `rampLow` to `rampHigh` % brightness — or `1` Layer Brightness, which suits photos and video. `colorBlend` `0` Linear (After Effects Colorama) / `1` Smooth (no crease where the gradient passes a palette stop).
548
+ - Edges: `rim` (0–200 %) and `rimWidth` (0.2–12 %), `edgeGlow` (0–100 %), and `softness` (0–8), which melts the colour band at the rim without blurring the shape's outline. Crisp (the default) is `softness` 0, `rim` 100; softer looks raise `softness` (1 / 2.5 / 5) with a wider, weaker rim (`rimWidth` 4 / 6.5 / 10, `rim` 85 / 70 / 55), `edgeGlow` 70–100 and `colorBlend` 1.
549
+ - Halo: `halo` (0–150 %, its opacity) and `haloSize` (4–60 %, its blur radius).
550
+
551
+ `rimWidth`, `softness` and `haloSize` are % of the layer box's shorter side, so the same values fit a full-frame number and a small caption. Edge glow, rim, softness and halo at 0 cost nothing. It writes outside the silhouette by design. Example (softer edges on the default colours and motion): `{"id": "gradientGlow", "mix": 100, "params": {"softness": 1, "rim": 85, "rimWidth": 4, "edgeGlow": 70, "colorBlend": 1}}`.
552
+
553
+ ## Fractal Noise
199
554
 
200
555
  `fractalNoise` (**Fractal Noise**, group Pattern, both scopes, free) is the After Effects Fractal Noise analogue: one looping noise field painted over the item — fog, smoke, cloud, light leaks, film wash and heat haze from one algorithm. A fresh entry IS the Drifting Fog preset (Basic, 4 octaves, floor 0.35, drift 0.25, Screen). `params.type` picks the fractal: `0` Basic (default; the plain rolling field) / `1` Turbulent (zero crossings fold into creases) / `2` Ridged (the fold inverted into bright filaments; `ridge` sharpens them). `colorShadow`/`colorLight` tint the field.
201
556
 
@@ -205,15 +560,17 @@ Motion: `churn` is whole turns of the evolution loop per clip (fractional values
205
560
 
206
561
  Composite: `blend` `0` Screen (default) / `1` Add / `2` Multiply / `3` Overlay / `4` Soft Light / `5` **Over (field = alpha)** — the field is a cloud body composited in front of the item, so dense cores occlude it. `maskLayer: 1` (Mask Layer by Noise) multiplies the item's alpha by the field: only the noise stays visible and the empty areas of the layer become transparent — combine with Over on a solid to make a free-floating cloud layer. Overlay `mix` is the opacity. The effect clips to the item's shape by default; set `clip: false` on an Over-blend entry to let the cloud spill past the silhouette.
207
562
 
208
- Presets (the panel's chips, all params + colours in one click): Drifting Fog, Rolling Smoke, Smoke Veins, Cloud Bed, Light Leak, Film Wash, Heat Veil.
563
+ Presets (each sets all params + colours): Drifting Fog, Rolling Smoke, Smoke Veins, Cloud Bed, Light Leak, Film Wash, Heat Veil.
209
564
 
210
- #### Bloom
565
+ ## Deep Glow (`glow`)
211
566
 
212
- `glow` (**Deep Glow**, group Glow, both scopes) is light with a physically shaped falloff: a bright core and an inverse-square tail, computed in linear light. `glowRadius` (10–2000 px at 1080p) is how far the light reaches; `exposure` (0–4) is how much of the source's light scatters (0 = the effect does nothing and costs nothing); `falloff` (1.3–3.5, `2` = inverse square) steepens or flattens the tail. What glows: `glowThreshold` (0–100 %) with `thresholdSmooth`, measured by `thresholdBy` `0` Brightness (any bright colour, saturated neon included) / `1` Luminance / `2` Everything (the whole layer glows; the usual choice on a transparent text or shape layer). Shape: `aspect` (0.25–16 ×, above 1 = wider) and `angle` (−90…90°, clockwise) make it an ellipse — `aspect` ~11 with a short `glowRadius` (~130) is an anamorphic streak. Colour: the light takes the source's colour; `tintAmount` (0–100 %) recolours it from `tintInner` at the core to `tintOuter` at the edge (`tintMode` `0` Colorize / `1` Multiply), so a glow in one colour is `tintAmount` 100 with both set to it; `saturation` (0–200 %). The keys `intensity`, `threshold`, `radius` and `color` are not read. `radiusRed` / `radiusGreen` / `radiusBlue` (0.3–2.5 ×) let one channel reach further than another (red wide, blue tight = a warm fringe) and `aberration` (0–100 %) splits it radially. Output: `tonemap` `2` Hot Core (default — the picture under the glow stays exact; over-bright colour turns white instead of clipping) / `0` Clamp / `1` Soft Clip / `3` Reinhard / `4` ACES Filmic / `5` Hable Filmic (the last three regrade the whole layer), `linearLight` `1`/`0`, `blend` `0` Add / `1` Screen, `dither` `1`/`0`, `quality` `1` Standard / `0` Draft (half-resolution core). It writes outside the silhouette by design; leave `clip` unset. Example: `{"id": "glow", "mix": 100, "params": {"glowRadius": 260, "exposure": 1.7, "glowThreshold": 35, "saturation": 125}}` (Neon Tube).
567
+ `glow` (**Deep Glow**, group Glow, both scopes) is light with a physically shaped falloff: a bright core and an inverse-square tail, computed in linear light. `glowRadius` (10–2000 px at 1080p) is how far the light reaches; `exposure` (0–4) is how much of the source's light scatters (0 = the effect does nothing and costs nothing); `falloff` (1.3–3.5, `2` = inverse square) steepens or flattens the tail. What glows: `glowThreshold` (0–100 %) with `thresholdSmooth`, measured by `thresholdBy` `0` Brightness (any bright colour, saturated neon included) / `1` Luminance / `2` Everything (the whole layer glows; the usual choice on a transparent text or shape layer). Shape: `aspect` (0.25–16 ×, above 1 = wider) and `angle` (−90…90°, clockwise) make it an ellipse — `aspect` ~11 with a short `glowRadius` (~130) is an anamorphic streak. Colour: the light takes the source's colour; `tintAmount` (0–100 %) recolours it from `tintInner` at the core to `tintOuter` at the edge (`tintMode` `0` Colorize / `1` Multiply), so a glow in one colour is `tintAmount` 100 with both set to it; `saturation` (0–200 %). The keys `intensity`, `threshold`, `radius` and `color` are not read. `radiusRed` / `radiusGreen` / `radiusBlue` (0.3–2.5 ×) let one channel reach further than another (red wide, blue tight = a warm fringe) and `aberration` (0–100 %) splits it radially. Output: `tonemap` `2` Hot Core (default — the picture under the glow stays exact; over-bright colour turns white instead of clipping) / `0` Clamp / `1` Soft Clip / `3` Reinhard / `4` ACES Filmic / `5` Hable Filmic (the last three regrade the whole layer), `linearLight` `1`/`0`, `blend` `0` Add / `1` Screen, `dither` `1`/`0`, `quality` `1` Standard / `0` Draft (half-resolution core). It writes outside the silhouette by design. Example: `{"id": "glow", "mix": 100, "params": {"glowRadius": 260, "exposure": 1.7, "glowThreshold": 35, "saturation": 125}}` (Neon Tube).
213
568
 
214
- `bloom` (**Bloom**, group Glow, both scopes) makes bright content leak light — a physically-shaped glow built on an octave pyramid rather than a blur kernel. `threshold` (0–1) with `knee` (soft shoulder) picks what counts as bright; `intensity` (0–600 %) scales the leak; `radius` (1–4) is **octaves of spread, not pixels** — 1 is a tight halo, 4 folds in a sixteenth-resolution octave whose halos span hundreds of px, and fractional values morph continuously. `saturation` desaturates or over-saturates the leaked light only, `tint` + `tintAmount` recolour it (the classic cold anamorphic look is a blue tint at ~60 %), and `blend` composites `0` Add / `1` Screen (protects already-bright frames from clipping) / `2` Lighten (max — halos never stack past the glow itself) / `3` Color Dodge (punchy filmic burn; falls back to Add over transparency so the outside halo survives) / `4` Bloom Only (just the leaked light, alpha from the bloom energy — for compositing the glow on its own). `anamorphic` (−100…100 %) smears the bloom into wide lens streaks — positive horizontal, negative vertical; magnitude sets streak length. `0` disables that pass entirely. It writes outside the silhouette by design; leave `clip` unset. On an adjustment item it blooms the whole below-stack — the usual "cinematic glow" move is an adjustment layer with `threshold` ~0.7 so only real highlights leak. Differs from `glow` (Deep Glow, below): Bloom's spread stops at 4 octaves and blends in display space; Deep Glow reaches up to 2000 px with an inverse-square tail in linear light.
569
+ ## Bloom (`bloom`)
215
570
 
216
- #### Echo
571
+ `bloom` (**Bloom**, group Glow, both scopes) makes bright content leak light — a physically-shaped glow built on an octave pyramid rather than a blur kernel. `threshold` (0–1) with `knee` (soft shoulder) picks what counts as bright; `intensity` (0–600 %) scales the leak; `radius` (1–4) is **octaves of spread, not pixels** — 1 is a tight halo, 4 folds in a sixteenth-resolution octave whose halos span hundreds of px, and fractional values morph continuously. `saturation` desaturates or over-saturates the leaked light only, `tint` + `tintAmount` recolour it (the classic cold anamorphic look is a blue tint at ~60 %), and `blend` composites `0` Add / `1` Screen (protects already-bright frames from clipping) / `2` Lighten (max — halos never stack past the glow itself) / `3` Color Dodge (punchy filmic burn; falls back to Add over transparency so the outside halo survives) / `4` Bloom Only (just the leaked light, alpha from the bloom energy — for compositing the glow on its own). `anamorphic` (−100…100 %) smears the bloom into wide lens streaks — positive horizontal, negative vertical; magnitude sets streak length. `0` disables that pass entirely. It writes outside the silhouette by design. On an adjustment item it blooms the whole below-stack — the usual "cinematic glow" move is an adjustment layer with `threshold` ~0.7 so only real highlights leak. Differs from `glow` (Deep Glow, above): Bloom's spread stops at 4 octaves and blends in display space; Deep Glow reaches up to 2000 px with an inverse-square tail in linear light.
572
+
573
+ ## Echo
217
574
 
218
575
  `echo` (**Echo**, group Motion, both scopes) is After Effects' Echo: the same image sampled at several times and folded together. It is the chain's only **temporal** effect — the one entry whose output depends on frames other than the one being drawn.
219
576
 
@@ -230,9 +587,11 @@ Parameters follow AE's names and sign convention: `echoTime` in seconds, **negat
230
587
 
231
588
  `capacity` (4–64 nominal frames) and `resolution` (`100` Full / `50` Half / `25` Quarter) shape the Frame History ring only, are hidden for the Analytic engine, and are deliberately **not keyframable** — they decide an allocation, and animating one only thrashes it. Everything else keyframes as `fx:<eid>:<param>`.
232
589
 
233
- Echo writes outside the layer's current silhouette by definition, so it does not clip by default — leave `clip` unset unless you specifically want the echoes cut to the layer's live shape. Compared with Motion Trail (a Layer Effect): Motion Trail styles ONE layer's own pixels and sits at a fixed point in that layer's pipeline; Echo is a chain entry, so it can be ordered among other Overlay Effects, given an Active Range, and — on an adjustment item — applied to the whole composited stack below it, which Motion Trail cannot do.
590
+ Echo writes outside the layer's current silhouette by definition, so it does not clip by default; `clip: true` cuts the echoes to the layer's live shape.
591
+
592
+ **Echo or Motion Trail?** [Motion Trail](#motion-trail-entry) (a Layer Effect) styles ONE layer's own pixels and sits at a fixed point in that layer's pipeline; Echo is a chain entry, so it can be ordered among other Overlay Effects, given an Active Range, and — on an adjustment item — applied to the whole composited stack below it, which Motion Trail cannot do.
234
593
 
235
- #### TimeShift
594
+ ## TimeShift
236
595
 
237
596
  `timeshift` (**TimeShift**, group Motion, both scopes) is the ARRI timing-shift box: the phase between the shutter and the film movement is shifted, so the shutter is still open while the pulldown drags the film through the gate. One exposure is therefore the picture SLIDING PAST the gate rather than sitting still in it, which is why the look is a **streak along the film path** and not a blur.
238
597
 
@@ -242,33 +601,33 @@ Echo writes outside the layer's current silhouette by definition, so it does not
242
601
  - `headroom` (**Highlight Headroom**, 0–99) is why streaks stay hot. A streak spreads fixed light over a longer path, so it dims as it lengthens — physically right, and on its own it turns every streak to mush, because the real thing is shot on highlights many stops over clipping. Drop it to `0` for the honest, dim version.
243
602
  - `shutter` (11.25–360°) trades streak length against how much subject motion the streak carries, `falloff` shapes the ramp along it, `jitter` re-dices streak length every frame the way the box's own jitter function does, `gap` rides the unexposed strip between frames through the smear, and `gain` (20–400 %) scales the result.
244
603
  - `samples` (4–48), `capacity` (**Ring**, 3–16) and `resolution` (`100` Full / `50` Half / `25` Quarter) are allocations, so they are deliberately NOT keyframable — animating one only thrashes it. Everything else keyframes as `fx:<eid>:<param>`.
245
- - It writes outside the layer's silhouette by definition, so leave `clip` unset.
604
+ - It writes outside the layer's silhouette by definition.
246
605
 
247
- #### Fill
606
+ ## Fill
248
607
 
249
- `fill` (**Fill**, group Color, both scopes) is After Effects' Fill: one flat colour painted over everything the item covers, keeping the item's alpha exactly as it was — a soft edge, a keyed subject or a text outline stays as transparent as before, and fully transparent pixels stay transparent. `params.color` is the fill colour (default `#ff3d3d`). `params.blend` selects how that colour meets the original pixels: `0` Normal (default — the flat AE fill) / `1` Multiply / `2` Screen / `3` Overlay / `4` Soft Light / `5` Hard Light / `6` Add / `7` Lighten / `8` Darken / `9` Difference / `10` Color (the fill's hue and saturation at the source's own luminance). The entry's `mix` is the fill's opacity (there is no separate opacity param); keyframe `fx:<eid>:mix` to fade a fill in or out. It is self-clipping (it never writes outside the source alpha), so leave `clip` unset — clipping it multiplies a semi-transparent layer's alpha a second time. On an adjustment item it fills the whole below-stack's coverage. Typical uses: a silhouette (Normal, `mix` 100), a colour tint (Multiply or Color), or a flash (Normal with a keyframed `mix`).
608
+ `fill` (**Fill**, group Color, both scopes) is After Effects' Fill: one flat colour painted over everything the item covers, keeping the item's alpha exactly as it was — a soft edge, a keyed subject or a text outline stays as transparent as before, and fully transparent pixels stay transparent. `params.color` is the fill colour (default `#ff3d3d`). `params.blend` selects how that colour meets the original pixels: `0` Normal (default — the flat AE fill) / `1` Multiply / `2` Screen / `3` Overlay / `4` Soft Light / `5` Hard Light / `6` Add / `7` Lighten / `8` Darken / `9` Difference / `10` Color (the fill's hue and saturation at the source's own luminance). The entry's `mix` is the fill's opacity (there is no separate opacity param); keyframe `fx:<eid>:mix` to fade a fill in or out. It is self-clipping (it never writes outside the source alpha); `clip: true` would multiply a semi-transparent layer's alpha a second time. On an adjustment item it fills the whole below-stack's coverage. Typical uses: a silhouette (Normal, `mix` 100), a colour tint (Multiply or Color), or a flash (Normal with a keyframed `mix`).
250
609
 
251
- #### Shape Rulers
610
+ ## Shape Rulers
252
611
 
253
- `shapeRulers` (**Shape Rulers**, group Graphic, **layer scope only**) draws full-frame guide lines along the four edges of the item's own box, with a mark on each corner — a design tool's selection guides. It has no animation of its own: every frame it measures the box from the item's current position, scale and rotation (keyframes, parenting and follow included), so to move, grow or turn the guides, animate the item. The box is the item's plate — a shape's size, an image's picture, a video's frame-sized quad — and on a **text** item the laid-out text block (its lines, alignment and letter spacing — not the frame-sized plate text draws on). Sizes are pixels at 1080p and scale with the frame's short side. `spacingX` / `spacingY` (−200…400 px) push the guides out from the box (negative pulls them in). `rotation`: `1` follows the item's rotation (default) / `0` frames the axis-aligned bounds of the rotated box. Lines: `lineColor`, `lineWidth` (0.5–12 px, default 1.5), `lineOpacity` (0–100 %, default 90), `extend` `0` full frame (default) / `1` past the corners by `extendLength` px / `2` box outline only, `dash` (0–60 px, `0` = solid). Corners: `markShape` `0` Square (default) / `1` Circle / `2` Diamond / `3` Square outline / `4` Circle outline / `5` Cross / `6` Corner bracket / `7` None, `markColor`, `markWidth` / `markHeight` (2–120 px, default 15 × 15), `markRadius` (squares), `markStroke` (outlines, cross, bracket), `midMarks` `1` adds a mark at each edge's midpoint. The guides lie outside the item's silhouette by design, so leave `clip` unset (`true` cuts them down to the artwork); they take the item's opacity and blend mode. Keyframable: every number param and `mix`. Example: `{"id": "shapeRulers", "mix": 100, "params": {"spacingX": 40, "spacingY": 24, "markShape": 6, "markStroke": 3}}`.
612
+ `shapeRulers` (**Shape Rulers**, group Graphic, **layer scope only**) draws full-frame guide lines along the four edges of the item's own box, with a mark on each corner — a design tool's selection guides. It has no animation of its own: every frame it measures the box from the item's current position, scale and rotation (keyframes, parenting and follow included), so to move, grow or turn the guides, animate the item. The box is the item's plate — a shape's size, an image's picture, a video's frame-sized quad — and on a **text** item the laid-out text block (its lines, alignment and letter spacing — not the frame-sized plate text draws on). Sizes are pixels at 1080p and scale with the frame's short side. `spacingX` / `spacingY` (−200…400 px) push the guides out from the box (negative pulls them in). `rotation`: `1` follows the item's rotation (default) / `0` frames the axis-aligned bounds of the rotated box. Lines: `lineColor`, `lineWidth` (0.5–12 px, default 1.5), `lineOpacity` (0–100 %, default 90), `extend` `0` full frame (default) / `1` past the corners by `extendLength` px / `2` box outline only, `dash` (0–60 px, `0` = solid). Corners: `markShape` `0` Square (default) / `1` Circle / `2` Diamond / `3` Square outline / `4` Circle outline / `5` Cross / `6` Corner bracket / `7` None, `markColor`, `markWidth` / `markHeight` (2–120 px, default 15 × 15), `markRadius` (squares), `markStroke` (outlines, cross, bracket), `midMarks` `1` adds a mark at each edge's midpoint. The guides lie outside the item's silhouette by design (`clip: true` cuts them down to the artwork); they take the item's opacity and blend mode. Keyframable: every number param and `mix`. Example: `{"id": "shapeRulers", "mix": 100, "params": {"spacingX": 40, "spacingY": 24, "markShape": 6, "markStroke": 3}}`.
254
613
 
255
- #### Copy Color Profile
614
+ ## Copy Color Profile
256
615
 
257
- `copyColor` (**Copy Color Profile**, group Color, both scopes, free) moves this content's colour distribution onto another layer's. `params.sourceLayer` is the **item id** of the reference layer (a `type: 'layer'` param — any other `image` or `video` item in the project, on any channel, at any time; the channel can stay hidden, which is the normal way to keep a colour reference out of shot; `''`, a deleted id or the item's own id = exact bypass). **The reference is always the clip's source picture** (an image: the file; a video: its thumbnail), cover-fitted and measured whole — mean + spread per channel, alpha-weighted — never the layer as posed or effected on screen; text/shape/sequence/model layers have no picture and bypass. The effect's own input is measured the same way (its layer box on a regular item, the whole frame on an adjustment item). The reference layer keeps drawing normally (it is not consumed like a Track Matte source). `space`: `0` Oklab (perceptual, default) / `1` YCbCr / `2` RGB. `transfer`: `0` Statistics (Reinhard-style: centre on the target mean, scale the spread toward the reference's by `contrast` 0–100 % — 0 = shift the mean only — clamped by `maxGain` 1–8, land on the reference mean) / `1` Ratio (multiply by the mean ratio, grey-world white balance; chroma axes shift instead). `lum` and `col` (0–100 %) are how much of the brightness axis and the chroma axes are transferred (in RGB `col` applies to all channels and `lum` blends the source brightness back). `sat` (−100…100 %) scales chroma after the transfer. `protHi` / `protLo` (0–100 %) hold the ends of the tone range so whites stay white and blacks stay black. `inset` (0–45 %) crops the sampled region toward the box centre (skip a border or letterbox); `grid` (8–64, static) is taps per axis. Keyframable: `lum`, `col`, `contrast`, `sat`, `maxGain`, `protHi`, `protLo`, `inset` and the entry's `mix`. Self-clipping (premultiplies by the source alpha), so leave `clip` unset. A reference inside a sequence must be another layer of the same sequence. Example: `{"id": "copyColor", "mix": 100, "params": {"sourceLayer": "<reference item id>", "space": 0, "transfer": 0, "lum": 60, "col": 100, "contrast": 50, "protHi": 30, "protLo": 20}}`.
616
+ `copyColor` (**Copy Color Profile**, group Color, both scopes, free) moves this content's colour distribution onto another layer's. `params.sourceLayer` is the **item id** of the reference layer (a `type: 'layer'` param — any other `image` or `video` item in the project, on any channel, at any time; the channel can stay hidden, which is the normal way to keep a colour reference out of shot; `''`, a deleted id or the item's own id = exact bypass). **The reference is always the clip's source picture** (an image: the file; a video: its thumbnail), cover-fitted and measured whole — mean + spread per channel, alpha-weighted — never the layer as posed or effected on screen; text/shape/sequence/model layers have no picture and bypass. The effect's own input is measured the same way (its layer box on a regular item, the whole frame on an adjustment item). The reference layer keeps drawing normally (it is not consumed like a Track Matte source). `space`: `0` Oklab (perceptual, default) / `1` YCbCr / `2` RGB. `transfer`: `0` Statistics (Reinhard-style: centre on the target mean, scale the spread toward the reference's by `contrast` 0–100 % — 0 = shift the mean only — clamped by `maxGain` 1–8, land on the reference mean) / `1` Ratio (multiply by the mean ratio, grey-world white balance; chroma axes shift instead). `lum` and `col` (0–100 %) are how much of the brightness axis and the chroma axes are transferred (in RGB `col` applies to all channels and `lum` blends the source brightness back). `sat` (−100…100 %) scales chroma after the transfer. `protHi` / `protLo` (0–100 %) hold the ends of the tone range so whites stay white and blacks stay black. `inset` (0–45 %) crops the sampled region toward the box centre (skip a border or letterbox); `grid` (8–64, static) is taps per axis. Keyframable: `lum`, `col`, `contrast`, `sat`, `maxGain`, `protHi`, `protLo`, `inset` and the entry's `mix`. Self-clipping (premultiplies by the source alpha). A reference inside a sequence must be another layer of the same sequence. Example: `{"id": "copyColor", "mix": 100, "params": {"sourceLayer": "<reference item id>", "space": 0, "transfer": 0, "lum": 60, "col": 100, "contrast": 50, "protHi": 30, "protLo": 20}}`.
258
617
 
259
- #### Double Exposure
618
+ ## Double Exposure
260
619
 
261
- `doubleExposure` (**Double Exposure**, group Color, both scopes, **PRO** — free to preview, rendering needs a PRO license) puts another layer's picture inside this layer's silhouette: the classic portrait-filled-with-a-forest look. `params.sourceLayer` is the **item id** of the texture layer (a `type: 'layer'` param; `''` / a deleted id / the item's own id = exact bypass). **The texture layer must be on ANOTHER visible channel and overlap this item in time** (`layerRefMode: 'live'` — unlike Copy Color Profile there is no source-picture fallback: a reference that is not on screen together with this item is a plain bypass, and the card's picker only offers layers that can be). While the effect is live the texture is used **as drawn** — its isolate is rendered every frame through its own camera with its transforms, keyframes and layer-effect stack — and it is **consumed**: it no longer draws on its own (like a Track Matte source). So put the texture on its own channel spanning the same time, and position / scale / rotate / keyframe that layer to place the texture; a video texture keeps its trim, speed and effects. There are no scale / offset / rotation params on the effect itself. `blend` (`0` Normal / `1` Screen (default) / `2` Lighten / `3` Add / `4` Multiply / `5` Darken / `6` Overlay / `7` Soft Light / `8` Hard Light / `9` Difference / `10` Exclusion / `11` Color Dodge / `12` Color Burn / `13` Luminosity / `14` Color) is how the texture meets this layer's own shading — Screen over a light background or Lighten over a dark one is the classic recipe, Normal turns the silhouette into a window. `region`: `0` Inside the silhouette (default) / `1` Whole frame / `2` Outside only (1 and 2 add coverage where this layer is transparent). `tones`: `0` Nothing / `1` weight the texture by the layer's shadows / `2` by its highlights. `silhouette`: `0` Layer alpha (default — use a cutout, Magic Mask or RotoScope) / `1` Luma key, dark = background / `2` Luma key, bright = background, with `keyThreshold` (0–100 %) and `keySoftness` (0–50 %); a luma key cuts the layer's alpha. `desaturate` (0–100 %) greys this layer; `texContrast` (0–200 %), `texBrightness` (−100…100 %), `texDesaturate` (0–100 %) grade the texture. `fade`: `0` Off / `1` Fades the texture (default) / `2` Fades texture and layer to transparent — a gradient edge across this layer's box at `fadeAngle` (degrees, `90` = toward the bottom, `-90` = toward the top, `0` = toward the right), centred at `fadePosition` (−200…200 % of the box height from its centre, negative = the other side) over `fadeWidth` (0–200 %). `duotone` (0–100 %) maps the finished plate onto `duoShadow` → `duoHighlight` by luminance. The entry's `mix` is the whole effect's amount. Keyframable: every number param (`keyThreshold`, `keySoftness`, `desaturate`, `texContrast`, `texBrightness`, `texDesaturate`, `fadeAngle`, `fadePosition`, `fadeWidth`, `duotone`) and `mix` — keyframe `fadePosition` from −150 to its resting value for a sweep-in reveal, or `mix` for a plain fade-in. It writes outside the source alpha in the Whole-frame / Outside modes, so leave `clip` unset. A texture inside a sequence must be another layer of the same sequence. Presets in the card: Classic, Noir, Window, Duotone, Negative, Dream. Example: `{"id": "doubleExposure", "mix": 100, "params": {"sourceLayer": "<texture item id>", "blend": 1, "region": 0, "desaturate": 35, "texDesaturate": 30, "fade": 1, "fadeAngle": 90, "fadePosition": 12, "fadeWidth": 50}}`.
620
+ `doubleExposure` (**Double Exposure**, group Color, both scopes, **PRO** — free to preview, rendering needs a PRO license) puts another layer's picture inside this layer's silhouette: the classic portrait-filled-with-a-forest look. `params.sourceLayer` is the **item id** of the texture layer (a `type: 'layer'` param; `''` / a deleted id / the item's own id = exact bypass). **The texture layer must be on ANOTHER visible channel and overlap this item in time** (`layerRefMode: 'live'` — unlike Copy Color Profile there is no source-picture fallback: a reference that is not on screen together with this item is a plain bypass). While the effect is live the texture is used **as drawn** — its isolate is rendered every frame through its own camera with its transforms, keyframes and layer-effect stack — and it is **consumed**: it no longer draws on its own (like a Track Matte source). So put the texture on its own channel spanning the same time, and position / scale / rotate / keyframe that layer to place the texture; a video texture keeps its trim, speed and effects. There are no scale / offset / rotation params on the effect itself. `blend` (`0` Normal / `1` Screen (default) / `2` Lighten / `3` Add / `4` Multiply / `5` Darken / `6` Overlay / `7` Soft Light / `8` Hard Light / `9` Difference / `10` Exclusion / `11` Color Dodge / `12` Color Burn / `13` Luminosity / `14` Color) is how the texture meets this layer's own shading — Screen over a light background or Lighten over a dark one is the classic recipe, Normal turns the silhouette into a window. `region`: `0` Inside the silhouette (default) / `1` Whole frame / `2` Outside only (1 and 2 add coverage where this layer is transparent). `tones`: `0` Nothing / `1` weight the texture by the layer's shadows / `2` by its highlights. `silhouette`: `0` Layer alpha (default — use a cutout, Magic Mask or RotoScope) / `1` Luma key, dark = background / `2` Luma key, bright = background, with `keyThreshold` (0–100 %) and `keySoftness` (0–50 %); a luma key cuts the layer's alpha. `desaturate` (0–100 %) greys this layer; `texContrast` (0–200 %), `texBrightness` (−100…100 %), `texDesaturate` (0–100 %) grade the texture. `fade`: `0` Off / `1` Fades the texture (default) / `2` Fades texture and layer to transparent — a gradient edge across this layer's box at `fadeAngle` (degrees, `90` = toward the bottom, `-90` = toward the top, `0` = toward the right), centred at `fadePosition` (−200…200 % of the box height from its centre, negative = the other side) over `fadeWidth` (0–200 %). `duotone` (0–100 %) maps the finished plate onto `duoShadow` → `duoHighlight` by luminance. The entry's `mix` is the whole effect's amount. Keyframable: every number param (`keyThreshold`, `keySoftness`, `desaturate`, `texContrast`, `texBrightness`, `texDesaturate`, `fadeAngle`, `fadePosition`, `fadeWidth`, `duotone`) and `mix` — keyframe `fadePosition` from −150 to its resting value for a sweep-in reveal, or `mix` for a plain fade-in. It writes outside the source alpha in the Whole-frame / Outside modes. A texture inside a sequence must be another layer of the same sequence. Presets: Classic, Noir, Window, Duotone, Negative, Dream. Example: `{"id": "doubleExposure", "mix": 100, "params": {"sourceLayer": "<texture item id>", "blend": 1, "region": 0, "desaturate": 35, "texDesaturate": 30, "fade": 1, "fadeAngle": 90, "fadePosition": 12, "fadeWidth": 50}}`.
262
621
 
263
- #### Film Stock
622
+ ## Film Stock
264
623
 
265
- `filmStock` (**Film Stock**, group Color, both scopes, **PRO** — free to preview, rendering needs a PRO license) is an analog film emulation in the Dehancer mould: one stock preset plus the entry's `mix`. `preset` selects the stock by index — `0` Kodak Vision3 250D (daylight cine negative, clean), `1` Kodak Vision3 500T (tungsten cine negative, cool shadows / warm skin, more grain), `2` Fuji Eterna 500 (low-contrast cine, teal blacks), `3` CineStill 800T (tungsten, strong red halation), `4` Kodak Portra 400 (peach skin, pastel), `5` Fuji Pro 400H (mint greens, cool pastel), `6` Agfa Vista 200 (punchy warm reds), `7` Kodak Ektachrome E100 (clean cool slide, deep blues), `8` Kodachrome 64 (hot reds, ivory whites, brown blacks), `9` Fuji Velvia 50 (maximum saturation slide), `10` Ilford HP5 Plus (black & white, open shadows), `11` Kodak Tri-X 400 (black & white, crunchy, coarse grain), `12` Polaroid 600 (faded instant, cyan shadows, creamy highlights, heavy vignette), `13` Cross Process (blue blacks, yellow whites, crushed contrast). Each stock carries its own white balance, dye matrix, tone curve, split tone, halation, bloom, grain and vignette; `grain`, `halation`, `bloom`, `vignette` (0–300 %, default 100) are multipliers on the stock's own amounts, not absolute values — `halation: 0` removes the glow entirely, `200` doubles it. Film damage is independent of the stock and off by default: `flicker` (exposure wobble), `weave` (gate weave — the whole frame drifts and jitters by a pixel or two), `leak` (a warm-to-magenta light leak breathing in from the right edge) with `leakSpeed` (10–2000 %, default 100 — the leak's clock; 400 % moves it four times as fast). Grain is static (it does not boil between frames); flicker, weave and the leak run on the clip clock, so renders are deterministic. Halation and bloom are true glows (threshold before blur) and bleed past a cutout's alpha, so leave `clip` unset. Keyframable: `grain`, `halation`, `bloom`, `vignette`, `flicker`, `weave`, `leak`, `leakSpeed` and the entry's `mix`; `preset` is a select. Presets in the card: Clean Scan, Projected Print, Worn Reel, Light Leak. Example: `{"id": "filmStock", "mix": 100, "params": {"preset": 3, "grain": 100, "halation": 120, "bloom": 100, "vignette": 100, "flicker": 0, "weave": 0, "leak": 0, "leakSpeed": 100}}`.
624
+ `filmStock` (**Film Stock**, group Color, both scopes, **PRO** — free to preview, rendering needs a PRO license) is an analog film emulation in the Dehancer mould: one stock preset plus the entry's `mix`. `preset` selects the stock by index — `0` Kodak Vision3 250D (daylight cine negative, clean), `1` Kodak Vision3 500T (tungsten cine negative, cool shadows / warm skin, more grain), `2` Fuji Eterna 500 (low-contrast cine, teal blacks), `3` CineStill 800T (tungsten, strong red halation), `4` Kodak Portra 400 (peach skin, pastel), `5` Fuji Pro 400H (mint greens, cool pastel), `6` Agfa Vista 200 (punchy warm reds), `7` Kodak Ektachrome E100 (clean cool slide, deep blues), `8` Kodachrome 64 (hot reds, ivory whites, brown blacks), `9` Fuji Velvia 50 (maximum saturation slide), `10` Ilford HP5 Plus (black & white, open shadows), `11` Kodak Tri-X 400 (black & white, crunchy, coarse grain), `12` Polaroid 600 (faded instant, cyan shadows, creamy highlights, heavy vignette), `13` Cross Process (blue blacks, yellow whites, crushed contrast). Each stock carries its own white balance, dye matrix, tone curve, split tone, halation, bloom, grain and vignette; `grain`, `halation`, `bloom`, `vignette` (0–300 %, default 100) are multipliers on the stock's own amounts, not absolute values — `halation: 0` removes the glow entirely, `200` doubles it. Film damage is independent of the stock and off by default: `flicker` (exposure wobble), `weave` (gate weave — the whole frame drifts and jitters by a pixel or two), `leak` (a warm-to-magenta light leak breathing in from the right edge) with `leakSpeed` (10–2000 %, default 100 — the leak's clock; 400 % moves it four times as fast). Grain is static (it does not boil between frames); flicker, weave and the leak run on the clip clock, so renders are deterministic. Halation and bloom are true glows (threshold before blur) and bleed past a cutout's alpha. Keyframable: `grain`, `halation`, `bloom`, `vignette`, `flicker`, `weave`, `leak`, `leakSpeed` and the entry's `mix`; `preset` is a select. Presets: Clean Scan, Projected Print, Worn Reel, Light Leak. Example: `{"id": "filmStock", "mix": 100, "params": {"preset": 3, "grain": 100, "halation": 120, "bloom": 100, "vignette": 100, "flicker": 0, "weave": 0, "leak": 0, "leakSpeed": 100}}`.
266
625
 
267
- #### Fast Blur
626
+ ## Fast Blur
268
627
 
269
- `fastBlur` (**Fast Blur**, group Lens, both scopes, free) is After Effects' Fast Box Blur. `blurriness` (0–250 px at 1080p, default 20) is the box radius: every iteration convolves a box `2 × blurriness + 1` px wide, so the visible spread is about `iterations × blurriness`. `iterations` (1–5, default 3): `1` is a hard box (a square-edged smear), `3` reads as Gaussian, and more iterations blur more, as in AE. `dimensions`: `0` Horizontal and Vertical (default) / `1` Horizontal / `2` Vertical (a one-axis blur is a motion-smear look and keeps the other axis sharp). `repeatEdges`: `1` On (default, the frame edges stay solid) / `0` Off (the frame edges fade toward transparent, as in AE's default). `blurriness: 0` is an exact bypass that costs no render pass. Fractional and keyframed `blurriness` animate smoothly, with no stepping. It is a growth effect: the blur spreads past a cutout's silhouette, so leave `clip` unset. Keyframable: `blurriness`, `iterations` (rounded to whole iterations) and `mix`; the selects are not. Presets in the card: Soft, Defocus, Heavy, Box, Smear H, Smear V. Example: `{"id": "fastBlur", "mix": 100, "params": {"blurriness": 24, "iterations": 3, "dimensions": 0, "repeatEdges": 1}}`. A rack-focus pull is a keyframed `fx:<eid>:blurriness` track from 40 to 0.
628
+ `fastBlur` (**Fast Blur**, group Lens, both scopes, free) is After Effects' Fast Box Blur. `blurriness` (0–250 px at 1080p, default 20) is the box radius: every iteration convolves a box `2 × blurriness + 1` px wide, so the visible spread is about `iterations × blurriness`. `iterations` (1–5, default 3): `1` is a hard box (a square-edged smear), `3` reads as Gaussian, and more iterations blur more, as in AE. `dimensions`: `0` Horizontal and Vertical (default) / `1` Horizontal / `2` Vertical (a one-axis blur is a motion-smear look and keeps the other axis sharp). `repeatEdges`: `1` On (default, the frame edges stay solid) / `0` Off (the frame edges fade toward transparent, as in AE's default). `blurriness: 0` is an exact bypass that costs no render pass. Fractional and keyframed `blurriness` animate smoothly, with no stepping. It is a growth effect: the blur spreads past a cutout's silhouette. Keyframable: `blurriness`, `iterations` (rounded to whole iterations) and `mix`; the selects are not. Presets: Soft, Defocus, Heavy, Box, Smear H, Smear V. Example: `{"id": "fastBlur", "mix": 100, "params": {"blurriness": 24, "iterations": 3, "dimensions": 0, "repeatEdges": 1}}`. A rack-focus pull is a keyframed `fx:<eid>:blurriness` track from 40 to 0.
270
629
 
271
- #### Radial Fast Blur
630
+ ## Radial Fast Blur
272
631
 
273
632
  `radialFastBlur` (**Radial Fast Blur**, group Lens, both scopes, free) is After Effects' CC Radial Fast Blur: every pixel gathers the picture along the line towards `centerX`/`centerY`, so content smears AWAY from the Center. It is the effect for light rays and light cones (a thin line lit from behind), zoom bursts and shadow rays; `radialBlur` is only a short symmetric zoom and `lightRays` averages a few dozen taps, so neither can carry a 2–4 px line over a long trail.
274
633
 
@@ -279,20 +638,21 @@ Echo writes outside the layer's current silhouette by definition, so it does not
279
638
  - `centerX` / `centerY` (−0.5…1.5, default 0.5): 0–1 with **y UP**, like every effect centre. An After Effects point (x, y) in a W×H comp is `centerX = x / W`, `centerY = 1 − y / H`.
280
639
  - `space`: `0` Layer (default — the Center is in the layer's own box and rides it, like Radial Blur) / `1` Composition (the Center is pinned to the frame: a fixed light that a moving, rotating layer passes in front of — how CC Radial Fast Blur behaves on an AE shape layer). On an adjustment item the box is the frame, so both read the same.
281
640
 
282
- It writes outside the silhouette by design; leave `clip` unset. Keyframable: `amount`, `centerX`, `centerY`, `falloff`, `intensity`, `mix`; the selects are not. Cost when used: a 3×3 prep and 1–5 eight-tap levels at half resolution (4 levels for Amount ~85 at 1080p — the fewest that keep the trail contiguous at the frame corner) plus one full-resolution pass; the full-resolution source stays sharp wherever the trail is under ~2 px. Presets in the card: Zoom Burst, Light Rays, Light Cone, Long Streaks, Shadow Rays. Example: `{"id": "radialFastBlur", "mix": 100, "params": {"amount": 85, "centerX": 0.27, "centerY": 0.5, "zoom": 1, "falloff": 75, "intensity": 400, "space": 1}}`.
641
+ It writes outside the silhouette by design. Keyframable: `amount`, `centerX`, `centerY`, `falloff`, `intensity`, `mix`; the selects are not. Cost when used: a 3×3 prep and 1–5 eight-tap levels at half resolution (4 levels for Amount ~85 at 1080p — the fewest that keep the trail contiguous at the frame corner) plus one full-resolution pass; the full-resolution source stays sharp wherever the trail is under ~2 px. Presets: Zoom Burst, Light Rays, Light Cone, Long Streaks, Shadow Rays. Example: `{"id": "radialFastBlur", "mix": 100, "params": {"amount": 85, "centerX": 0.27, "centerY": 0.5, "zoom": 1, "falloff": 75, "intensity": 400, "space": 1}}`.
283
642
 
284
643
  **Light cone through a line (the AE "Light Text Reveal" port).** The AE rig is a 2–4 px line whose own layer carries CC Radial Fast Blur ×2, Glow ×4 and Tint, with the text layers using it as a LUMA track matte. Rebuild it as:
285
644
 
286
645
  1. A `sequence` clip holding: the glow line (`shapeType: "line"`, `lineWidth` 4, `lineStroke.paint` `{mode: "gradient", colors: ["#ffffff", "#000000", "#000000"], span: "path"}` — AE's white→black gradient stroke, which becomes the yellow→green split after the tint), animated with the AE `translateX/Y`/`rotate` keys; an adjustment item with `radialFastBlur` twice — Center 1 = Glow Position + (0, Spread), `zoom` 1, `falloff` 98, `intensity` 1500, `mix` 40; Center 2 = Glow Position − (0, Spread), `zoom` 1, `falloff` 96, `intensity` 1000 — `space` 1 on both, `amount` = AE amount (80 + Light Distance, 80 + 2 × Light Distance); and a second adjustment item with AE's outer glows as two `glow` entries (`exposure` 0.5 / `glowThreshold` 71 / `glowRadius` 240 and `exposure` 0.1 / `glowThreshold` 0 / `glowRadius` 160), the Tint as `curves` with two points per channel (black → Light Color 3, white → Light Color 2: `r [[0, 43/255], [1, 198/255]]`, `g [[0, 217/255], [1, 1]]`, `b [[0, 143/255], [1, 74/255]]`) and AE's comp Curves as a second `curves` (kept inside the cone: on top of everything it also dims the white line). An effect chain holds 4 entries, hence two adjustment items.
287
646
  2. Two items of that ONE sequence clip at the top level: one visible (the cone), one used only as the matte source (a matte source is hidden from output).
288
647
  3. The white line (2 px) on its own channel, and the text with `trackMatte: {sourceId: <the matte item>, mode: "luma"}`.
289
- Key the light on/off with the cone items' `opacity` plus `fx:<eid>:amount`, and raise `fx:<eid>:falloff` towards 99.8 as it goes off (CC's trail shortens with Amount; here the visible length is set by Falloff); key the light source with `fx:<eid>:centerX/centerY`. A ready rig: `tests/radial-fast-blur/build_light_rig.py`. CC's Standard mode keeps a thin line bright, which this effect's normalised Standard does not; Brightest + Falloff + Intensity is the measured match (see the report in `tests/radial-fast-blur/README.md`).
290
648
 
291
- #### Grid
649
+ Key the light on/off with the cone items' `opacity` plus `fx:<eid>:amount`, and raise `fx:<eid>:falloff` towards 99.8 as it goes off (CC's trail shortens with Amount; here the visible length is set by Falloff); key the light source with `fx:<eid>:centerX/centerY`. CC's Standard mode keeps a thin line bright, which this effect's normalised Standard does not; Brightest + Falloff + Intensity is the measured match.
292
650
 
293
- `gridLines` (**Grid**, group Graphic, both scopes, free) draws anti-aliased grid lines over the item. `lines`: `0` Columns and Rows (default) / `1` Columns Only (vertical lines) / `2` Rows Only (horizontal lines). `cellSize` (4–600 px, default 120) is the line spacing, `lineWidth` (0–40 px, default 1.5; widths under 1 px fade rather than flicker; `0` is a bypass), `rotation` (−180…180°, clockwise positive) turns the grid about its anchor, and `offsetX` / `offsetY` (−1000…1000 px, `+Y` = down) move the anchor from the centre. A line always passes through the anchor, so animating `offsetX` by one `cellSize` per second pans the grid seamlessly. `color` is the line colour; the entry's `mix` is the line opacity. Sizes are px at 1080p of the item's own box. On a regular item the grid is printed on the artwork: it moves, scales and rotates with the item and takes the item's own alpha (no lines over transparent pixels), so leave `clip` unset. On an adjustment item it covers the whole frame, transparent areas included. On an adjustment layer, axis-aligned odd-width lines snap to pixel centres, so `lineWidth: 1` at 1080p is one crisp pixel. Keyframable: `cellSize`, `lineWidth`, `rotation`, `offsetX`, `offsetY`, `mix`. Presets in the card: Hairline, Blueprint, Columns, Rows, Diagonal, Bold. Example: `{"id": "gridLines", "mix": 60, "params": {"lines": 0, "cellSize": 120, "lineWidth": 1, "rotation": 0, "color": "#ffffff"}}`.
651
+ ## Grid
294
652
 
295
- #### Strip
653
+ `gridLines` (**Grid**, group Graphic, both scopes, free) draws anti-aliased grid lines over the item. `lines`: `0` Columns and Rows (default) / `1` Columns Only (vertical lines) / `2` Rows Only (horizontal lines). `cellSize` (4–600 px, default 120) is the line spacing, `lineWidth` (0–40 px, default 1.5; widths under 1 px fade rather than flicker; `0` is a bypass), `rotation` (−180…180°, clockwise positive) turns the grid about its anchor, and `offsetX` / `offsetY` (−1000…1000 px, `+Y` = down) move the anchor from the centre. A line always passes through the anchor, so animating `offsetX` by one `cellSize` per second pans the grid seamlessly. `color` is the line colour; the entry's `mix` is the line opacity. Sizes are px at 1080p of the item's own box. On a regular item the grid is printed on the artwork: it moves, scales and rotates with the item and takes the item's own alpha (no lines over transparent pixels). On an adjustment item it covers the whole frame, transparent areas included. On an adjustment layer, axis-aligned odd-width lines snap to pixel centres, so `lineWidth: 1` at 1080p is one crisp pixel. Keyframable: `cellSize`, `lineWidth`, `rotation`, `offsetX`, `offsetY`, `mix`. Presets: Hairline, Blueprint, Columns, Rows, Diagonal, Bold. Example: `{"id": "gridLines", "mix": 60, "params": {"lines": 0, "cellSize": 120, "lineWidth": 1, "rotation": 0, "color": "#ffffff"}}`.
654
+
655
+ ## Strip
296
656
 
297
657
  `strip` (**Strip**, group Graphic, both scopes, free) draws a solid bar along the top and the bottom edge of the item: a letterbox. Use it for cinematic bars instead of two rectangle shapes. One entry follows the item, and the slide in and out is built in.
298
658
 
@@ -301,16 +661,16 @@ Key the light on/off with the cone items' `opacity` plus `fx:<eid>:amount`, and
301
661
  - `intro`: `1` On (default) / `0` Off. The top bar slides down from above the box and the bottom bar slides up from below it, over `introDuration` seconds (0.05–5, default 1.25) from the item's start, on `introEasing` (default `"Smooth"`). At the item's first frame the bars are not there yet.
302
662
  - `outro`: `0` Off (default) / `1` On. The same move backwards over `outroDuration` (0.05–5, default 0.8), finishing on the item's last frame, on `outroEasing` (default `"Smooth"`).
303
663
  - With `start` / `end` set, the entrance starts at `start` and the outro finishes at `end`. If the two durations together are longer than the item (or the window), the bars turn back early instead of jumping.
304
- - The bars sit on the item's box. On a regular item they move, scale and turn with it and cover the box whatever its alpha, so leave `clip` unset. On an adjustment item they span the whole frame, which is the usual letterbox over footage.
664
+ - The bars sit on the item's box. On a regular item they move, scale and turn with it and cover the box whatever its alpha. On an adjustment item they span the whole frame, which is the usual letterbox over footage.
305
665
  - Keyframable: `height`, `mix`. The switches, durations and easings are static.
306
666
 
307
667
  Example, a 2.39:1 letterbox that slides in and out over everything below (on an adjustment item): `{"id": "strip", "eid": "letterbox", "enabled": true, "mix": 100, "params": {"height": 12.8, "intro": 1, "introDuration": 1.25, "introEasing": "Smooth", "outro": 1, "outroDuration": 0.8, "outroEasing": "Expo In"}}`.
308
668
 
309
- #### ASCII
669
+ ## ASCII
310
670
 
311
- `ascii` (**ASCII**, group Print, both scopes, free) rebuilds the picture from characters. Each cell's average tone picks a glyph from a ramp sorted by how much ink each character has (sparse = dark, dense = bright). `cellSize` (6–400 px at 1080p of the item's box, default 14) is the character height; the width follows the font. `charset`: `0` Standard ` .:-=+*#%@` (default) / `1` Detailed (70 characters) / `2` Blocks `░▒▓█` / `3` Binary / `4` Dots / `5` Matrix (half-width katakana) / `6` Custom, which uses `customChars`. `customChars` is a `type: "text"` param: a string of up to 80 characters, de-duplicated and auto-sorted by ink, with a space added for empty cells, so any word works. `font`: `0` Mono / `1` Mono Bold (default) / `2` Sans Bold / `3` Serif. `colorMode`: `0` Source (each glyph in its cell's colour, `colorBoost` 0–100 % pushes it toward full brightness) / `1` Single Color (`glyphColor`) / `2` Gradient (`glyphColor` for dark cells → `lightColor` for bright ones). `background`: `0` Solid (`backgroundColor`, default `#050505`) / `1` Source (the picture behind the glyphs at `sourceLevel` 0–100 %) / `2` None (transparent between glyphs). `contrast` (0–300 %, default 120) and `brightness` (−100…100) shape the tone before glyphs are picked. `invert` `1` maps dark areas to dense glyphs, the dark-ink-on-paper look (pair it with a light `backgroundColor`). `dither` (0–100 %, default 60) breaks up the contour lines a slow gradient would otherwise draw; it is fixed per cell, so video does not shimmer. `edgeLines` (0–100 %, default 0) swaps in `- / | \` along contours, following the edge direction. The character grid is printed on the item's box, so on a regular item it rides the layer; `clip` defaults on (the background is cut to the item's shape). Keyframable: `cellSize`, `colorBoost`, `sourceLevel`, `contrast`, `brightness`, `dither`, `edgeLines`, `mix`; the selects, colours and `customChars` are not. Presets in the card: Terminal, Color, Blocks, Binary, Matrix, Edges, Paper. Example: `{"id": "ascii", "mix": 100, "params": {"cellSize": 14, "charset": 6, "customChars": "GREENLIGHT", "font": 1, "colorMode": 1, "glyphColor": "#39ff7a", "background": 0, "backgroundColor": "#040806"}}`.
671
+ `ascii` (**ASCII**, group Print, both scopes, free) rebuilds the picture from characters. Each cell's average tone picks a glyph from a ramp sorted by how much ink each character has (sparse = dark, dense = bright). `cellSize` (6–400 px at 1080p of the item's box, default 14) is the character height; the width follows the font. `charset`: `0` Standard ` .:-=+*#%@` (default) / `1` Detailed (70 characters) / `2` Blocks `░▒▓█` / `3` Binary / `4` Dots / `5` Matrix (half-width katakana) / `6` Custom, which uses `customChars`. `customChars` is a `type: "text"` param: a string of up to 80 characters, de-duplicated and auto-sorted by ink, with a space added for empty cells, so any word works. `font`: `0` Mono / `1` Mono Bold (default) / `2` Sans Bold / `3` Serif. `colorMode`: `0` Source (each glyph in its cell's colour, `colorBoost` 0–100 % pushes it toward full brightness) / `1` Single Color (`glyphColor`) / `2` Gradient (`glyphColor` for dark cells → `lightColor` for bright ones). `background`: `0` Solid (`backgroundColor`, default `#050505`) / `1` Source (the picture behind the glyphs at `sourceLevel` 0–100 %) / `2` None (transparent between glyphs). `contrast` (0–300 %, default 120) and `brightness` (−100…100) shape the tone before glyphs are picked. `invert` `1` maps dark areas to dense glyphs, the dark-ink-on-paper look (pair it with a light `backgroundColor`). `dither` (0–100 %, default 60) breaks up the contour lines a slow gradient would otherwise draw; it is fixed per cell, so video does not shimmer. `edgeLines` (0–100 %, default 0) swaps in `- / | \` along contours, following the edge direction. The character grid is printed on the item's box, so on a regular item it rides the layer; `clip` defaults on (the background is cut to the item's shape). Keyframable: `cellSize`, `colorBoost`, `sourceLevel`, `contrast`, `brightness`, `dither`, `edgeLines`, `mix`; the selects, colours and `customChars` are not. Presets: Terminal, Color, Blocks, Binary, Matrix, Edges, Paper. Example: `{"id": "ascii", "mix": 100, "params": {"cellSize": 14, "charset": 6, "customChars": "GREENLIGHT", "font": 1, "colorMode": 1, "glyphColor": "#39ff7a", "background": 0, "backgroundColor": "#040806"}}`.
312
672
 
313
- #### Mosaic
673
+ ## Mosaic
314
674
 
315
675
  `pixelate` (**Mosaic**, group Display, both scopes, free) rebuilds the item from flat blocks. Use it for every pixelated look: pixel blocks, a mosaic, an 8-bit look, censor blocks, a pixelate transition. `crtMatrix` is a screen texture (CRT, LED and LCD sub-pixels), not pixelation.
316
676
 
@@ -320,7 +680,7 @@ Example, a 2.39:1 letterbox that slides in and out over everything below (on an
320
680
  - `grid`: `0` Layer (default) puts the blocks on the item's own box. They move, turn and tilt with it and are clipped to the box, so the item's edge stays straight; anything an earlier effect drew outside the box passes through unpixelated. `1` Frame fixes the blocks to the frame, and the item moves through them like a low-res screen. A block the item only partly covers averages in the transparency around it, so the edge breaks into soft blocks, and anything an earlier effect drew outside the box (an `echo` trail, a glow) is pixelated too. On an adjustment item both values pixelate everything below on the frame grid.
321
681
  - `offsetX` / `offsetY` (−100…100 % of a block, `+Y` = down) slide the grid. At 0 one block is centred on the box (Layer) or the frame (Frame), so a growing `size` opens from the middle. 100 % is exactly one block, so a key from 0 to 100 loops seamlessly.
322
682
  - Keyframable: `size`, `stretch`, `offsetX`, `offsetY`, `mix`. `sampling` and `grid` are static.
323
- - Leave `clip` unset. The blocks are the new edge, and `clip: true` cuts them back to the smooth silhouette.
683
+ - The blocks are the new edge; `clip: true` cuts them back to the smooth silhouette.
324
684
 
325
685
  Starting points: soft blocks `{"size": 36, "grid": 1}` · censor mosaic `{"size": 22}` · 8-bit `{"size": 10, "sampling": 1, "grid": 1}` · giant blocks `{"size": 120}` · columns `{"size": 30, "stretch": -80, "grid": 1}` · bands `{"size": 26, "stretch": 80, "grid": 1}`. For a pixelate transition, key `fx:<eid>:size` from about 120 down to 1 at the start of the item (in) or from 1 up to 120 at its end (out).
326
686
 
@@ -358,14 +718,14 @@ The soft pixel-blob look turns a blurry colour shape and its trail into blocks.
358
718
  }
359
719
  ```
360
720
 
361
- #### Posterize
721
+ ## Posterize
362
722
 
363
723
  `posterize` (**Posterize**, group Color, both scopes, free) cuts every colour channel down to `level` tones. It is After Effects' Posterize, pixel for pixel, so use it whenever a brief says posterize, poster, screenprint, flat colour bands or fewer colours. Colorama's Constant interpolation maps brightness onto a gradient instead.
364
724
 
365
725
  - `level` (2–255, default 5) is the tones per channel. The bins are equal slices of 0–255 and the tones run evenly from black to white: Level 3 gives 0, 128 and 255; Level 2 gives eight pure colours. Fractions are kept, as in AE: a keyed `level` slides the bands smoothly between whole levels instead of stepping.
366
726
  - `channels`: `0` RGB (default) posterizes each channel on its own, which is AE's look and shifts hues on soft colour. `1` Luminance cuts only the brightness into `level` bands and keeps each pixel's colour, for a cel-shaded or illustrated look.
367
727
  - `dither` (0–100 %, default 9) dissolves the band edges with an ordered (Bayer 8×8) pattern; `0` is exactly AE. The bands stay where AE puts them, so a low value only softens their edges and 100 % turns them into a full retro dither. `ditherSize` (1–8 px, default 1) is the dot size. The pattern is fixed to the item, so it moves with it instead of crawling.
368
- - Alpha is never changed, and a semi-transparent pixel is posterized on its own colour, so soft edges and shadows stay soft. Leave `clip` unset: `clip: true` multiplies semi-transparent pixels by their alpha a second time.
728
+ - Alpha is never changed, and a semi-transparent pixel is posterized on its own colour, so soft edges and shadows stay soft. `clip: true` would multiply semi-transparent pixels by their alpha a second time.
369
729
  - Keyframable: `level`, `dither`, `ditherSize`, `mix`. `channels` is static.
370
730
 
371
731
  Starting points: AE's default `{"level": 7, "dither": 0}` · screenprint `{"level": 3, "dither": 0}` · eight colours `{"level": 2, "dither": 0}` · tone bands `{"level": 5, "channels": 1, "dither": 0}` · retro dither `{"level": 3, "dither": 100, "ditherSize": 2}` · soft contour `{"level": 8, "channels": 1, "dither": 40}`.
@@ -396,28 +756,73 @@ To posterize everything below, put it on an adjustment item (channel order as in
396
756
  }
397
757
  ```
398
758
 
399
- #### Motion Effects II notes
759
+ ## Light Sweep
760
+
761
+ `lightSweep` (**Light Sweep**, group Light, both scopes, free) is After Effects' CC Light Sweep: a straight beam of light across the item, with the edges that face the light catching extra light. Use it for a shine or glint across a logo, a title, a button, a card or a product shot. It needs no keyframes: by default the beam runs across the item on its own and repeats.
762
+
763
+ - AE's controls, with AE's defaults: `direction` (−30°; 0 is a vertical beam, angles turn clockwise), `shape` (`0` Linear, `1` Smooth, `2` Sharp, default), `width` (50 px: the half-width at half strength, so the light ends 2 × `width` from the centre line), `sweepIntensity` (25), `edgeIntensity` (50), `edgeThickness` (4 px; the edge light fades out 2 × that far inside the edge), `lightColor` (`#fffaf0`), `reception` (`0` Add brightens the item, `1` Composite paints the light colour over it, `2` Cutout keeps only the light). Pixel sizes are for a 1080-high frame and scale with the render. Everything stays inside the item's own alpha.
764
+ - `animate`: `1` Sweep (default) moves the beam from fully off one side of the item to fully off the other in `duration` seconds (1.2) on `easing` (an easing name, default `Smooth`), then waits `gap` seconds (1.2) and goes again; `repeat: 0` plays it once at the start of the item (or of the effect's Active Range). `travel`: `0` Forward, `1` Backward, `2` Back and forth. `0` Manual parks the beam at `centerX` / `centerY` (fractions of the item's box, Y from the top; AE's default is `0.5` / `0.25`); key those to move it yourself.
765
+ - Add brightens toward white, so a pure white item shows no sweep; use it on coloured, grey or photographic items, or use Composite with a coloured `lightColor`.
766
+ - Keyframable: `direction`, `width`, `sweepIntensity`, `edgeIntensity`, `edgeThickness`, `centerX`, `centerY`, `mix`. `duration` and `gap` are not.
767
+ - `direction`, `centerX`, `centerY`, `width` and `edgeThickness` carry `"free": true` in capabilities: their value is not limited to min and max. Write a spin as keys past ±180 (`-30` → `-271`), and a pivot off the item as a Center outside 0–1 (a beam through `centerX: 2` at `direction: 85` still crosses the item).
768
+
769
+ Starting points: AE's default `{}` · logo shine `{"shape": 2, "width": 60, "sweepIntensity": 70, "edgeIntensity": 60, "edgeThickness": 3, "lightColor": "#ffffff"}` · soft sheen `{"shape": 1, "width": 120, "sweepIntensity": 35, "edgeIntensity": 20, "edgeThickness": 5}` · thin glint `{"shape": 0, "width": 12, "sweepIntensity": 120}` · warm gold `{"width": 70, "sweepIntensity": 55, "edgeIntensity": 80, "lightColor": "#ffd98a"}` · edges only `{"shape": 1, "width": 120, "sweepIntensity": 10, "edgeIntensity": 120, "edgeThickness": 7}` · one shine on reveal `{"repeat": 0, "duration": 0.9}`.
770
+
771
+ ```json
772
+ { "id": "lightSweep", "eid": "logo-shine", "enabled": true, "mix": 100,
773
+ "params": { "shape": 2, "width": 60, "sweepIntensity": 70, "edgeIntensity": 60, "edgeThickness": 3,
774
+ "lightColor": "#ffffff", "animate": 1, "duration": 1.2, "easing": "Smooth", "repeat": 1, "gap": 2 } }
775
+ ```
776
+
777
+ ## Light Rays
778
+
779
+ `lightRays` (**Light Rays**, group Light, both scopes) in `source: 0` (Alpha) mode makes the layer silhouette emit; on an adjustment item prefer `1` (Luminance) or `2` (Highlights). `originX`/`originY` are in the layer's own 0–1 box and may go outside it (e.g. `originY: 1.2` for light from above). It averages 24–48 taps, so a thin line smeared over a long distance comes out dotted and faint — use `radialFastBlur` (Brightest) for that.
780
+
781
+ ## Long Shadow
400
782
 
401
- Nine of them have non-obvious semantics: `lightRays`, `longShadow`, `outlineBands`, `lensPrism`, `kaleidoscope`, `crtMatrix`, `vectorFieldWarp`, `pixelStretch`, `edgeDetect`. Non-obvious semantics:
783
+ `longShadow` (**Long Shadow / Faux Extrude**, group Graphic) is **layer-scope only** — it paints outside the source silhouette, which an adjustment layer's opaque below-stack has no room for. Its `direction` is authored in the layer's own frame and rotates with it. Do not put it on text unless the user asks for a shadow (SKILL.md (GreenLight Dash only) shared contracts): it reads as a stock default.
402
784
 
403
- - `longShadow` is **layer-scope only** — it paints outside the source silhouette, which an adjustment layer's opaque below-stack has no room for.
404
- - `outlineBands` grows contour bands from a distance field. On a regular layer the default `source: 0` (Alpha) uses the layer silhouette; on an adjustment item alpha is the opaque below-stack, so use `source: 1` (Luminance) or `2` (Edges) there. `params.ramp` is a CSS gradient string colouring the band field near → far.
405
- - `lightRays` in `source: 0` (Alpha) mode makes the layer silhouette emit; on an adjustment item prefer `1` (Luminance) or `2` (Highlights). `originX`/`originY` are in the layer's own 0–1 box and may go outside it (e.g. `originY: 1.2` for light from above). It averages 24–48 taps, so a thin line smeared over a long distance comes out dotted and faint — use `radialFastBlur` (Brightest) for that.
406
- - `pixelStretch` is deliberately a realtime pixel-**stretch** approximation, not an exact sort.
407
- - `edgeDetect` (**Edge Detect**, group Graphic, both scopes) turns the content into a styled edge overlay. `method` picks the detector: `0` Gradient (thick luminous outlines, `operator`/`threshold`/`knee`), `1` Thin Line/Canny (1px lines; `lowRatio` + `linkIter` control hysteresis), `2` Ink Sketch/XDoG (`dogSigma`/`dogK`/`dogTau`/`dogPhi`/`dogEps` — keeps hatching and shading), `3` Contour Bands (`bands` luminance level-sets). There is **no backdrop** — `sourceLevel` (0–100, keyframable) shows the original underneath: `0` is pure edges over transparency in layer scope, `100` is the full original with edges on top (use `blend: 1` Add for the glowing-outline social look). `colorMode`: `0` flat `edgeColor`, `1` source color, `2` hue from each edge's own gradient angle, `3` duo ramp `edgeColor2`→`edgeColor` by luminance. `animMode` drives itself from the clip clock: `1` Draw On and `2` Scanner sweep along `sweepAngle` (layer-space, rotates with the layer) across the clip's duration — the Scanner shows the untouched original ahead of the line; `3` Flicker, `4` Glow Pulse, `5` Line Boil (hand-drawn wobble) run on clip time. `animSpeed` (0.1–4×) scales all of it — the sweeps complete in 1/speed of the clip, the clock modes tick that much faster. Presets: Neon Wire / Outline Pop / Ink Sketch / HUD Scanner / Topographic / Prism Flow / Electric Ghost — all ship with `animMode: 0`; turn animation on deliberately.
408
- - Directions (`longShadow.direction`, prism axis) are authored in the layer's own frame and rotate with it.
785
+ ## Outline Bands
409
786
 
410
- `fastCameraWiggle` (**Fast Camera Wiggle**, group Motion, both scopes) mimics fast handheld camera shake with real shutter motion blur — built for shot cuts. fBm noise drives a screen affine of the plate (offset / roll / zoom pulse), and the blur is exact: `samples` sub-frame transforms across the `shutter` window (degrees at a 30fps reference, up to 720) are averaged in one pass. `amount` is the master (0–2000%; 100% ≈ 10% of frame height of peak excursion) and scales offsets, `rotation` and `zoomPulse` together — for a shot cut, **keyframe `fx:<eid>:amount`**: spike it at the cut frame and decay to 0 over ~10 frames. The default `frequency` (1 Hz) is a slow drift; cut shakes want ~8–17 Hz. `xWeight`/`yWeight` shape the direction (handheld reads best with Y below X), `roughness` is the noise persistence (high = jerkier), `seed` decorrelates instances. `phase`: `0` Trailing (streak behind the motion, default) / `1` Centered. `edges`: `0` Mirror (tiles in the layer's box — the full-frame default), `1` Zoom to Cover (auto punch-in computed from the amplitude bound, capped 4×, so it follows a keyframed amount), `2` Transparent (the isolated plate's real edges). The wiggle is a pure function of the clip-local clock, so scrubbing and renders are deterministic; two instances with the same seed shake identically. Pivot is the layer's own centre (frame centre on an adjustment item), so an off-centre layer shakes in place. It writes outside the silhouette by design; leave `clip` unset. Presets: Subtle Handheld / Fast Cut / Impact / Whip Chaos / Earthquake MAX.
787
+ `outlineBands` (**Animated Outline Bands**, group Graphic, both scopes) grows contour bands from a distance field. On a regular layer the default `source: 0` (Alpha) uses the layer silhouette; on an adjustment item alpha is the opaque below-stack, so use `source: 1` (Luminance) or `2` (Edges) there. `params.ramp` is a CSS gradient string colouring the band field near → far.
411
788
 
412
- `sdfLensBlur` (**SDF Lens Blur**, group Optical, both scopes) defocuses the content under a soft travelling circular lens. `mode: 0` blurs inside the lens; `mode: 1` inverts it (everything defocused *except* the lens region — a focus spotlight). `lensPath` selects `0` Orbit, `1` Figure 8, `2` Sweep, or `3` Manual; `lensX`/`lensY` are the path centre (or the fixed spot with Manual) in the layer's own 0–1 box, so the lens rides a moving layer. `magnify` adds a loupe-style refraction bulge that follows the lens even in Focus mode. `blur` is the maximum defocus in 1080p-reference px; `0` disables the blur passes entirely.
789
+ ## Lens Prism
790
+
791
+ `lensPrism` (**Lens Prism**, group Optical, both scopes). Its `direction` (the prism axis) is authored in the layer's own frame and rotates with it. Leave it out unless the user asks for it (see [Preferred and avoided effects](#preferred-and-avoided-effects)).
792
+
793
+ ## Pixel Stretch
794
+
795
+ `pixelStretch` (**Pixel Stretch**, group Glitch, both scopes) is deliberately a realtime pixel-**stretch** approximation, not an exact sort.
796
+
797
+ ## Edge Detect
798
+
799
+ `edgeDetect` (**Edge Detect**, group Graphic, both scopes) turns the content into a styled edge overlay. `method` picks the detector: `0` Gradient (thick luminous outlines, `operator`/`threshold`/`knee`), `1` Thin Line/Canny (1px lines; `lowRatio` + `linkIter` control hysteresis), `2` Ink Sketch/XDoG (`dogSigma`/`dogK`/`dogTau`/`dogPhi`/`dogEps` — keeps hatching and shading), `3` Contour Bands (`bands` luminance level-sets). There is **no backdrop** — `sourceLevel` (0–100, keyframable) shows the original underneath: `0` is pure edges over transparency in layer scope, `100` is the full original with edges on top (use `blend: 1` Add for the glowing-outline social look). `colorMode`: `0` flat `edgeColor`, `1` source color, `2` hue from each edge's own gradient angle, `3` duo ramp `edgeColor2`→`edgeColor` by luminance. `animMode` drives itself from the clip clock: `1` Draw On and `2` Scanner sweep along `sweepAngle` (layer-space, rotates with the layer) across the clip's duration — the Scanner shows the untouched original ahead of the line; `3` Flicker, `4` Glow Pulse, `5` Line Boil (hand-drawn wobble) run on clip time. `animSpeed` (0.1–4×) scales all of it — the sweeps complete in 1/speed of the clip, the clock modes tick that much faster. Presets: Neon Wire / Outline Pop / Ink Sketch / HUD Scanner / Topographic / Prism Flow / Electric Ghost — all ship with `animMode: 0`; turn animation on deliberately.
800
+
801
+ ## Fast Camera Wiggle
802
+
803
+ `fastCameraWiggle` (**Fast Camera Wiggle**, group Motion, both scopes) mimics fast handheld camera shake with real shutter motion blur — built for shot cuts. fBm noise drives a screen affine of the plate (offset / roll / zoom pulse), and the blur is exact: `samples` sub-frame transforms across the `shutter` window (degrees at a 30fps reference, up to 720) are averaged in one pass. `amount` is the master (0–2000%; 100% ≈ 10% of frame height of peak excursion) and scales offsets, `rotation` and `zoomPulse` together — for a shot cut, **keyframe `fx:<eid>:amount`**: spike it at the cut frame and decay to 0 over ~10 frames. The default `frequency` (1 Hz) is a slow drift; cut shakes want ~8–17 Hz. `xWeight`/`yWeight` shape the direction (handheld reads best with Y below X), `roughness` is the noise persistence (high = jerkier), `seed` decorrelates instances. `phase`: `0` Trailing (streak behind the motion, default) / `1` Centered. `edges`: `0` Mirror (tiles in the layer's box — the full-frame default), `1` Zoom to Cover (auto punch-in computed from the amplitude bound, capped 4×, so it follows a keyframed amount), `2` Transparent (the isolated plate's real edges). The wiggle is a pure function of the clip-local clock, so scrubbing and renders are deterministic; two instances with the same seed shake identically. Pivot is the layer's own centre (frame centre on an adjustment item), so an off-centre layer shakes in place. It writes outside the silhouette by design. Presets: Subtle Handheld / Fast Cut / Impact / Whip Chaos / Earthquake MAX.
804
+
805
+ ## SDF Lens Blur
806
+
807
+ `sdfLensBlur` (**SDF Lens Blur**, group Optical, both scopes) defocuses the content under a soft travelling circular lens. `mode: 0` blurs inside the lens; `mode: 1` inverts it (everything defocused *except* the lens region — a focus spotlight). `lensPath` selects `0` Orbit, `1` Figure 8, `2` Sweep, or `3` Manual; `lensX`/`lensY` are the path centre (or the fixed spot with Manual) in the layer's own 0–1 box, so the lens rides a moving layer. `magnify` adds a loupe-style refraction bulge that follows the lens even in Focus mode. `blur` is the maximum defocus in 1080p-reference px; `0` disables the blur passes entirely. Leave it out unless the user asks for it (see [Preferred and avoided effects](#preferred-and-avoided-effects)).
808
+
809
+ ## Optics Compensation
413
810
 
414
811
  `opticsCompensation` (**Optics Compensation**, group Lens, both scopes) is the lens-angle warp: `fov` is a real Field of View in degrees measured across the dimension chosen by `orientation` (`0` Horizontal, `1` Vertical, `2` Diagonal), and `reverse` picks the direction — `0` adds barrel distortion, `1` removes it. The two are exact inverses, so the same `fov` and `orientation` applied twice with opposite `reverse` returns the original plate; that is the way to undistort footage, warp it, and redistort it. `fov: 0` is a no-op. Adding barrel empties the corners and removing it crops them, so `resize: 1` (Fit) rescales the plate to put the source corner exactly on the frame corner; `zoom` scales on top of that. `centerX`/`centerY` are the view centre in the layer's own 0–1 box and stay a fixed point of the warp. Prefer this over `fisheye`, whose `distortion` is a unitless polynomial coefficient with no inverse.
415
812
 
416
- `warpStyles` (**Warp Styles**, group Distortion, both scopes) is AE's **Warp** effect — one whole-layer shape chosen by `style` (`0` Arc, `1` Arc Upper, `2` Arc Lower, `3` Arch, `4` Bulge, `5` Flag, `6` Wave, `7` Fish, `8` Rise, `9` Inflate, `10` Squeeze, `11` Twist), scaled by `bend` (−100…100 %, signed: negative mirrors the shape). `distortH`/`distortV` (−100…100 %) add the perspective skew of the same AE control and stack on top of any style. All three amounts are keyframable; `style` is a select, so animate the amounts, not the shape. `bend: 0` with both distortions at `0` is an exact bypass. The warp is measured in the layer's own box and rides it, and it deliberately writes **outside** that box (an arc lifts the middle clear of the layer's own top edge), so leave `clip` unset. Only what was inside the box is warped — anything the map pushes off the source is simply not drawn, which is what gives the arced/twisted silhouette its clean edge. Past roughly ±70 % on Inflate, Squeeze and Twist the shape starts folding over itself and the folded region drops out rather than smearing. On an adjustment item the box is the whole frame, so the below-stack warps as one sheet.
813
+ ## Warp Styles
814
+
815
+ `warpStyles` (**Warp Styles**, group Distortion, both scopes) is AE's **Warp** effect — one whole-layer shape chosen by `style` (`0` Arc, `1` Arc Upper, `2` Arc Lower, `3` Arch, `4` Bulge, `5` Flag, `6` Wave, `7` Fish, `8` Rise, `9` Inflate, `10` Squeeze, `11` Twist), scaled by `bend` (−100…100 %, signed: negative mirrors the shape). `distortH`/`distortV` (−100…100 %) add the perspective skew of the same AE control and stack on top of any style. All three amounts are keyframable; `style` is a select, so animate the amounts, not the shape. `bend: 0` with both distortions at `0` is an exact bypass. The warp is measured in the layer's own box and rides it, and it deliberately writes **outside** that box (an arc lifts the middle clear of the layer's own top edge). Only what was inside the box is warped — anything the map pushes off the source is simply not drawn, which is what gives the arced/twisted silhouette its clean edge. Past roughly ±70 % on Inflate, Squeeze and Twist the shape starts folding over itself and the folded region drops out rather than smearing. On an adjustment item the box is the whole frame, so the below-stack warps as one sheet.
816
+
817
+ ## Curves
417
818
 
418
819
  `curves` (**Curves**, group Color, both scopes) applies Photoshop-style tone curves. Its single param `curves` is an object of four channels — `m` (master RGB), `r`, `g`, `b` — each a list of `[x, y]` points in the 0–1 unit box (`x` = input, `y` = output), at least 2 and at most 16 points per channel, sorted by `x`. Identity is `[[0,0],[1,1]]`. Interpolation between points is monotone cubic (no overshoot), values are clamped to 0–1, and the master curve is applied after the per-channel curves. The `curves` param is not keyframable; keyframe the entry's `mix` to animate the grade. Example: `{"id": "curves", "mix": 100, "params": {"curves": {"m": [[0,0],[0.25,0.18],[0.75,0.82],[1,1]], "r": [[0,0],[1,1]], "g": [[0,0],[1,1]], "b": [[0,0],[1,1]]}}}`.
419
820
 
420
- `extract` (**Extract (Histogram Key)**, group Matte, both scopes) keys on a value RANGE of one channel — AE's Extract. `exChannel` picks what is measured (`0` Luminance (Rec.709), `1` R, `2` G, `3` B, `4` Alpha, `5` Max RGB, `6` Saturation) and the pixel survives when that value lies inside `[exBlack, exWhite]`, both ends 0–255 and both INCLUSIVE — the defaults `exBlack: 0` / `exWhite: 255` are an exact bypass. `exBlackSoft` ramps up from `exBlack`, `exWhiteSoft` ramps down to `exWhite`; `exInvert: 1` removes the range instead of keeping it. `exKeepAlpha: 1` (default) multiplies the key into the layer's existing alpha, so a cutout stays a cutout; `0` ignores it. Matte shaping beyond AE: `exGamma` (−100…100) biases the alpha ramp, `exBlur` (px) softens the matte, and `exChoke`/`exEdge` re-threshold it — **choke does nothing at `exBlur: 0`**, because a hard matte has no soft band to slide the pivot through. `exBoost` (Unpremult Boost) lifts thin keyed content (smoke, fire) back toward its full-coverage colour, and `exTintAmount`/`exTint` recolour the result. `exView: 1` shows the matte and `2` the raw channel — both are inspection views, so leave it `0` in delivered projects. The four range params are the ones to keyframe (`fx:<eid>:exBlack` etc.): sweeping `exBlack` 0 → 255 burns the layer away from its darks up, and a narrow `[exBlack, exWhite]` window travelling together is a band scan.
821
+ ## Extract
822
+
823
+ `extract` (**Extract (Histogram Key)**, group Matte, both scopes) keys on a value RANGE of one channel — AE's Extract. `exChannel` picks what is measured (`0` Luminance (Rec.709), `1` R, `2` G, `3` B, `4` Alpha, `5` Max RGB, `6` Saturation) and the pixel survives when that value lies inside `[exBlack, exWhite]`, both ends 0–255 and both INCLUSIVE — the defaults `exBlack: 0` / `exWhite: 255` are an exact bypass. `exBlackSoft` ramps up from `exBlack`, `exWhiteSoft` ramps down to `exWhite`; `exInvert: 1` removes the range instead of keeping it. `exKeepAlpha: 1` (default) multiplies the key into the layer's existing alpha, so a cutout stays a cutout; `0` ignores it. Matte shaping beyond AE: `exGamma` (−100…100) biases the alpha ramp, `exBlur` (px) softens the matte, and `exChoke`/`exEdge` re-threshold it — **choke does nothing at `exBlur: 0`**, because a hard matte has no soft band to slide the pivot through. `exBoost` (Unpremult Boost) lifts thin keyed content (smoke, fire) back toward its full-coverage colour, and `exTintAmt`/`exTint` recolour the result. `exView: 1` shows the matte and `2` the raw channel — both are inspection views, so leave it `0` in delivered projects. The four range params are the ones to keyframe (`fx:<eid>:exBlack` etc.): sweeping `exBlack` 0 → 255 burns the layer away from its darks up, and a narrow `[exBlack, exWhite]` window travelling together is a band scan.
824
+
825
+ ## Neon Glow
421
826
 
422
827
  `neonGlow` (**Neon Glow**, group Glow, **layer scope only**, **PRO** — free to preview, rendering a project that uses it needs a PRO license) runs a neon light (hot core + 1/distance halo) along the LAYER'S OWN OUTLINE, on any visual layer:
423
828
 
@@ -426,11 +831,52 @@ Nine of them have non-obvious semantics: `lightRays`, `longShadow`, `outlineBand
426
831
  - **image** clips: the traced alpha silhouette of the file; fully opaque images fall back to the media content rect (with the item's corner radii).
427
832
  - **video** and other media: the media content rect.
428
833
 
429
- Params: `pathSpeed` (loops/s, negative = reverse), `pathTrimStart`/`pathTrimEnd` (% of the outline), `pathTrail` (% of the trimmed range lit behind the head), `pathCoreSize`/`pathGlowRadius` (px in the layer's own plate), `pathIntensity`, `pathHueCycle` (°/s), `pathSourceLevel`, `pathRelax` (Relax Points 0–100% — bake-side Laplacian smoothing of the baked outline points, per contour: it rounds the pixel-staircase of a traced glyph contour and softens sharp polyline corners, and high values round glyphs into a loose neon wire; NOT keyframable), `pathColor1`. The per-effect `mix` is the master amount, and `start`/`end` give it an Active Range like any chain entry. All number params keyframe as `fx:<eid>:<param>` tracks; **`pathSpeed` and `pathHueCycle` are rates whose tracks are INTEGRATED over clip time** (a ramp accelerates the runner smoothly; an Active Range rebases the phase onto its own start). The lit run is always the **exact baked outline** clipped to the window behind the head — at every trail length, not just 100%. Nothing is resampled, so the light never jumps. Bridges between contours never light and each contour closes onto itself, so on text the light walks glyph by glyph (Trail 100% on a closed outline therefore lights every contour as its own complete neon ring). Legacy `layerEffects` entries with id `neonGlow`/`pathEffects` migrate automatically: the entry moves into `effects`, its `pathAmount` becomes the chain `mix`.
834
+ Params: `pathSpeed` (loops/s, negative = reverse), `pathTrimStart`/`pathTrimEnd` (% of the outline), `pathTrail` (% of the trimmed range lit behind the head), `pathCoreSize`/`pathGlowRadius` (px in the layer's own plate), `pathIntensity`, `pathHueCycle` (°/s), `pathSourceLevel`, `pathRelax` (Relax Points 0–100% — bake-side Laplacian smoothing of the baked outline points, per contour: it rounds the pixel-staircase of a traced glyph contour and softens sharp polyline corners, and high values round glyphs into a loose neon wire; NOT keyframable), `pathColor1`. The per-effect `mix` is the master amount, and `start`/`end` give it an Active Range like any chain entry. All number params keyframe as `fx:<eid>:<param>` tracks; **`pathSpeed` and `pathHueCycle` are rates whose tracks are INTEGRATED over clip time** (a ramp accelerates the runner smoothly; an Active Range rebases the phase onto its own start). The lit run is always the **exact baked outline** clipped to the window behind the head — at every trail length, not just 100%. Nothing is resampled, so the light never jumps. Bridges between contours never light and each contour closes onto itself, so on text the light walks glyph by glyph (Trail 100% on a closed outline therefore lights every contour as its own complete neon ring).
835
+
836
+ ## Lines Draw
430
837
 
431
838
  `linesDraw` (**Lines Draw**, group Glow, **layer scope only**, **PRO**) chases up to eight tapered glowing ribbons along the same per-media outline Neon Glow uses (shape path / traced text or motion contours / image alpha silhouette / media rect). The distinctive behaviour is **speed-driven accumulation**: the head's pace along the outline comes from an eased lap progress (`ldDirection` selects the travel mode — `0` Forward default: every lap re-draws from the path start, `1` Backward, `2` Ping-Pong; `ldProfile`, a select — `0` AE Smooth default, then AE In/Out, Cubic/Natural/Expo/Back in-out, rhythm profiles Dash & Pause / Elastic Stops / Stutter / Heartbeat, Sprint & Drift, Wind-up Blast, Surge, Chaos, Linear), and BOTH the ribbon width and its visible trail length scale with the head's instantaneous speed — trails bloom on sprints and collapse to a dot at stops. `ldLapDur` (s per lap, default 2), `ldLines` (1–8, default 4 — each line carries a fixed ring offset of `ldSpread` px around the head — at a stop every line collapses to its own point — with optional `ldLag` % stagger (default 0) and colours cycling `ldColor1..3`), `ldTrail` (max % of outline), `ldWidth` (max px) with `ldSpeedGain`/`ldMinWidth`/`ldSmooth` (speed→width response and its smoothing window in seconds), `ldTaper` (end taper), `ldRound` (Path Smooth — low-pass outline resampling, % of arc, rounds sharp polyline corners), `ldGlow`/`ldIntensity` (bloom), `ldHeadBright` (whitening toward the head), `ldSourceLevel` (underlying layer opacity). The trail's visible length rides the speed factor too, so it collapses to a dot wherever the head stops. Closed outlines lap continuously; in Ping-Pong mode open outlines bounce and the trail swings behind the head on the return leg. The phase is a pure function of clip time — no simulation state — so range renders and scrubs are exact.
432
839
 
433
- #### Object effects (subject-tracked)
840
+ Leave it out unless the user asks for it (see [Preferred and avoided effects](#preferred-and-avoided-effects)).
841
+
842
+ ## Gooey
843
+
844
+ `gooey` (**Gooey**, group Matte, both scopes, free) is a liquid merge (metaballs). It blurs the group's colour and alpha, then cuts the blurred alpha at `threshold` with a narrow soft edge. Shapes that come close sum their halos above the threshold in the gap, so a liquid bridge grows between them, stretches into a neck as they part, then snaps; corners and thin parts melt round. What "the group" is depends on the item:
845
+
846
+ - **Sequence item** (the usual set-up): the sequence's composite. Put the shapes, dots or cursors in a sequence and add Gooey to the sequence item. Keep the background and any text that must stay sharp on channels outside the sequence, with labels above it.
847
+ - **Adjustment item**: everything below it. The project background is not included, but any layer below is, so an opaque picture or solid under the adjustment item fills the frame. Use a sequence when there is a background layer.
848
+ - **Regular layer**: that layer alone. It rounds a shape's corners and melts thin strokes; text inside the group melts like any shape.
849
+
850
+ | Param | Range | Default | What it does |
851
+ |---|---|---|---|
852
+ | `radius` | 0–200 px at 1080p | 30 | How far shapes reach for each other. At `threshold` 0.5 a bridge holds while the gap is under about 0.7 × radius between straight edges and about 0.45 × radius between round ends. It also sets how much corners melt. `0` is the alpha cut alone (only the anti-aliased edge changes). Keyframable. |
853
+ | `threshold` | 0.05–0.95 | 0.5 | Where the blurred alpha is cut. Lower = fatter shapes and longer bridges; higher = thinner shapes, earlier snaps, small shapes vanish. Keyframable. |
854
+ | `softness` | 0–0.25 | 0.04 | Width of the cut's soft edge, in alpha. 0.02–0.05 reads crisp; more gives a blurry edge. Keyframable. |
855
+ | `colorMode` | `0` / `1` / `2` | `0` | `0` Blend colours (two colours mix inside a bridge), `1` One fill (everything in `fill`), `2` Keep shapes + add bridges (each shape keeps its own pixels where solid; only the melt and the bridges come from the blur — for cursors, icons and logos that must stay recognisable). Static. |
856
+ | `fill` | colour | `#34efab` | The colour for `colorMode: 1`. Static. |
857
+ | `mix` | 0–100 | 100 | Crossfade to the untouched group. Keyframable as `fx:<eid>:mix`. |
858
+
859
+ The blur reads clamp-to-edge, so a shape running off the frame stays solid up to the edge. Cost: two blur passes plus one full-resolution pass. The blur runs at full resolution up to a 12 px radius, half resolution up to 64 px, and one level lower per doubling beyond; the look is the same at every level and a keyed `radius` never steps. Presets: Liquid, Tight, Blobby, Bridges, One Fill.
860
+
861
+ Cursors that merge into a logo as they arrive. This is the sequence item that holds the cursor and logo layers, with the bridges growing over the first 40 %:
862
+
863
+ ```json
864
+ {
865
+ "id": "swarm-item", "clipId": "swarm-seq", "freeStart": 0, "trimStart": 0, "trimEnd": 4,
866
+ "effects": [
867
+ { "id": "gooey", "eid": "goo", "enabled": true, "mix": 100,
868
+ "params": { "radius": 44, "threshold": 0.5, "softness": 0.04, "colorMode": 2, "fill": "#34efab" } }
869
+ ],
870
+ "propertyKeyframes": {
871
+ "fx:goo:radius": [
872
+ { "position": 0, "value": "0", "easing": "AE Smooth" },
873
+ { "position": 40, "value": "44", "easing": "AE Smooth" }
874
+ ]
875
+ }
876
+ }
877
+ ```
878
+
879
+ ## Object effects (subject-tracked)
434
880
 
435
881
  `objectDither` (**Dither Print**), `objectIsolines` (**Field Isolines**) and `objectWindow` (**Portrait Window**), group Object, **layer scope only**. They read a SUBJECT FIELD — a per-effect weight map of where the person/face is — and each entry owns its own.
436
882
 
@@ -452,9 +898,9 @@ The field lives in an optional `track` object **top-level on the entry**, beside
452
898
  - `samples[].t` is **source** seconds (media time, not clip time), so trimming or speed-ramping the clip keeps the track aligned; a still has one sample, a clip is sampled at a few Hz and interpolated.
453
899
  - `fc` is `[x, y, w, h, score]` of the face box in media uv, y from the top.
454
900
  - A malformed field drops the whole `track` on load — and so does ANY bad sample in it, a run-length pair that covers nothing, an odd byte count, more than 200 samples, an unknown `v`, or a track over 900 KB. **Preserve `track` verbatim** when editing an entry: it is detection output, it cannot be reconstructed from the other fields, and the effects still render without it (the field reads as a flat 1.0 — every region control then treats the whole layer as the subject).
455
- - `track.scan` is what that scan WAS — `{ media, feather, faceBoost, from, to }`. The panel compares it with the card's current controls and shows a re-scan hint when they disagree, so carry it with the track; a track without it is only checked on `trackSource`.
901
+ - `track.scan` is what that scan WAS — `{ media, feather, faceBoost, from, to }`. The editor compares it with the entry's current params and marks the scan stale when they disagree, so carry it with the track; a track without it is only checked on `trackSource`.
456
902
 
457
- `params.trackSource` (`0` person silhouette, `1` face, `2` both), `trackFeather` and `trackFaceBoost` describe how the field was built, so they only take effect for a `track` produced with them: changing any of the three marks the stored scan stale in the panel, as does swapping the layer's media or trimming past what the scan covered.
903
+ `params.trackSource` (`0` person silhouette, `1` face, `2` both), `trackFeather` and `trackFaceBoost` describe how the field was built, so they only take effect for a `track` produced with them: changing any of the three marks the stored scan stale in the editor, as does swapping the layer's media or trimming past what the scan covered.
458
904
 
459
905
  The pack has eleven programs in two families. **Fragment programs** shade pixels: `objectDither` (Dither Print), `objectIsolines` (Field Isolines), `objectWindow` (Portrait Window), `objectSlitScan` (Slit Scan), `objectCurtain` (Spectral Curtain), `objectGrid` (Grid System), `objectContour` (Contour Studio). **Geometry programs** draw strokes and type on a Canvas2D plate: `objectNodes` (Node Field), `objectLinks` (Link Graph), `objectFacets` (Facet Plates), `objectMeasure` (Measure Rig).
460
906
 
@@ -468,194 +914,26 @@ The rest of the fragment family: `objectSlitScan` slides strips of the picture
468
914
 
469
915
  Geometry look controls: `objectNodes` — `shape` (0–7, `8` mixed), `size`, `weight`, `fill`, `vary`, `random`, `rotate`, `hero`/`heroScale`/`ring`, plus `labels`/`labelStyle`/`labelSize` (Node Labels is this program's option, not a separate effect). `objectLinks` — `mode` (`0` proximity, `1` Delaunay, `2` edge chain, `3` radial hubs, `4` orthogonal, `5` hybrid), `dist`, `weight`, `conf`. `objectFacets` — `detail`, `maxSide` (drops the Delaunay hull slivers), `fill`, `shade` (`0` flat, `1` facing ramp, `2` monochrome), `edge`, `subject`. `objectMeasure` — `target` (`0` subject bounds, `1` face box, `2` whole layer), `pad`, `corners`, `dims`, `cross`, `readout`, `weight`.
470
916
 
471
- ### Regular-layer Overlay Effects example
472
-
473
- ```json
474
- {
475
- "id": "title-item",
476
- "clipId": "title-clip",
477
- "trimStart": 0,
478
- "trimEnd": 4,
479
- "effects": [
480
- {
481
- "id": "halftone",
482
- "eid": "title-halftone",
483
- "enabled": true,
484
- "mix": 70,
485
- "params": {},
486
- "clip": true
487
- },
488
- {
489
- "id": "glow",
490
- "eid": "title-glow",
491
- "enabled": true,
492
- "mix": 85,
493
- "params": { "glowRadius": 240, "exposure": 1.2, "glowThreshold": 50, "tintAmount": 100, "tintInner": "#69e7ff", "tintOuter": "#69e7ff" },
494
- "clip": false
495
- }
496
- ]
497
- }
498
- ```
499
-
500
- ### Adjustment-layer Overlay Effects example
501
-
502
- An adjustment clip is synthetic. Put its channel after the channels it should affect, but before the camera channel:
503
-
504
- ```json
505
- {
506
- "clips": [
507
- { "id": "adj-clip", "source": "LOCAL", "mediaType": "adjustment", "url": "", "proxyUrl": null, "name": "Look", "duration": 6 }
508
- ],
509
- "channels": [
510
- { "id": "footage", "name": "Footage", "visible": true, "items": ["..."] },
511
- {
512
- "id": "look-channel",
513
- "name": "Look",
514
- "visible": true,
515
- "unsnapped": true,
516
- "items": [{
517
- "id": "look-item",
518
- "clipId": "adj-clip",
519
- "freeStart": 0,
520
- "trimStart": 0,
521
- "trimEnd": 6,
522
- "effects": [
523
- { "id": "vignette", "eid": "look-vignette", "enabled": true, "mix": 65, "params": {} },
524
- { "id": "grain", "eid": "look-grain", "enabled": true, "mix": 35, "params": {} }
525
- ]
526
- }]
527
- }
528
- ]
529
- }
530
- ```
531
-
532
- Channel order defines adjustment reach. `cameraLocked` changes camera behavior, not effect reach.
533
-
534
- ### Overlay Effect keyframes
535
-
536
- Overlay Effects use `fx:<eid>:<parameter>` tracks:
537
-
538
- ```json
539
- {
540
- "propertyKeyframes": {
541
- "fx:title-glow:mix": [
542
- { "position": 0, "value": "0", "easing": "AE Smooth" },
543
- { "position": 25, "value": "100", "easing": "AE Smooth" }
544
- ],
545
- "fx:title-glow:glowRadius": [
546
- { "position": 0, "value": "80", "easing": "Smooth" },
547
- { "position": 100, "value": "440", "easing": "Linear" }
548
- ]
549
- }
550
- }
551
- ```
552
-
553
- Mix and numeric params are keyframable. Colors, gradients, and selects are static. Remove an effect's `fx:<eid>:*` tracks when removing the effect.
554
-
555
- ### Real 3D on 3D model layers
556
-
557
- On a `model3d` item, these Overlay Effects have a REAL 3D version that renders inside the model's own 3D pass — reading its surface normals, its position in its own space and its real depth — instead of filtering the flat picture the pass hands back:
558
-
559
- `rgbSplit`, `pixelate`, `crtMatrix`, `halftone`, `objectDither`, `ascii`, `fractalNoise`, `fill`, `colorama`, `gradientBeam`, `gradientSweep`, `objectContour`, `objectCurtain`, `bloom`, `auraBlur`, `lightRays`, `neonGlow`, `linesDraw`, `edgeDetect`, `flashBlink`, `glitch`, `objectSlitScan`, `pixelStretch`, `shapeRulers`, `curves`.
560
-
561
- - In `capabilities.json` these effects carry `real3dOnModels: true`; 3D-only params and options carry `model3d: true`.
562
- - The 3D version runs only when the entry says `"real3d": true` AND its id is in the list. The editor sets it when one of these is ADDED to a model layer (the picker marks them **3D**); the card's **Render in 3D** toggle switches an entry between the two. Any other effect on a model stays on the flat chain.
563
- - Same `params`, Mix, Active Range and `fx:<eid>:*` keyframes as the flat effect. `clip` and the Object family's tracking params are ignored in 3D — the model IS the subject.
564
- - What changes in 3D, in short: patterns (Mosaic Layer grid, Pixel Matrix, Halftone, ASCII, Dither, Fractal Noise) are locked to the SURFACE and turn with the model; lighting effects (Halftone dot size, Fill's shading, Colorama, Flash) use the model's real light and normals; RGB Split re-films the model from two shifted cameras (real parallax); Bloom thresholds the displayed light but blooms the HDR light; Beam / Sweep / Contour / Glitch / Slit Scan / Pixel Stretch run along the MODEL's own axes; Neon Glow, Lines Draw and Edge Detect trace its real outline, creases and depth steps; Light Rays' origin sits in the model's box and the body occludes its own rays; Shape Rulers draws its real 3D bounding box; Curves applies to the HDR light in tone space. Bloom's Threshold / Soft Knee are judged on the displayed light (same meaning as the flat slider), and what blooms is the HDR light, so a strong highlight glows more than a clipped one.
565
- - 3D-only values (hidden on a flat entry; turning **Render in 3D** off resets them): Colorama `input` `5` Height / `6` Depth / `7` Facing; Mosaic `grid` `2` **Voxels** (the model rebuilt from cubes on a grid in its own space — `size` is the cube size in px, the silhouette turns blocky too); Light Rays `source` `3` **Behind** (a light disc behind the body, rays escape round its silhouette); Flash Blink `lightAngle` (0–360°, default 135 — the side of the body the flash lands on).
566
- - Light that leaves the silhouette (halos, streaks, the ruler box, RGB Split's copies) is composited at the model's centre depth, so it is occluded by — and occludes — other 3D layers like the model itself.
567
- - Cost: nothing until a model carries one of these. The passes and their buffers load and allocate on first use.
568
-
569
- ### Installed effects (`local:<id>`)
570
-
571
- Effect packages an agent built or the user imported (custom-effects skill (GreenLight Dash only))
572
- apply exactly like the registry effects above, in the same `effects` list and both scopes:
573
-
574
- ```json
575
- { "id": "local:chromatic-drift", "eid": "fx-drift-1", "enabled": true, "mix": 100,
576
- "params": { "amount": 6, "angle": 0, "speed": 0.5, "tint": "#ffffff", "tintMix": 0 } }
577
- ```
578
-
579
- - Discover them in `GET /api/video-editor/capabilities` → `effects.local`: each entry carries its
580
- params (keys, ranges, defaults, which are keyframable), `scopes`, the `renderer` it was written
581
- for and `installation.status`. Number params animate with `fx:<eid>:<param>` tracks, like any
582
- effect.
583
- - Use one whose `installation.status.state` is `ok`. `error` means it does not draw (the message
584
- says why); `unchecked` means no open editor has checked it yet.
585
- - An installed effect draws on the engines it was written for: `renderer` is `webgl`, `webgpu` or
586
- `both`, against `GET /api/video-editor/engine`. On another engine it is skipped.
587
- - An entry whose package is not installed on this machine is kept in the project as stored and
588
- skipped, so a project moves between machines without losing it; installing the package (or a
589
- bundle restore or web sync that carries it) brings it back. A plain board ZIP import installs no
590
- packages.
591
-
592
- ### Custom Overlay Effect (GLSL)
593
-
594
- ```json
595
- {
596
- "id": "custom",
597
- "eid": "custom-wave",
598
- "enabled": true,
599
- "mix": 100,
600
- "params": {},
601
- "code": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n uv.x += sin(uv.y * 40.0 + iTime * 3.0) * 0.01;\n fragColor = texture2D(iChannel0, uv);\n}"
602
- }
603
- ```
604
-
605
- - `code` is **GLSL**, Shadertoy-shaped: define `void mainImage(out vec4 fragColor, in vec2 fragCoord)`.
606
- The framework wraps it, seeds `fragColor` with the source pixel and applies Mix afterwards, so do
607
- not blend against the source yourself. The shader is compiled when the effect is created, and a
608
- body with no `mainImage` is rejected there.
609
- - **Classic renderer only.** The body is compiled at runtime and no WebGPU renderer compiles GLSL, so
610
- when the app's engine is WebGPU (`GET /api/video-editor/engine`) the effect is skipped with a reason
611
- rather than drawn.
612
- The app also no longer offers it as a new effect: reach for the registry above, or build an
613
- installed effect (below), and treat this section as the contract for the entries existing
614
- projects already carry.
615
- - Shadertoy names in scope: `iChannel0` (the isolated layer, or the below-stack composite on an
616
- adjustment layer), `iResolution` (vec3), `iTime`, `iProgress`, plus `iTimeDelta`, `iFrameRate`,
617
- `iFrame`, `iMouse` (always zero), `iDate`, `iChannelResolution[4]` and `iChannelTime[4]` for
618
- compatibility. There is no `iChannel1..3` — there is only one bound texture.
619
- - The app's own uniforms, same values: `uTexture`, `uResolution` (vec2), `uTime`, `uProgress`, `uMix`,
620
- `uQuality`, `uParams[28]`, `uColor`, `uColors[5]`.
621
- - `fxLocal(uv)`, `fxFrame(p)`, `fxDir(v)`, `fxRes()`, `fxAspect()`, `fxCoord(uv)` — geometry in the
622
- MOVING layer box. Without these an effect centres itself on the frame, so it stands still while the
623
- shape slides through it. On adjustment layers they reduce to frame space.
624
- - Redeclaring any of those names in your own code is safe — the duplicate declaration is dropped from
625
- the prelude, which is what lets a shader pasted from elsewhere compile unchanged.
626
- - `iTime`/`uTime` is clip-local seconds and restarts at 0 inside an active range (`start`/`end`).
627
- - Keep output premultiplied-alpha safe near transparent edges.
628
-
629
- ## Layer Effects (`item.layerEffects`)
630
-
631
- Layer Effects are an array of typed entries on regular visual items. Current ids:
632
-
633
- - `runningGlow` — any visual layer; PRO-gated at render.
634
- The three below are authored from the inspector's **DEPTH EFFECTS** section, not the Layer Effects picker — they share one depth map and that section owns it. They are still ordinary `layerEffects` entries in the data.
635
-
636
- - `depthMap` — the depth SOURCE, image AND video layers. Renders nothing itself. Created by the section's **Analyse depth** button, not added as an effect. On video the map is FROZEN from one frame (the playhead's) and held for the whole clip.
637
- - `depth3d` — image and video layers; 2.5D parallax driven by the project camera, reading the shared depth map.
638
- - `depthScan` — image and video layers; a LiDAR-style slice through the shared depth map. Also `model3d` layers, where it slices EXACT depth and needs no map (`scnSpace`, below). FREE.
639
- - `fresnelRim` — Fresnel Rim: light that hugs the subject's edges, read from its shape. `model3d` layers (the real surface rim, no map needed) and image/video layers (edges of the shared depth map, swept like Depth Scan). FREE.
640
- - `motionBlur` — any visual layer except 3D models; smears the layer along its own transform motion.
641
- - `motionTrail` — the same layer drawn again at its own past poses (or its past FRAMES); any visual layer except 3D models and camera-baked 3D plates. PRO-gated at render.
642
- - `autoFill` — any visual layer (real 3D on models); geodesic "infection" fill. PRO-gated at render.
643
- - `rig` — Rig (Puppet Pin) mesh warp on video/image/text/shape/motion layers; PRO-gated at render.
644
- - `repetile` — repeats the layer into a grid of tiles with per-tile stagger; any visual layer except 3D models and camera-baked 3D plates.
645
- - `regionDistort` — rescales only what is inside a rectangular band, the rest of the layer slides but keeps its shape; any visual layer except 3D models and camera-baked 3D plates.
646
- - `bend` — AE's CC Bend It: rigid outside two axis handles, an arc between them; same layer scope as `regionDistort`.
647
- - `cornerPin` — the layer box mapped onto an arbitrary quad by a projective homography; same layer scope as `regionDistort`. **Was an OVERLAY effect in 9.6.32 only** — an `item.effects` entry with this id migrates into `layerEffects` on load.
648
- - `meshWarp` — a Catmull-Rom surface over a draggable lattice of up to 5x5 vertices; same layer scope as `regionDistort`.
649
- - `bezierWarp` — four cubic edges driving a Coons patch, so the silhouette CURVES instead of kinking at a vertex; same layer scope as `regionDistort`.
650
- - `droste` — the layer recursed INTO ITSELF (Escher's Print Gallery map); same layer scope as `regionDistort`. PRO-gated at render.
651
- - `glass` — the shape becomes a pane of GLASS that refracts, frosts, tints and shadows the layers BEHIND it; `rectangle` / `ellipse` / `circle` shape layers only, FREE. It runs before the shape's own paint, so its order among the other entries does not change it.
652
- - `chromaKey` — removes a backdrop of ANY colour (green, blue, red, white, black, grey) and makes the matte the layer's shape; video/image/sequence/grid layers, FREE.
653
- - `mockupFrame` — draws a device chassis AROUND the layer and re-seats the layer's pixels in its screen; any visual layer except 3D models and camera-baked 3D plates. FREE.
654
- - `layerMask` — one or more animatable vector masks that cut the layer's OWN pixels; video/image/text/shape/motion layers, FREE. On an **`adjustment` item** the same entry scopes the grade/effect chain instead: inside the masks the stack below is graded, outside it shows through untouched.
655
-
656
- There can be at most one entry of each id. Each entry has a stable `eid`, an `enabled` flag, and a `params` object.
657
-
658
- ### Running Glow entry
917
+ # GreenLight Dash — Layer Effects reference
918
+
919
+ > Part of the `greenlight-dash` skill. Read [video-effects.md](#greenlight-dash--video-editor-layer-effects-and-overlay-effects) first for the storage contract and the id index; read [video-keyframes.md](video-keyframes.md) when animating effects.
920
+
921
+ ## Contents
922
+
923
+ - [Running Glow entry](#running-glow-entry)
924
+ - [Motion Blur entry](#motion-blur-entry)
925
+ - [Posterize Time entry](#posterize-time-entry)
926
+ - [Motion Trail entry](#motion-trail-entry)
927
+ - [Grow Fill entry](#grow-fill-entry)
928
+ - [Rig (Puppet) entry](#rig-puppet-entry)
929
+ - [Repetile entry](#repetile-entry)
930
+ - [Glass entry](#glass-entry)
931
+ - [Chroma Key entry](#chroma-key-entry)
932
+ - [Mockup Frame entry](#mockup-frame-entry)
933
+
934
+ Layer Effects live in `item.layerEffects`: at most one entry per id, each with a stable `eid`, an `enabled` flag and a `params` object. Which layers each id works on, and which need PRO, is in the [Layer Effects index](#layer-effects-index). Warps (Region Distort, Bend, Corner Pin, Mesh / Bezier Warp, Droste, Pryzm) are in [effects-warps.md](#greenlight-dash--warp-layer-effects), the depth effects in [effects-depth.md](#greenlight-dash--depth-effects-and-real-3d-on-models) and Layer Mask in [masks.md](#greenlight-dash--layer-masks).
935
+
936
+ ## Running Glow entry
659
937
 
660
938
  ```json
661
939
  {
@@ -687,141 +965,9 @@ For outline text, hairlines, or open paths set `glowTraceThin: 1` and choose `gl
687
965
 
688
966
  A text background bar (`textBgEnabled`), caption-bar border (`textBorderEnabled`), layer border or shadow can make the layer's silhouette a rectangle. Remove those decorations when the intended glow should trace the glyphs; keep them when it should trace a panel.
689
967
 
690
- Works on `model3d` layers too — it traces the alpha contour of the texture the 3D pass hands back, exactly as it does for any other layer. (Capability data published before v9.0.6 omitted `model3d` from this effect's media types; that was wrong, and the editor never enforced it.)
691
-
692
- ### Build Depth Map entry
693
-
694
- The depth SOURCE. Several effects sample the same estimate, so one entry owns it
695
- rather than each carrying a copy of the same megabyte. In the editor this is the
696
- **DEPTH EFFECTS** section's own Analyse / re-analyse button rather than a row in
697
- any picker — an effect you can add that then does nothing until you run an
698
- analysis is a worse affordance than a button that says so.
699
-
700
- ```json
701
- {
702
- "id": "depthMap",
703
- "eid": "layer-depthmap-1",
704
- "enabled": true,
705
- "params": { "dmInvert": 0 },
706
- "depthMap": {
707
- "w": 384,
708
- "h": 216,
709
- "data": "data:image/png;base64,...",
710
- "t": 2.4
711
- }
712
- }
713
- ```
714
-
715
- - `depthMap` rides TOP-LEVEL on the entry (not inside `params`), so the project stays self-contained.
716
- - `data` MUST start with `data:image/png;base64,` and `w`/`h` must be ≥ 2, or the map is dropped on load.
717
- - `t` (video only, optional) — the SOURCE second the map was measured at. One map serves the whole clip; `t` only records which frame it describes, so the panel can name it and flag when the playhead has moved elsewhere. Images carry no `t`.
718
- - `dmInvert` 0 = white is near (what the estimator produces), 1 = white is far. Every Depth effect on the layer obeys it, so they can never disagree about which end of the scene is close.
719
- - This entry renders nothing on its own; `depth3d` and `depthScan` render nothing WITHOUT it. Write it alongside them.
720
- - Do not invent a depth map. Generate it through the editor's **DEPTH EFFECTS ▸ Analyse depth** flow — the section opens on its **Add Depth Effect** picker, and the Analyse / Load block appears once a depth effect is on the layer (or a map already exists). On video, park the playhead on the frame to measure first, since the button reads that frame.
721
- - **Projects written before this entry existed** keep their map on the `depth3d` entry and still work; the editor adopts it into the shared entry when Build Depth Map is added.
722
-
723
- ### 3D Depth entry
724
-
725
- ```json
726
- {
727
- "id": "depth3d",
728
- "eid": "layer-depth-1",
729
- "enabled": true,
730
- "params": {
731
- "depthStrength": 100,
732
- "depthFocus": 45
733
- }
734
- }
735
- ```
736
-
737
- - Reads the `depthMap` entry's map. Renders nothing without one.
738
- - Accepts the Overlay Effects **Active Range** — optional top-level `start`/`end` in clip-local seconds. Outside the window the strength is 0 (an exact bypass). It is a HARD edge; keyframe `depthStrength` for a gentle arrival. The timeline draws it as a coloured line under the clip, like a windowed Overlay Effect.
739
- - `depthStrength` is 0–300%; `depthFocus` is 0–100%.
740
- - Keyframe them with `propertyKeyframes.depthStrength` and `propertyKeyframes.depthFocus`.
741
- - The effect uses the evaluated project-camera pose, including Camera Motion Paths, pan, tilt, and dolly. Lens-only `camZoom` intentionally produces no parallax.
742
- - A `cameraLocked` channel does not receive camera-driven depth displacement.
743
-
744
- ### Depth Scan entry
745
-
746
- A narrow slice THROUGH DEPTH, not a screen wipe: pixels whose depth is within
747
- `scnWidth` of the moving threshold light up, so the contour crawls over the
748
- subject's surface instead of marching across the frame.
749
-
750
- ```json
751
- {
752
- "id": "depthScan",
753
- "eid": "layer-scan-1",
754
- "enabled": true,
755
- "params": {
756
- "scnSpeed": 83.33,
757
- "scnPhase": 4,
758
- "scnEase": 0,
759
- "scnWidth": 1.1,
760
- "scnSplit": 2.4,
761
- "scnFeather": 0.35,
762
- "scnGrain": 22,
763
- "scnPixel": 1,
764
- "scnColorMode": 0,
765
- "scnColor": "#b8ff52",
766
- "scnRamp": "linear-gradient(90deg, #001b2e 0%, #0aa6b8 40%, #b8ff52 70%, #ffffff 100%)",
767
- "scnGamma": 1,
768
- "scnLow": 1.5,
769
- "scnHigh": 99.5,
770
- "scnBorder": 100,
771
- "scnBrightness": 125,
772
- "scnSource": 0,
773
- "scnAmount": 100,
774
- "scnView": 0
775
- }
776
- }
777
- ```
778
-
779
- - Reads the `depthMap` entry's map. Renders nothing without one.
780
- - Accepts the Overlay Effects **Active Range** — optional top-level `start`/`end` in clip-local seconds (NOT inside `params`), drawn on the timeline like a windowed Overlay Effect. Outside the window the pass is skipped entirely. Inside it the SWEEP CLOCK IS REBASED, so the slice starts at `scnPhase` wherever the window sits rather than being caught mid-travel; keyframe tracks stay on the clip clock.
781
- - `scnColorMode` picks how the slice is coloured:
782
- - `0` **RGB Split** (default) — three slices at `phase − split` / `phase` / `phase + split`, which reads as chromatic fringing on the contour.
783
- - `1` **Flat Color** — one slice in `scnColor`. `scnSplit` is ignored.
784
- - `2` **Gradient** — one slice widened to `width + split`, with `scnRamp` laid across it far-to-near. `scnSplit` still means "how far the colours spread through depth".
785
- - `scnColor` is a hex string; `scnRamp` is a CSS `linear-gradient(...)` string, same form the gradient picker reads and writes.
786
- - The sweep is a pure function of clip time: `phase = scnPhase + scnSpeed × seconds`, wrapped to 0–1. `scnEase: 1` (Hold Ends) applies a closed-form remap so it crawls at the extremes.
787
- - `scnBorder` (0–100%) rejects the depth CLIFF at the subject's silhouette. At 0 the silhouette becomes a permanent outline that never moves, which reads as a filter rather than a scan.
788
- - `scnView` `1` (Depth Map) and `2` (Original) are diagnostics that **render and export exactly as shown** — set it back to `0` before exporting.
789
- - `scnAmount: 0` is a byte-exact bypass.
790
- - Keyframable as same-name `propertyKeyframes` tracks: `scnPhase`, `scnWidth`, `scnSplit`, `scnFeather`, `scnGrain`, `scnGamma`, `scnLow`, `scnHigh`, `scnBorder`, `scnBrightness`, `scnSource`, `scnAmount`. `scnSpeed` is deliberately static (phase is `start + speed × t`, so an animated rate teleports the slice), as are `scnPixel` and the enums.
791
- - **On a `model3d` layer** no `depthMap` entry is needed: the slice runs through the model's EXACT depth inside its 3D pass. `scnSpace` picks the axis — `0` Camera depth (distance from the camera across the model's own depth range, default), `1` / `2` / `3` a plane sweeping along the model's own X / Y / Z, so the line runs over the body however it is posed. `scnPixel`, `scnBorder` and `scnView` do nothing there.
792
-
793
- ### Fresnel Rim entry
968
+ Works on `model3d` layers too — it traces the alpha contour of the texture the 3D pass hands back, exactly as it does for any other layer.
794
969
 
795
- Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
796
-
797
- ```json
798
- {
799
- "id": "fresnelRim",
800
- "eid": "layer-rim-1",
801
- "enabled": true,
802
- "params": {
803
- "frColorA": "#ff4fd8",
804
- "frColorB": "#7b3cff",
805
- "frIntensity": 5.2,
806
- "frWidth": 42,
807
- "frSoftness": 40,
808
- "frHalo": 1.2,
809
- "frBody": 25,
810
- "frPulse": 0,
811
- "frDirMix": 0,
812
- "frAngle": 0,
813
- "frAmount": 100
814
- }
815
- }
816
- ```
817
-
818
- - **3D model:** the real surface rim (1 − n·v from the model's own normals) inside its 3D pass, with its own even halo. It turns with the model and lights edges inside the outline too. No depth map. Fields: `frWidth` (rim width), `frSoftness` (hard band → raw falloff), `frBody` (0–100 %, the model's own light under the rim — 100 leaves it untouched; the presets dim it so the rim reads).
819
- - **Image / video:** needs the `depthMap` entry. Only EDGES glow — wherever depth falls away behind a pixel — fading inward over `frRimPx` (px of a 1080-tall frame); `frEdgeMin` is the smallest depth drop that counts. Like Depth Scan, a window (`frWindow` ± `frFeather`) around `frPhase` picks WHICH depth's edges light, swept by `frSpeed` (%/s) with `frEase` (0 Linear, 1 Hold Ends); `frGamma`, `frLow`, `frHigh` shape the depth; `frBgDim` (0–100 %) dims what is not lit.
820
- - Both: `frColorA` edge colour, `frColorB` falloff colour, `frIntensity` (0–6), `frHalo` (0–3, 0 skips the halo pass), `frPulse` %, `frDirMix` % with `frAngle` ° keep the rim to one side, `frAmount` (0 is an exact bypass).
821
- - Presets (the inspector's): Backlight, Neon Edge, Ember, Glacier.
822
- - Accepts the Active Range (top-level `start`/`end`). Keyframable: `frIntensity`, `frWidth`, `frHalo`, `frBody`, `frDirMix`, `frAngle`, `frPhase`, `frRimPx`, `frWindow`, `frAmount`.
823
-
824
- ### Motion Blur entry
970
+ ## Motion Blur entry
825
971
 
826
972
  ```json
827
973
  {
@@ -847,8 +993,9 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
847
993
  - `mblurSamples` is used verbatim at render quality (halved during interactive playback only).
848
994
  - Keyframe with `propertyKeyframes.mblurShutter`, `propertyKeyframes.mblurAmount`, and (velocity mode) `propertyKeyframes.mblurMaxBlur` / `propertyKeyframes.mblurFalloff`. `mblurSamples` is not keyframable — an animated tap count steps the kernel per integer.
849
995
  - It cannot see motion INSIDE the clip's pixels (a person walking through a static video stays sharp) and does nothing on `model3d` layers.
996
+ - On a layer inside a sequence it runs as at the top level, both modes; in a grid cell it is skipped, since the cell layout replaces the layer's own motion ([Inside a sequence](#preferred-and-avoided-effects)).
850
997
 
851
- ### Posterize Time entry
998
+ ## Posterize Time entry
852
999
 
853
1000
  ```json
854
1001
  { "id": "posterizeTime", "eid": "layer-posterize", "enabled": true, "params": { "ptFps": 12 } }
@@ -865,7 +1012,7 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
865
1012
  up smoothly and time never steps backwards.
866
1013
  - Costs nothing per frame beyond reading the time; a layer without it pays one array scan.
867
1014
 
868
- ### Motion Trail entry
1015
+ ## Motion Trail entry
869
1016
 
870
1017
  ```json
871
1018
  {
@@ -896,16 +1043,17 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
896
1043
  - `0` **Frame Echo** replays a ring of past PLATES, so it is the only method that carries motion from INSIDE the footage — a video, a talking head, per-glyph text animation.
897
1044
  - `1` **Trail Buffer** (default) keeps one accumulator and erodes it `trailTaper` px per frame, for a continuous comet.
898
1045
  - `2` **Transform Repeats** re-draws the CURRENT plate at past poses with no history at all, so it is exact at any instant and free to scrub.
899
- - Frame Echo and Transform Repeats share `trailCount` (repeats, max 24) / `trailSpacing` / `trailDecay` and the drift set (`trailScale`, `trailRotate`, `trailOffsetX/Y`); Trail Buffer ignores those and uses `trailPersist` / `trailTaper` instead. Writing a value into the wrong half is silently inert, so set `trailMethod` first.
1046
+ - Frame Echo and Transform Repeats share `trailCount` (repeats, max 32; not keyframable) / `trailSpacing` / `trailDecay` and the drift set (`trailScale`, `trailRotate`, `trailOffsetX/Y`); Trail Buffer ignores those and uses `trailPersist` / `trailTaper` instead. Writing a value into the wrong half is silently inert, so set `trailMethod` first.
900
1047
  - `trailSpacing` is counted in 30fps timeline frames and may be fractional: small values give a continuous smear, large ones stepped ghosts.
901
1048
  - `trailScale` is a SIGNED per-repeat delta about the layer's own centre — `0` neutral, negative tapers to a comet, positive grows the tail.
902
1049
  - `trailBlend`: `0` Behind (trail under the layer) / `1` Add / `2` Screen / `3` Maximum. `trailColorize` (%) ramps the stack toward `trailTint`.
903
1050
  - Every method carries the FINISHED layer look (glow, defocus, the overlay chain, tiles) and follows the real motion path, parenting and follow rigs included.
904
1051
  - Frame Echo and Trail Buffer are temporal: a seek rebuilds them from reprojected poses, so a scrubbed frame is usable but can differ slightly from a sequential export on footage whose pixels move internally.
905
- - Every number keyframes as a same-name `propertyKeyframes` track; the two selects do not.
906
- - Compared with the `echo` OVERLAY effect: Motion Trail styles ONE layer's own pixels at a fixed point in that layer's pipeline; `echo` is a chain entry, so it can be ordered among other Overlay Effects, given an Active Range, and — on an adjustment item — applied to the whole composited stack below it.
1052
+ - Every number except `trailCount` keyframes as a same-name `propertyKeyframes` track; the selects do not.
1053
+ - On a layer inside a sequence it runs as at the top level, all three methods; in a grid cell it is skipped, since the cell layout replaces the layer's own motion ([Inside a sequence](#preferred-and-avoided-effects)).
1054
+ - Motion Trail or the `echo` Overlay Effect: see [Echo or Motion Trail?](#echo).
907
1055
 
908
- ### Grow Fill entry
1056
+ ## Grow Fill entry
909
1057
 
910
1058
  ```json
911
1059
  {
@@ -924,14 +1072,14 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
924
1072
  }
925
1073
  ```
926
1074
 
927
- - **Grow Fill** (id `autoFill`, PRO at render — free to preview) grows an "infection" fill through the layer's own alpha from infection points — the AutoFill-2-style reveal. The growth is GEODESIC: a CPU-baked arrival-time field travels through letterforms and around holes instead of wiping across them; **Bridges** (`afBridges`/`afBridgeReach`/`afBridgeSlow`) let it jump between disconnected letters with a time toll while paint still clips to the true alpha. Works on any visual layer. **On `model3d` layers the fill is REAL 3D**: the arrival field is baked geodesically over the mesh surface (welded across seams and touching parts) and rendered by injected materials, so the front creeps around the geometry and respects occlusion; manual seeds live in the entry's `points3d` array (`{x, y, z, delay}` in the model's root-local space, on the mesh surface). Speed-map Shape Flow/Combine degrade to Uniform/Turbulent on models; front/back edge px sliders map onto the normalised surface span.
1075
+ - **Grow Fill** (id `autoFill`, PRO at render — free to preview) grows an "infection" fill through the layer's own alpha from infection points — the AutoFill-2-style reveal. The growth is GEODESIC: a CPU-baked arrival-time field travels through letterforms and around holes instead of wiping across them; **Bridges** (`afBridges`/`afBridgeReach`/`afBridgeSlow`) let it jump between disconnected letters with a time toll while paint still clips to the true alpha. Works on any visual layer. **On `model3d` layers the fill is REAL 3D**: the arrival field is baked geodesically over the mesh surface (welded across seams and touching parts) and rendered by injected materials, so the front creeps around the geometry and respects occlusion; manual seeds live in the entry's `points3d` array (`{x, y, z, delay}` in the model's root-local space, on the mesh surface). Speed-map Shape Flow/Combine degrade to Uniform/Turbulent on models; front/back edge px values map onto the normalised surface span.
928
1076
  - **`afProgress` (0–100 %) is the animation driver** — keyframe `propertyKeyframes.afProgress` 0→100 to play the fill. `afSpeed` also takes a track, and it is a RATE whose track is INTEGRATED over clip time (a ramp accelerates the front smoothly). The render-side shaping params keyframe too — `afSoftness`, `afWavAmp`, `afWavScale`, `afFeWidth`, `afFeGlowW`, `afFeGlowI`, `afBeOffset`, `afBeWidth`, `afBeGlowW`, `afBeGlowI` — they are plain shader uniforms and never touch the baked field. Bake-side params (seeds, speed map, bridges) deliberately are not keyframable: a keyed one would force a ~30 ms CPU rebake every frame.
929
1077
  - `afMode`: 0 Fill (paint over the layer), 1 Unfill (burn it away), 2 Reveal (fill as a matte). `afStyle`: 0 Solid (`afFillColor`) / 1 Gradient by Age (`afRamp` colours by time-since-arrival) / 2 Gradient Loop (`afRamp` + `afLoops`, `afFlow` — travelling wave bands) / 3 Noise Texture / 4 None (edges only).
930
1078
  - Front shaping: `afSoftness`, `afWavAmp`/`afWavScale`. **Front Edge**: `afFeWidth` crisp band, `afFeColorMode` 0 Solid (`afFeColor`) / 1 Gradient (`afFeRamp` across the band), `afFeGlowW`/`afFeGlowI` additive glow. **Back Edge** (`afBeEnable` 1): trailing band `afBeOffset` px behind the front with `afBeWidth`/`afBeColorMode`/`afBeColor`/`afBeRamp`/`afBeGlowW`/`afBeGlowI`.
931
1079
  - **Infection points**: `afSeedMode` 0 Left / 1 Center / 2 Right / 3 Top / 4 Bottom / 5 Scatter (procedural, with `afSeedCount`/`afSeedSeed`/`afSeedStagger`) or **6 Manual** — the entry's top-level `points` array (`{x, y, delay}`, layer-box 0..1 with y down, delay 0..1 staggering that point's start; max 32). Speed maps: `afSpeedType` 0 Uniform / 1 Turbulent Noise (`afNoiseScale`/`afNoiseContrast`/`afNoiseSeed`) / 2 Shape Flow (`afContourBias`) / 3 Combine.
932
- - 14 presets (Ink, Glow Sweep, Electric, Fire, Ocean Wave, Neon Gradient, Neon Edge, Scan Sweep, Echo, Magic Mist, Handwriting, Frost, Burn Out, Disintegrate). Glow is additive — over white content it reads best in Reveal/Unfill modes or over a dark fill. Look-dev source of truth: `html/finished/autofill-lab.html`.
1080
+ - 14 presets (Ink, Glow Sweep, Electric, Fire, Ocean Wave, Neon Gradient, Neon Edge, Scan Sweep, Echo, Magic Mist, Handwriting, Frost, Burn Out, Disintegrate). Glow is additive — over white content it reads best in Reveal/Unfill modes or over a dark fill.
933
1081
 
934
- ### Rig (Puppet) entry
1082
+ ## Rig (Puppet) entry
935
1083
 
936
1084
  ```json
937
1085
  {
@@ -959,12 +1107,12 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
959
1107
  - **Rig (Puppet)** (id `rig`, PRO at render) is the After Effects Puppet Pin analog: a rigid moving-least-squares mesh warp of the layer's projected plate. Every pin both POSES and HOLDS its region — a pin you never animate is an anchor. Works on `video`, `image`, `text`, `shape`, and `motion` layers (not models, sequences, grids).
960
1108
  - **Pins live top-level on the entry** in unit-box coords (±0.5 of the layer box, y down — the `shapePath` convention), so they ride the artwork through move/scale/rotate, parenting, keyframes, and the project camera. `pid` must match `^[A-Za-z0-9_-]{1,24}$`, one entry per pid, max 64 pins.
961
1109
  - Pin `type`: `position` (moves), `advanced` (moves + rotate/scale ring), `bend` (rotate/scale about a HELD point), `starch` (stiffens a region — `amount` 0–100 %, `extent` in % of the box height, `hardness` = the fully-rigid plateau fraction).
962
- - **Pose animates via `rig:<pid>:<chan>` keyframe tracks**, never by editing pin rest coords: channels are `x`/`y` (OFFSETS from rest in % of the layer box; 0 = at rest), `rot` (degrees), `scl` (%, 100 = neutral), `amount` (%; starch only). Channels valid per type: position `x,y`; advanced `x,y,rot,scl`; bend `rot,scl`; starch `amount`. Tracks for a deleted pin are dropped on load. "Reset pose" = delete the `rig:` tracks.
1110
+ - **Pose animates via `rig:<pid>:<chan>` keyframe tracks**, never by editing pin rest coords: channels are `x`/`y` (OFFSETS from rest in % of the layer box; 0 = at rest), `rot` (degrees), `scl` (%, 100 = neutral), `amount` (%; starch only). Channels valid per type: position `x,y`; advanced `x,y,rot,scl`; bend `rot,scl`; starch `amount`. Tracks for a deleted pin are dropped on load. To reset the pose, delete the `rig:` tracks.
963
1111
  - Params: `rigDensity` = mesh cells across the box's longer side (8–48), `rigExpansion` dilates the alpha-clipped grid, `rigWeights` 0 Geodesic (disconnected islands stay independent — two legs don't drag each other) / 1 Euclidean, `rigStiffness` = weight falloff exponent (higher = more local).
964
- - Every pin is animated from birth (AE-style): it carries `rig:` tracks from the moment it exists. Look-dev sources of truth: `html/rig-tool-lab-v2.html` and `html/rig-tool-app-mock.html`.
1112
+ - Every pin is animated from birth (AE-style): it carries `rig:` tracks from the moment it exists.
965
1113
  - **IK limbs** (`chains`): `{ cid, joints: [rootPid, elbowPid, endPid], bend: ±1, preserveTex, fleshR }` — max 8, each pin in at most one limb, joints must be existing non-bend/non-starch pins (chains referencing missing pins dissolve on load). Limbs are pure AUTHORING assists: a 2-bone solve runs only while a member pin is being posed and writes the joints' `rig:` tracks — playback and export never run a solver. Bone lengths and all render-side geometry derive from the joints' REST pins. What DOES run at render time is deterministic: rigidity samples along each bone keep the flesh from candy-wrapping, an Advanced root/end ring inherits its bone's rotation, and `preserveTex` (with `fleshR`, % of box height) binds nearby flesh rigidly to its bone so texture stops smearing on strong poses. **To animate a limb from the API, do not solve IK — just write the joint pins' `rig:<pid>:x|y` offset tracks directly** (bone lengths are respected only when the values came from the editor's solve; arbitrary offsets simply stretch the limb like plain pins).
966
1114
 
967
- ### Repetile entry
1115
+ ## Repetile entry
968
1116
 
969
1117
  ```json
970
1118
  {
@@ -989,8 +1137,8 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
989
1137
  - `repSpacingX`/`repSpacingY` are the grid PITCH as a % of the layer box (5–1000): 100 = tiles touch, above = gaps, below = overlap. They never resize the artwork. `repScale` (1–800) is the only thing that does — a tile is the layer at `repScale`, so 50 draws each copy at half size on the same pitch, and `repScaleFall` (−200…500) ramps that across the field. `repClipCell: 1` clips every tile to its cell (true CC RepeTile); `0` lets rotated or oversized tiles overlap. `repEdgeMode` decides what a tile shows where it reaches past the layer box (`0` Repeat, `1` Mirror, `2` Clamp, `3` Transparent) and only matters once `repBlendBorders` or a large `repScale` pushes past the cell.
990
1138
  - Per-tile stagger is the reason to reach for this over a duplicated layer. `repOrder` (`0` Columns, `1` Rows, `2` Diagonal, `3` Radial, `4` Random, `5` Columns mirrored) chooses the index everything else counts along. `repRotateStep` adds its angle ONCE PER TILE, so the field fans out from the original instead of turning as a block; `repScaleFall` and `repOpacityFall` ramp across the field; `repRotateRnd`/`repScaleRnd`/`repOpacityRnd`/`repJitterX`/`repJitterY` scatter it, all reproducible from `repSeed`.
991
1139
  - `repReveal` (0–100) is the entrance and the param to keyframe: each tile arrives after a delay set by `repSpread`, starting from `repFromScale`/`repFromRotate`/`repFromOffset` and easing in with `repEase`. `repReveal: 0` with `repFromScale: 0` draws nothing, so a single 0 → 100 pair is a full grid build-on.
992
- - `repRevealType` is the pose a tile arrives FROM, and the From fields shape it further: `0` Fade (opacity only — with the default From values this is exactly the historical entrance), `1` Pop (starts at 15% scale and overshoots on the scale channel whatever easing is picked), `2` Rise (starts half a layer-height below), `3` Spin (starts at 40% scale, turned 180°), `4` From Center (the tile collapses onto the original's own centre and travels out to its cell, so the field blooms from the artwork). All five land on exactly the same finished field at `repReveal: 100`.
993
- - `repRevealOriginal` decides whether the layer itself joins the entrance. `1` (default, and what the effect always did) means `repReveal: 0` hides the layer along with every copy — the field builds on from nothing. `0` means the entrance belongs to the COPIES: the original tile sits it out at full opacity in its finished pose, so the layer the user positioned never blinks.
1140
+ - `repRevealType` is the pose a tile arrives FROM, and the From fields shape it further: `0` Fade (opacity only), `1` Pop (starts at 15% scale and overshoots on the scale channel whatever easing is picked), `2` Rise (starts half a layer-height below), `3` Spin (starts at 40% scale, turned 180°), `4` From Center (the tile collapses onto the original's own centre and travels out to its cell, so the field blooms from the artwork). All five land on exactly the same finished field at `repReveal: 100`.
1141
+ - `repRevealOriginal` decides whether the layer itself joins the entrance. `1` (default) means `repReveal: 0` hides the layer along with every copy — the field builds on from nothing. `0` means the entrance belongs to the COPIES: the original tile sits it out at full opacity in its finished pose, so the layer the user positioned never blinks.
994
1142
  - `repScrollX`/`repScrollY` (px/s at 1080p reference) drift the whole field for a seamless scrolling pattern; `repWaveAmp` with `repWaveAxis`/`repWaveLength`/`repWaveSpeed` runs a travelling wave through the per-tile Y, X, radial offset, rotation or scale.
995
1143
  - `repDelay` (0–60 frames at a nominal 30fps, `0` = off) lags each tile's PICTURE behind the layer's own along the stagger order, so a grid of a video plays as a canon rather than in lockstep: the head of `repOrder` plays live and its tail lags by the full delay. It is the one repetile control that is not a pure function of time — it replays a ring of 16 recorded half-res plates, the same machine as Motion Trail's Frame Echo, so it fills in during playback and export while a fresh seek shows the live frame until the ring catches up. Long delays are spanned by recording every Nth frame rather than by more VRAM. While it is on, every tile reads the ring (including the undelayed ones) so the field is uniformly soft, and tiles layer by delay first and `repStack` second. Not keyframable, for the same reason `repWaveSpeed` is not: it is a slot lookup, so a moving value would jump rather than accelerate.
996
1144
  - `repTint`/`repTintAmount`/`repTintMode` colour tiles across the field, `repBlendMode` (`0` Normal, `1` Add, `2` Screen) composites them, and `repStack` orders the draw so overlapping tiles layer the way you want.
@@ -998,7 +1146,136 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
998
1146
  - `repAmount` is the master: it fades the ADDED tiles only, leaving the original at full opacity, and `0` is an exact bypass. Keyframe `propertyKeyframes.repReveal`, `repAmount`, `repScale`, `repScaleFall`, `repScaleRnd`, `repRotate`, `repRotateStep`, `repRotateRnd`, `repOpacityFall`, `repOpacityRnd`, `repJitterX`, `repJitterY`, `repSpacingX`, `repSpacingY`, `repBrick`, `repBlendBorders`, `repWaveAmp`, `repWaveLength`, `repSpread`, `repFromScale`, `repFromRotate`, `repFromOffset`, `repTintAmount`, `repScrollX`, `repScrollY`, `repLeft`, `repRight`, `repUp`, `repDown`, `repLayoutSpread`, `repLayoutTurns`. **`repScrollX`/`repScrollY` are RATES whose tracks are INTEGRATED over clip time** (like `afSpeed` and Neon Glow's `pathSpeed`): a ramp accelerates the field smoothly instead of teleporting it when the rate changes. `repWaveSpeed` is a rate too and is deliberately NOT keyframable for the same reason `glowSpeed` is not — its phase is a plain `time × speed`, so keying it would jump rather than accelerate. `repSeed` is not keyframable either: a changed seed re-rolls every random draw, so an animated one strobes — but growing the grid does NOT re-roll anything, because each tile's random draws are hashed from its own position in the field, so tiles already on screen hold their jitter, scale and rotation while new rings fade in.
999
1147
  - Not available on `model3d` layers or camera-baked 3D plates: they come back from the 3D pass on a neutral full-frame quad, so there is no layer box to lay a field out in.
1000
1148
 
1001
- ### Region Distort entry
1149
+ ## Glass entry
1150
+
1151
+ Use this whenever a video needs glass: glassmorphism cards and buttons, a liquid-glass bar, frosted panels, a magnifier or lens over footage or type, crystal or prism tiles. Do not fake it with `backdropBlur` plus a white fill, an HTML clip with CSS `backdrop-filter`, or a generated video — none of them bends what is behind, and a stack of them does not refract itself. To show a layer's OWN pixels through reeded, rippled or diamond-cut glass (no shape needed), use [Pryzm](#pryzm-entry) instead.
1152
+
1153
+ A shape with a `glass` entry is a pane the compositor renders over the layers below it: they are bent through the pane's bevel or lens (refraction with spectral dispersion), frosted and tinted, the rim is lit and the pane drops a soft shadow on them. Panes stack: a glass shape higher in the timeline refracts, frosts and shadows the glass shapes under it, so overlapping cards and glass buttons on a glass bar work as real glass. The pane follows the layer's transform, keyframes and 3D rotation (`rotateX`, `rotateY`, the camera): a tilted pane shows its thickness as side walls.
1154
+
1155
+ ```json
1156
+ { "shapeType": "rectangle", "shapeSizeUnit": "px", "shapeWidthPx": 760, "shapeHeightPx": 220, "borderRadius": 64,
1157
+ "shapeFillColor": "rgba(255,255,255,0)",
1158
+ "layerEffects": [{ "id": "glass", "eid": "layer-glass", "enabled": true, "params": { "glassPreset": "liquid" } }] }
1159
+ ```
1160
+
1161
+ - `params.glassPreset` sets every parameter at once: `liquid` (clear pane, curled edge — the usual choice), `frosted` (blurred milky pane), `slab` (thick crystal block with a small chamfer; walls show when tilted), `opal` (milky tile), `magnifier` (dome lens that enlarges), `fisheye` (concave lens that shrinks), `prism` (cut edge with a spectral split), `fluted` (reeded ribs), `hammered` (dimpled cast glass), `smoked` (dark tint), `etched` (sandblasted face inside a clear border), `gel` (tinted bubble). An unknown id loads as `liquid`. Turn the pane off with `enabled: false` (settings kept) or remove the entry.
1162
+ - Send a transparent `shapeFillColor` with it. The fill draws on top of the pane, so an opaque fill hides the glass; a faint one tints it. `borderWidth` / `borderColor` draw a rim line over the pane.
1163
+ - Only `rectangle` (with any `borderRadius`, per-corner radii included), `ellipse` and `circle` shapes take glass; other shape types, clip-mask shapes and non-shape layers ignore the entry. The pane fades with the layer's `opacity`, keeps only what its `trackMatte` keeps, and is cut by its Layer Mask (a `layerMask` entry in the same `layerEffects` list): the masked-away part shows no glass and casts no shadow.
1164
+ - Every param below is optional; one you leave out takes the preset's value when the project loads (and is stored from then on). Send only the ones you change. `%` sizes are of the shape's shorter side, so one preset reads the same on a button and on a full-width card.
1165
+
1166
+ | Param | Range | Meaning |
1167
+ |---|---|---|
1168
+ | `glassProfile` | `circle` · `squircle` · `chamfer` · `dome` · `concave` | Edge profile (round, squircle, flat cut), or a whole-pane lens: `dome` enlarges, `concave` shrinks |
1169
+ | `glassBevel` | 0.5–40 % | Width of the bent edge band (the rim cap of a lens) |
1170
+ | `glassHeight` | 0–50 % | Bevel height — how steep the edge is, so how strongly it bends |
1171
+ | `glassDepth` | 0–50 % | Thickness under the bevel; shows as side walls on a tilted pane |
1172
+ | `glassCorner` | 2–6 | Corner smoothing: 2 = circular corners, 4–5 ≈ continuous (squircle) corners |
1173
+ | `glassIor` | 1–2.4 | Index of refraction (1.5 ≈ glass) |
1174
+ | `glassDispersion` | 0–1 | Spectral split at the bent edges |
1175
+ | `glassMagnify` | 0.5–2.5 | Extra zoom of what is behind the whole pane |
1176
+ | `glassFrost` | 0–60 px | Blur of what is behind |
1177
+ | `glassGrain` | 0–1 | Sandblasted grain in the frost |
1178
+ | `glassClearBorder` | 0–20 % | Clear polished border around a frosted face |
1179
+ | `glassPattern` | `none` · `fluted` · `hammered` · `ripple` | Surface pattern |
1180
+ | `glassPatternScale` / `glassPatternDepth` / `glassPatternAngle` | 4–160 px / 0–40 px / 0–180° | Pattern size, relief and rotation |
1181
+ | `glassTint` | `#rrggbb` | Glass colour |
1182
+ | `glassTintAmount` / `glassTintDepth` | 0–1 / 0–1 | Tint strength; extra darkening through thick parts |
1183
+ | `glassMilk` | 0–1 | Milky white scatter |
1184
+ | `glassSaturation` / `glassBrightness` | 0–2 / −0.5–0.5 | What is behind, as seen through the pane |
1185
+ | `glassEdgeShade` | 0–1 | Darkening toward the rim |
1186
+ | `glassLightAngle` / `glassLightElevation` | 0–360° / 5–85° | Light direction: degrees counter-clockwise from screen right (120 = upper left), height above the pane |
1187
+ | `glassSpecular` / `glassShininess` | 0–3 / 4–400 | Highlight strength / tightness |
1188
+ | `glassRim` / `glassRimWidth` | 0–2 / 0.5–20 px | Lit rim line |
1189
+ | `glassReflection` / `glassSoftness` | 0–1.5 / 0–1 | Studio reflection on the curved edge / how soft highlights and reflections are |
1190
+ | `glassShadow` / `glassShadowBlur` / `glassShadowOffset` | 0–0.8 / 0–30 % / 0–20 % | The pane's shadow on what is behind |
1191
+
1192
+ Keyframe tracks named like the params: `glassIor`, `glassDispersion`, `glassMagnify`, `glassFrost`, `glassTintAmount`, `glassMilk`, `glassLightAngle`, `glassShadow` — e.g. `propertyKeyframes.glassFrost` frosts a pane in. Animate position, scale and rotation through the layer transform as usual; the refraction follows the pane.
1193
+
1194
+ ## Chroma Key entry
1195
+
1196
+ ```json
1197
+ {
1198
+ "id": "chromaKey",
1199
+ "eid": "layer-ck",
1200
+ "enabled": true,
1201
+ "params": {
1202
+ "ckKeyColor": "#3fb14a", "ckMethod": 0,
1203
+ "ckBalance": 40, "ckAdapt": 40, "ckTolerance": 0.3, "ckLumaWeight": 0.5, "ckChromaGuard": 1.8,
1204
+ "ckGain": 115, "ckClipBlack": 8, "ckClipWhite": 70,
1205
+ "ckShrink": 0, "ckFeather": 0,
1206
+ "ckSpill": 100, "ckSpillBias": 30, "ckSpillLuma": 70, "ckSpillTintAmt": 0, "ckSpillTint": "#c9a58a",
1207
+ "ckWrapAmt": 0, "ckWrapWidth": 12, "ckWrapColor": "#ffe9c9",
1208
+ "ckView": 0, "ckAmount": 100
1209
+ }
1210
+ }
1211
+ ```
1212
+
1213
+ - Keys out a backdrop of **any colour**, not only green. **`ckKeyColor` is the screen colour and must match the footage** — a generic green keys nothing on a blue or white plate, so never leave the default. Sample it from the media before writing the entry: fetch the file (or render a still — see `/render-still` (GreenLight Dash only)) and average a patch of pixels near the frame border, which is where a screen shows and a subject usually does not.
1214
+ - **`ckMethod: 0` (Auto) picks the keyer from the key colour** and is the right choice almost always: a saturated key runs **Colour Difference** (the Keylight family — the key hue is rotated onto green internally, so blue, red, magenta and yellow screens key exactly like green, and an unevenly lit cloth keys from one pair of clip levels); a very light or very dark key runs **Luma** (`ckTolerance` + `ckChromaGuard`); a mid grey runs **3D Colour Distance** (`ckTolerance` + `ckLumaWeight`). `1`–`4` force one (`1` diff, `2` chroma distance in CbCr, `3` luma, `4` 3D).
1215
+ - Colour Difference controls: **`ckBalance`** compares the key channel against the stronger (`0`) or weaker (`100`) of the other two — **lower it to ≈35 when dark skin or a dark subject goes semi-transparent** on a yellow-green screen; **`ckAdapt`** scales the expected screen level by each pixel's brightness so a dark corner of the screen still reads as screen (raise toward `80` for a badly lit cyc).
1216
+ - **Matte levels** apply to every keyer: `ckGain` multiplies screen-ness (raise to clear noise on the screen), `ckClipBlack` / `ckClipWhite` are the levels on the matte (raise Clip Black to clean the screen, lower Clip White to make the subject solid). Then **`ckShrink`** chokes the matte (negative erodes, positive grows — a grown matte reveals the source pixel, never a black fringe, and never past the layer box) and **`ckFeather`** softens it; both in **composition px**.
1217
+ - **Spill**: `ckSpill` clamps the key hue out of the subject (screen light on edges and hair), `ckSpillBias` picks how much of the other channels' envelope survives, `ckSpillLuma` puts the removed brightness back as grey, and `ckSpillTintAmt` + `ckSpillTint` replace it with a colour instead. **Automatically off for a hue-less key** (white / black / grey) — there is no hue to remove.
1218
+ - **Light wrap** (`ckWrapAmt`, `ckWrapWidth` px, `ckWrapColor`) tints the matte's edge with a colour — the colour of whatever will sit behind the layer. It is a colour, not the scene: a layer effect cannot see the layers below.
1219
+ - **`ckView` renders into the output**: `1` writes the matte (grey), `2` a black / grey / white status view. Leave `0` (Result) in any project meant for export.
1220
+ - `ckAmount: 0` is an exact bypass (`0`–`100` mixes with the unkeyed plate). Every number except the key colour and the two selects is keyframable as a plain `propertyKeyframes.ck*` track.
1221
+ - Runs **FIRST** of all per-layer stages, in place on the isolated plate: masks, Rig, Grow Fill, DOF, Running Glow, the warps, Motion Blur / Trail and track mattes all see the keyed silhouette. Works on any `video` / `image` / `sequence` / `grid` layer, including camera-baked 3D plates (it reads pixels, not the layer box).
1222
+ - Starting values that measured best against ground-truth mattes (all with the sampled `ckKeyColor`): coloured screen, fine hair → `ckGain 100, ckClipBlack 10, ckClipWhite 85`; dark subject on a yellow-green screen → `ckBalance 35, ckClipWhite 62`; compressed / webcam footage → `ckClipBlack 14, ckClipWhite 80, ckShrink -0.6, ckFeather 1.2`; unevenly lit cyc → `ckAdapt 80, ckGain 135, ckClipBlack 12`; white backdrop → `ckMethod 3, ckTolerance 0.38, ckChromaGuard 3, ckGain 120, ckClipBlack 14, ckClipWhite 85, ckSpill 0`; black backdrop → `ckMethod 3, ckTolerance 0.2, ckChromaGuard 2, ckGain 130, ckClipBlack 8, ckClipWhite 80, ckSpill 0`; grey backdrop → `ckMethod 4, ckTolerance 0.34, ckLumaWeight 1.4, ckGain 130, ckClipBlack 8, ckClipWhite 70, ckSpill 0`.
1223
+
1224
+ ## Mockup Frame entry
1225
+
1226
+ ```json
1227
+ {
1228
+ "id": "mockupFrame",
1229
+ "eid": "layer-mockup",
1230
+ "enabled": true,
1231
+ "params": {
1232
+ "mfDevice": "mbp16", "mfFinish": "", "mfOrient": "native",
1233
+ "mfSizeMode": 0, "mfFit": 70,
1234
+ "mfOffsetX": 0, "mfOffsetY": 0, "mfRot": 0,
1235
+ "mfMediaFit": 0, "mfMediaScale": 100, "mfMediaX": 0, "mfMediaY": 0,
1236
+ "mfMediaRot": 0, "mfMediaUpright": 1, "mfScreenBg": "#0b0d12",
1237
+ "mfScrollX": 0, "mfScrollY": 0, "mfScrollSpeedX": 10, "mfScrollSpeedY": 10, "mfScrollWrap": 0,
1238
+ "mfGlare": 35, "mfShadow": 55, "mfShadowBlur": 60,
1239
+ "mfShadowY": 26, "mfShadowSpread": 0,
1240
+ "mfStand": 1, "mfNano": 0, "mfBand": 1,
1241
+ "mfVariant": "safari", "mfTheme": "dark", "mfUrl": "greenlightdash.com"
1242
+ }
1243
+ }
1244
+ ```
1245
+
1246
+ - Draws a device chassis AROUND the layer and re-seats the layer's own pixels in its screen. FREE. The chassis is PATH GEOMETRY re-drawn at the isolate's live pixel size, so a camera push finds more detail instead of a softer edge — which is why it holds up from 360p to 4K where a picture of a phone would not.
1247
+ - `mfDevice` picks the chassis: `browser`, `iphone17`, `iphone17air`, `pixel`, `ipad13`, `mbp16`, `mba15`, `imac24`, `imacSpaceGrey`, `xdr`, `watch`. `mfFinish` picks a colour from that device's own list and is ignored by the single-finish frames.
1248
+ - `mfSizeMode` `0` fits the whole chassis inside the layer box so nothing clips; `1` matches the chassis SCREEN to the layer box, which keeps the artwork at its original size and lets the chassis overflow — that is the case that wants `mfFit` around 70.
1249
+ - `mfOrient` (`native` / `portrait` / `landscape`) rotates the chassis and carries its screen with it, exactly as real hardware does. `mfMediaUpright` (default `1`) counter-rotates the artwork and swaps the cover/contain box, so a portrait phone can still hold a landscape clip.
1250
+ - `mfMediaFit`: `0` Cover / `1` Contain / `2` Stretch, with `mfMediaScale` / `mfMediaX` / `mfMediaY` / `mfMediaRot` placing the picture inside the screen and `mfScreenBg` filling what it does not cover.
1251
+ - **Animated screen scroll.** `mfScrollX` / `mfScrollY` (0/1) add `speed × clip time` to `mfMediaX` / `mfMediaY`; `mfScrollSpeedX` / `mfScrollSpeedY` are % of the screen per second (+X moves the artwork LEFT, i.e. scrolls right; +Y moves it UP, i.e. scrolls down a page). The speeds are RATES: keyframed, the offset is the integral of the track, so a speed change never jumps. `mfScrollWrap` (0/1) tiles the artwork so a scroll never runs off into `mfScreenBg` — set it for an endless scroll; leave it 0 to scroll a tall screenshot once (use Cover fit + `mfMediaScale`).
1252
+ - `mfVariant` (`safari` / `chrome` / `arc` / `minimal`), `mfTheme` (`light` / `dark`) and `mfUrl` apply to the `browser` chassis only. `mfStand`, `mfNano` and `mfBand` are per-device extras (0/1).
1253
+ - The framed device is ONE image: the layer's own 3D tilt (`rotateX` / `rotateY`) keystones the chassis with the screen, and Mockup Frame runs FIRST in the geometry chain, so `regionDistort`, `bend`, `meshWarp`, `bezierWarp`, `droste`, [`pryzm`](#pryzm-entry), `cornerPin` and `repetile` on the same item reshape the whole device (listing order in `layerEffects` does not matter). With `mfSizeMode` `1` those stages work on the layer box grown to cover the overflowing chassis. A framed layer is rendered SEATED into the screen (the artwork's own pixels are moved into the chassis screen on the layer's plane before masks, fills, glow and overlay effects run), so those effects act on the seated artwork and a big or tilted layer never needs pixels outside the frame. Overlay effects that resample through the layer box (CRT Matrix, VHS), Layer Mask, Depth Scan and Grow Fill (`autoFill`) are exact on a 3D-tilted layer too — the box is the plane's projective map, not an affine frame.
1254
+ - The numbers keyframe as same-name tracks; the selects and strings do not.
1255
+ - Not the same thing as a **device mockup layer** — that is a rendered `video` clip carrying a `mockupConfig` (see motion.md (GreenLight Dash only)). This is a layer effect on artwork you already have on the timeline.
1256
+
1257
+ # GreenLight Dash — Warp Layer Effects
1258
+
1259
+ > Part of the `greenlight-dash` skill. Read [video-effects.md](#greenlight-dash--video-editor-layer-effects-and-overlay-effects) first for the storage contract and the id index; read [video-keyframes.md](video-keyframes.md) when animating effects.
1260
+
1261
+ ## Contents
1262
+
1263
+ - [Warp order and scope](#warp-order-and-scope)
1264
+ - [Region Distort entry](#region-distort-entry)
1265
+ - [Bend entry](#bend-entry)
1266
+ - [Corner Pin entry](#corner-pin-entry)
1267
+ - [Mesh Warp entry](#mesh-warp-entry)
1268
+ - [Bezier Warp entry](#bezier-warp-entry)
1269
+ - [Droste entry](#droste-entry)
1270
+ - [Pryzm entry](#pryzm-entry)
1271
+
1272
+ ## Warp order and scope
1273
+
1274
+ All seven work on `video`, `image`, `text`, `shape`, `motion`, `sequence` and `grid` layers, not on `model3d` layers or camera-baked 3D plates (those come back from the 3D pass with no layer box to warp). Positions are percent of the layer box with +y down, so a warp rides the layer through move, scale, rotate and parenting. Droste needs PRO at render; the others are free.
1275
+
1276
+ **Order.** The geometry stages run in a fixed order, whatever their order in `layerEffects`: [Mockup Frame](#mockup-frame-entry) first, then Region Distort → Bend → Mesh Warp → Bezier Warp → Droste or Pryzm (same slot) → Corner Pin. Repetile also runs after Region Distort, and Motion Blur after Repetile. So a recursed or glass-warped layer can still be pinned onto a plane last.
1277
+
1278
+ ## Region Distort entry
1002
1279
 
1003
1280
  ```json
1004
1281
  {
@@ -1012,14 +1289,14 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
1012
1289
  }
1013
1290
  ```
1014
1291
 
1015
- - Rescales only what is inside the band; outside it the layer slides to make room but keeps its own shape — the "stretch the word, not the burger" tool. The map is a per-fragment inverse, so nothing is resampled twice.
1292
+ - Rescales only what is inside the band; outside it the layer slides to make room but keeps its own shape — the "stretch the word, not the burger" tool.
1016
1293
  - **All geometry is PERCENT OF THE LAYER BOX with +y DOWN**, not the frame: the band rides the layer through move/scale/rotate/parenting. `dsRegX`/`dsRegY` are the band CENTRE (−50…150), `dsRegW`/`dsRegH` its size (1…200).
1017
1294
  - `dsScaleX`/`dsScaleY` (10…400 %) are the stretch. **Both at 100 is an exact bypass** — the pass is skipped entirely unless a `dsScaleX`/`dsScaleY` keyframe track exists, so a keyed ramp away from neutral still runs.
1018
1295
  - `dsFeather` (0.2…35 %) is how gradually the stretch hands back to the untouched part. `dsKeepSize` `0` lets the layer grow past its bounds, `1` rescales the result back into the original box.
1019
- - Keyframe `propertyKeyframes.dsRegX`, `dsRegY`, `dsRegW`, `dsRegH`, `dsScaleX`, `dsScaleY`, `dsFeather` — plain numbers, so the on-canvas band and the panel fields write the same values. `dsKeepSize` is not keyframable (it switches maps).
1296
+ - Keyframe `propertyKeyframes.dsRegX`, `dsRegY`, `dsRegW`, `dsRegH`, `dsScaleX`, `dsScaleY`, `dsFeather` — plain numbers. `dsKeepSize` is not keyframable (it switches maps).
1020
1297
  - Runs after the layer's Overlay Effects chain and Running Glow, before Bend and Repetile. Not available on `model3d` layers or camera-baked 3D plates — they come back on a neutral full-frame quad with no layer box to warp in.
1021
1298
 
1022
- ### Bend entry
1299
+ ## Bend entry
1023
1300
 
1024
1301
  ```json
1025
1302
  {
@@ -1033,14 +1310,14 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
1033
1310
  }
1034
1311
  ```
1035
1312
 
1036
- - After Effects' CC Bend It: everything outside the two axis handles stays rigid, and the span between them bends around an arc. A forward vertex warp over a 128² grid, which is why it is its own effect rather than a mode of `regionDistort` (an inverse fragment map) — they share no parameter, and a layer may carry both.
1313
+ - After Effects' CC Bend It: everything outside the two axis handles stays rigid, and the span between them bends around an arc. It is a separate effect from `regionDistort` (they share no parameter), and a layer may carry both.
1037
1314
  - `bnAX`/`bnAY` (handle A) and `bnBX`/`bnBY` (handle B) are PERCENT OF THE LAYER BOX with +y DOWN, −50…150, same convention as `regionDistort`.
1038
1315
  - `bnAngle` (−180…180°) is how far the span arcs; `bnStretch` (20…300 %) lengthens the span along its own axis so the rigid ends slide apart instead of scaling. **`bnAngle: 0` with `bnStretch: 100` is an exact bypass** unless one of those two channels is keyframed.
1039
1316
  - Keyframe `propertyKeyframes.bnAX`, `bnAY`, `bnBX`, `bnBY`, `bnAngle`, `bnStretch`.
1040
1317
  - With `regionDistort` on the same layer the region stretch runs FIRST and the bend arcs the already-stretched artwork. Same media scope and same 3D exclusions as `regionDistort`.
1041
- - A legacy single `distort` entry (the two warps behind one `dsType` selector) migrates on load: `dsType: 1` becomes a `bend` entry with its `ds`-prefixed axis params renamed to `bn*`, anything else becomes `regionDistort`. Write the split ids, not `distort`.
1318
+ - Write `regionDistort` and `bend`, never the combined `distort` id (a `distort` entry is migrated on load).
1042
1319
 
1043
- ### Corner Pin entry
1320
+ ## Corner Pin entry
1044
1321
 
1045
1322
  ```json
1046
1323
  {
@@ -1054,14 +1331,14 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
1054
1331
  }
1055
1332
  ```
1056
1333
 
1057
- - Maps the layer box onto an arbitrary quad through a TRUE projective homography, so parallel edges converge like a plane seen at an angle rather than stretching evenly. That perspective is why it is an inverse fragment map and not a forward vertex warp like `bend`: a forward warp interpolates affinely inside each triangle, which flattens exactly the convergence the effect exists to produce.
1334
+ - Maps the layer box onto an arbitrary quad through a TRUE projective homography, so parallel edges converge like a plane seen at an angle rather than stretching evenly.
1058
1335
  - The eight params are the corner positions in the winding UL → UR → LR → LL, as **PERCENT OF THE LAYER BOX** with +y down (−50…150, so a corner may sit outside the box). **`0,0 / 100,0 / 100,100 / 0,100` is an exact bypass**; `defaultParams` deliberately leans the top edge back so a freshly added effect shows what it does.
1059
1336
  - All eight keyframe as `propertyKeyframes.cpULX` … `cpLLY`, which is how a pin tracks a moving surface. Because they are the layer's own box, the pin also rides move/scale/rotate/parenting for free.
1060
1337
  - Anything outside the quad is transparent, and a degenerate quad (three collinear corners, or a corner dragged past the projective horizon) renders nothing rather than a mirrored ghost.
1061
1338
  - Runs AFTER `regionDistort` and `bend` when a layer carries several, so the pin lays the already-warped artwork onto the plane. Same media scope and 3D exclusions as `regionDistort`.
1062
- - **Migration:** it shipped as an OVERLAY effect (`item.effects`) in 9.6.32 only. Such an entry is lifted into `layerEffects` on load, its 0–1 corner fractions (`ulX`…`llY`) rescaled to percent and its `fx:<eid>:<param>` tracks renamed to plain `cp*` ones. Write the layer-effect form.
1339
+ - Write it in `layerEffects`. An `effects` entry with this id is migrated on load (its 0–1 corner fractions become percent).
1063
1340
 
1064
- ### Mesh Warp entry
1341
+ ## Mesh Warp entry
1065
1342
 
1066
1343
  ```json
1067
1344
  {
@@ -1077,12 +1354,12 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
1077
1354
  }
1078
1355
  ```
1079
1356
 
1080
- - After Effects' Mesh Warp: a Catmull-Rom surface over a control lattice, so dragging ONE vertex moves its neighbourhood and leaves the rest of the layer alone. FREE, same layer scope as `regionDistort`.
1357
+ - After Effects' Mesh Warp: a Catmull-Rom surface over a control lattice, so moving ONE vertex moves its neighbourhood and leaves the rest of the layer alone. FREE, same layer scope as `regionDistort`.
1081
1358
  - The lattice is `mwCols` x `mwRows` (2–5 each) and its vertices are the numbered params `mwP<i>X` / `mwP<i>Y`, **percent of the layer box with +y down**, running left→right then top→bottom. Only the first `mwCols * mwRows` slots are live; the rest are inert and may be omitted.
1082
1359
  - **Changing `mwCols` or `mwRows` RE-SEEDS the whole lattice** to a regular grid and drops any keyframe tracks on its vertices, because slot `i` means a different vertex at a different grid size. Choose the grid size first, then move points.
1083
1360
  - Every live vertex keyframes, but its channels are published per layer rather than from the static registry — they appear in the Keyframe Builder only while the effect is on the layer.
1084
1361
 
1085
- ### Bezier Warp entry
1362
+ ## Bezier Warp entry
1086
1363
 
1087
1364
  ```json
1088
1365
  {
@@ -1102,116 +1379,226 @@ Rim light from the subject's SHAPE. Authored from the DEPTH EFFECTS section.
1102
1379
  }
1103
1380
  ```
1104
1381
 
1105
- - After Effects' Bezier Warp: four cubic edges driving a Coons patch, so the silhouette curves smoothly instead of kinking at a vertex the way a lattice does. FREE, same layer scope as `regionDistort`.
1106
- - Twelve points in a clockwise ring from upper left — corner, its two outgoing tangents, next corner, and so on — all **percent of the layer box with +y down**, and all keyframable as same-name tracks.
1107
- - The identity ring (a flat box with evenly spaced tangents, i.e. `bwT1Y`/`bwT2Y` at `0` and `bwB1Y`/`bwB2Y` at `100`) is an exact bypass. The registry defaults deliberately bend the layer into an arc so a freshly added effect shows what it does — **write the identity ring explicitly if you want it inert**.
1382
+ - After Effects' Bezier Warp: four cubic edges driving a Coons patch, so the silhouette curves smoothly instead of kinking at a vertex the way a lattice does. FREE, same layer scope as `regionDistort`.
1383
+ - Twelve points in a clockwise ring from upper left — corner, its two outgoing tangents, next corner, and so on — all **percent of the layer box with +y down**, and all keyframable as same-name tracks.
1384
+ - The identity ring (a flat box with evenly spaced tangents, i.e. `bwT1Y`/`bwT2Y` at `0` and `bwB1Y`/`bwB2Y` at `100`) is an exact bypass. The registry defaults deliberately bend the layer into an arc so a freshly added effect shows what it does — **write the identity ring explicitly if you want it inert**.
1385
+
1386
+ ## Droste entry
1387
+
1388
+ ```json
1389
+ {
1390
+ "id": "droste",
1391
+ "eid": "layer-droste",
1392
+ "enabled": true,
1393
+ "params": {
1394
+ "droR1": 32, "droR2": 100, "droShape": 100, "droFit": 0, "droStretch": 100,
1395
+ "droCX": 50, "droCY": 50,
1396
+ "droTwist": 0, "droSpin": 0, "droZoom": 0, "droTiling": 0,
1397
+ "droFade": 0, "droTintAmount": 0, "droTint": "#0b1436",
1398
+ "droDepthLimit": 0, "droVoidOpacity": 0, "droVoid": "#000000",
1399
+ "droZoomSpeed": 0, "droSpinSpeed": 0,
1400
+ "droEdge": 0, "droQuality": 1, "droDetail": 0, "droAmount": 100
1401
+ }
1402
+ }
1403
+ ```
1404
+
1405
+ - Recurses the layer into itself — Escher's *Print Gallery* map. The recursion is CLOSED FORM (a `mod` in log-polar space), so every level exists at once: there is no level count, and depth costs nothing.
1406
+ - **`droR1` / `droR2`** are the annulus that repeats, as PERCENT OF THE LAYER BOX. Everything inside `droR1` is the next copy down; `droR2: 100` is the layer's own edge, which is the case where nothing is cropped. Only the band between them is ever sampled.
1407
+ - **`droTwist` is LEVELS PER TURN.** `0` nests straight frames inside frames (the "infinite frame"); `1` is the classic Escher spiral; `2` braids two arms; negative winds the other way. **It is seamless only at WHOLE numbers** — a fractional twist leaves a real discontinuity on the 180° ray. That is a legitimate look, never an accidental one; do not write a fractional twist unless a cut is what is wanted.
1408
+ - **`droShape`** morphs the nesting outline: `-100` diamond, `0` circle (the true conformal Droste), `100` the layer's own RECTANGLE. Use `100` for "the layer inside the layer" and `0` for a vortex. **`droFit: 1`** measures the shape against the SHORT side, so a circle stays round on a wide layer instead of becoming an ellipse; `droStretch` widens it on its x axis.
1409
+ - **`droZoom` is measured in levels and ONE WHOLE LEVEL IS A CLOSED LOOP.** Keyframe it `0 → 1` (or `0 → -1`) over the clip and the endless zoom repeats seamlessly. `droSpin` is degrees.
1410
+ - **`droZoomSpeed` (levels/s) and `droSpinSpeed` (°/s) are RATES and are deliberately NOT keyframable** — a track on a rate makes the phase jump every time the rate changes. Animate `droZoom` / `droSpin` instead. Every other number keyframes as a plain `propertyKeyframes.dro*` track.
1411
+ - **`droDepthLimit` is the COPY COUNT**. `0` is endless — the recursion costs nothing to continue, so it runs past anything the frame can resolve. An integer 1…20 draws exactly that many copies and cuts cleanly ON the level boundary, leaving a hole that `droVoid` at `droVoidOpacity` fills. A set count is a **FINITE STACK that runs BOTH WAYS** — N copies nesting inward and N growing outward, spanning copy index (−N, N). At rest the outward half is bigger than `droR2` and therefore off frame, so a still Droste is unaffected by it; it is what keeps the frame filled once a zoom carries the stack outward, instead of the copies shrinking to a speck on an empty frame. Because the stack is finite it **ENDS** rather than looping — grown past its span it has left the frame entirely. Use `droDepthLimit: 0` for the endless recursion that loops seamlessly.
1412
+ - `droTiling: 1` mirrors every second level, which breaks self-similarity on purpose (the scale breathes instead of running away). `droFade` / `droTintAmount` + `droTint` shade by depth.
1413
+ - `droQuality` is the tap budget (0 fast, 1 smooth, 2 best) — the centre of a Droste is infinitely fine, so taps are chosen per pixel from the local magnification and the last few pixels fade rather than alias. `droDetail` moves that threshold. `droEdge` handles a `droR2` past 100 (transparent / clamp / repeat / mirror, in BOX units).
1414
+ - `droAmount: 0` is an exact bypass. Runs AFTER the four shaping warps and BEFORE `cornerPin`, so a recursed layer can then be laid onto a plane. Same media scope and 3D exclusions as `regionDistort`.
1415
+
1416
+ ## Pryzm entry
1417
+
1418
+ ```json
1419
+ { "id": "pryzm", "eid": "layer-pryzm", "enabled": true,
1420
+ "params": { "pzGlass": 0, "pzCell": 16, "pzRelief": 1, "pzMagnify": 100, "pzFrost": 70, "pzAmount": 100 } }
1421
+ ```
1422
+
1423
+ - The layer seen through GLASS. `pzGlass` picks the kind: fluted ribs, concentric rings or a diamond lattice (one
1424
+ height field with three tessellations, refracted once per wavelength). `pzCell` is the cell size.
1425
+ - `pzRelief` is how STEEP the glass is. The default (1 %) is on purpose: that look is a cell magnifier, not refraction.
1426
+ Raise Relief, not Thickness, to bend light.
1427
+ - `pzMagnify` pulls each sample toward its cell's centre: at 100 % every cell shows one point of the layer behind it
1428
+ (a glass mosaic). It is separate from `pzIor`.
1429
+ - `pzDisp` spreads the colours across `pzTaps` samples (`pzSpectrum`: a smooth rainbow or a hard RGB split, which
1430
+ ignores the taps). Frost is jittered per tap, so raise `pzTaps` when a wide `pzFrost` shows ghosts.
1431
+ - `pzWarpSrc: 2` drives the cell widths from the layer's own luminance along the Origin crosshair; with Separable
1432
+ warp the ribs stay straight and only their spacing changes, the signature look.
1433
+ - `pzDrift` and `pzFlow` are rates per second and not keyframable; key the static fields instead.
1434
+ - Runs after the free-form warps and before Corner Pin (Droste's slot), so the glass can be laid onto a plane after.
1435
+ All 42 params, ranges and defaults: `GET /api/video-editor/capabilities` → `effects.layer` → `pryzm`.
1436
+
1437
+ # GreenLight Dash — Depth effects and real 3D on models
1438
+
1439
+ > Part of the `greenlight-dash` skill. Read [video-effects.md](#greenlight-dash--video-editor-layer-effects-and-overlay-effects) first for the storage contract and the id index; [video-3d.md](video-3d.md) covers model layers themselves.
1440
+
1441
+ ## Contents
1442
+
1443
+ - [Build Depth Map entry](#build-depth-map-entry)
1444
+ - [3D Depth entry](#3d-depth-entry)
1445
+ - [Depth Scan entry](#depth-scan-entry)
1446
+ - [Fresnel Rim entry](#fresnel-rim-entry)
1447
+ - [Real 3D on 3D model layers](#real-3d-on-3d-model-layers)
1448
+
1449
+ The four depth Layer Effects — `depthMap` (the source), `depth3d`, `depthScan` and `fresnelRim` — share one depth map, held by the `depthMap` entry. In the data they are ordinary `layerEffects` entries.
1450
+
1451
+ They work on `image` and `video` layers; `depthScan` and `fresnelRim` also run on `model3d` layers, where they read the model's exact depth and need no map. `GET /api/video-editor/capabilities` under-reports this (it lists image only and omits Depth Scan's `scnSpace`); trust this page for those two points. All four are free.
1452
+
1453
+ Overlay Effects that render in real 3D on a model are covered in [Real 3D on 3D model layers](#real-3d-on-3d-model-layers).
1454
+
1455
+ ## Build Depth Map entry
1456
+
1457
+ The depth SOURCE: one entry owns the depth estimate that 3D Depth, Depth Scan and Fresnel Rim sample. It is created by the depth analysis and renders nothing itself.
1458
+
1459
+ ```json
1460
+ {
1461
+ "id": "depthMap",
1462
+ "eid": "layer-depthmap-1",
1463
+ "enabled": true,
1464
+ "params": { "dmInvert": 0 },
1465
+ "depthMap": {
1466
+ "w": 384,
1467
+ "h": 216,
1468
+ "data": "data:image/png;base64,...",
1469
+ "t": 2.4
1470
+ }
1471
+ }
1472
+ ```
1473
+
1474
+ - `depthMap` rides TOP-LEVEL on the entry (not inside `params`), so the project stays self-contained.
1475
+ - `data` MUST start with `data:image/png;base64,` and `w`/`h` must be ≥ 2, or the map is dropped on load.
1476
+ - `t` (video only, optional) — the SOURCE second the map was measured at. One map serves the whole clip; `t` only records which frame it describes. Images carry no `t`.
1477
+ - `dmInvert` 0 = white is near (what the estimator produces), 1 = white is far. Every Depth effect on the layer obeys it, so they can never disagree about which end of the scene is close.
1478
+ - This entry renders nothing on its own; `depth3d`, `depthScan` and (on image / video) `fresnelRim` render nothing WITHOUT it. Write it alongside them.
1479
+ - Do not invent a depth map. It comes from the editor's depth analysis, which on video measures the frame under the playhead.
1480
+ - A map kept on the `depth3d` entry instead also renders, and the editor moves it into a `depthMap` entry when Build Depth Map is added.
1108
1481
 
1109
- ### Droste entry
1482
+ ## 3D Depth entry
1110
1483
 
1111
1484
  ```json
1112
1485
  {
1113
- "id": "droste",
1114
- "eid": "layer-droste",
1486
+ "id": "depth3d",
1487
+ "eid": "layer-depth-1",
1115
1488
  "enabled": true,
1116
1489
  "params": {
1117
- "droR1": 32, "droR2": 100, "droShape": 100, "droFit": 0, "droStretch": 100,
1118
- "droCX": 50, "droCY": 50,
1119
- "droTwist": 0, "droSpin": 0, "droZoom": 0, "droTiling": 0,
1120
- "droFade": 0, "droTintAmount": 0, "droTint": "#0b1436",
1121
- "droDepthLimit": 0, "droVoidOpacity": 0, "droVoid": "#000000",
1122
- "droZoomSpeed": 0, "droSpinSpeed": 0,
1123
- "droEdge": 0, "droQuality": 1, "droDetail": 0, "droAmount": 100
1490
+ "depthStrength": 100,
1491
+ "depthFocus": 45
1124
1492
  }
1125
1493
  }
1126
1494
  ```
1127
1495
 
1128
- - Recurses the layer into itself — Escher's *Print Gallery* map. The recursion is CLOSED FORM (a `mod` in log-polar space), so every level exists at once: there is no level count, and depth costs nothing.
1129
- - **`droR1` / `droR2`** are the annulus that repeats, as PERCENT OF THE LAYER BOX. Everything inside `droR1` is the next copy down; `droR2: 100` is the layer's own edge, which is the case where nothing is cropped. Only the band between them is ever sampled.
1130
- - **`droTwist` is LEVELS PER TURN.** `0` nests straight frames inside frames (the "infinite frame"); `1` is the classic Escher spiral; `2` braids two arms; negative winds the other way. **It is seamless only at WHOLE numbers** — a fractional twist leaves a real discontinuity on the 180° ray. That is a legitimate look, never an accidental one; do not write a fractional twist unless a cut is what is wanted.
1131
- - **`droShape`** morphs the nesting outline: `-100` diamond, `0` circle (the true conformal Droste), `100` the layer's own RECTANGLE. Use `100` for "the layer inside the layer" and `0` for a vortex. **`droFit: 1`** measures the shape against the SHORT side, so a circle stays round on a wide layer instead of becoming an ellipse; `droStretch` widens it on its x axis.
1132
- - **`droZoom` is measured in levels and ONE WHOLE LEVEL IS A CLOSED LOOP.** Keyframe it `0 → 1` (or `0 → -1`) over the clip and the endless zoom repeats seamlessly. `droSpin` is degrees.
1133
- - **`droZoomSpeed` (levels/s) and `droSpinSpeed` (°/s) are RATES and are deliberately NOT keyframable** — a track on a rate makes the phase jump every time the rate changes. Animate `droZoom` / `droSpin` instead. Every other number keyframes as a plain `propertyKeyframes.dro*` track.
1134
- - **`droDepthLimit` is the COPY COUNT** (the panel calls it **Copies**; the key is historic). `0` is endless — the recursion costs nothing to continue, so it runs past anything the frame can resolve. An integer 1…20 draws exactly that many copies and cuts cleanly ON the level boundary, leaving a hole that `droVoid` at `droVoidOpacity` fills. A set count is a **FINITE STACK that runs BOTH WAYS** — N copies nesting inward and N growing outward, spanning copy index (−N, N). At rest the outward half is bigger than `droR2` and therefore off frame, so a still Droste is unaffected by it; it is what keeps the frame filled once a zoom carries the stack outward, instead of the copies shrinking to a speck on an empty frame. Because the stack is finite it **ENDS** rather than looping — grown past its span it has left the frame entirely. Use `droDepthLimit: 0` for the endless recursion that loops seamlessly.
1135
- - `droTiling: 1` mirrors every second level, which breaks self-similarity on purpose (the scale breathes instead of running away). `droFade` / `droTintAmount` + `droTint` shade by depth.
1136
- - `droQuality` is the tap budget (0 fast, 1 smooth, 2 best) — the centre of a Droste is infinitely fine, so taps are chosen per pixel from the local magnification and the last few pixels fade rather than alias. `droDetail` moves that threshold. `droEdge` handles a `droR2` past 100 (transparent / clamp / repeat / mirror, in BOX units).
1137
- - `droAmount: 0` is an exact bypass. Runs AFTER the four shaping warps and BEFORE `cornerPin`, so a recursed layer can then be laid onto a plane. Same media scope and 3D exclusions as `regionDistort`.
1138
-
1139
- ### Glass entry
1496
+ - Reads the `depthMap` entry's map. Renders nothing without one.
1497
+ - Accepts the Overlay Effects **Active Range** — optional top-level `start`/`end` in clip-local seconds. Outside the window the strength is 0 (an exact bypass). It is a HARD edge; keyframe `depthStrength` for a gentle arrival. The timeline draws it as a coloured line under the clip, like a windowed Overlay Effect.
1498
+ - `depthStrength` is 0–300%; `depthFocus` is 0–100%.
1499
+ - Keyframe them with `propertyKeyframes.depthStrength` and `propertyKeyframes.depthFocus`.
1500
+ - The effect uses the evaluated project-camera pose, including Camera Motion Paths, pan, tilt, and dolly. Lens-only `camZoom` intentionally produces no parallax.
1501
+ - A `cameraLocked` channel does not receive camera-driven depth displacement.
1140
1502
 
1141
- Use this whenever a video needs glass: glassmorphism cards and buttons, a liquid-glass bar, frosted panels, a magnifier or lens over footage or type, crystal or prism tiles. Do not fake it with `backdropBlur` plus a white fill, an HTML clip with CSS `backdrop-filter`, or a generated video — none of them bends what is behind, and a stack of them does not refract itself.
1503
+ ## Depth Scan entry
1142
1504
 
1143
- A shape with a `glass` entry is a pane the compositor renders over the layers below it: they are bent through the pane's bevel or lens (refraction with spectral dispersion), frosted and tinted, the rim is lit and the pane drops a soft shadow on them. Panes stack: a glass shape higher in the timeline refracts, frosts and shadows the glass shapes under it, so overlapping cards and glass buttons on a glass bar work as real glass. The pane follows the layer's transform, keyframes and 3D rotation (`rotateX`, `rotateY`, the camera): a tilted pane shows its thickness as side walls.
1505
+ A narrow slice THROUGH DEPTH, not a screen wipe: pixels whose depth is within
1506
+ `scnWidth` of the moving threshold light up, so the contour crawls over the
1507
+ subject's surface instead of marching across the frame.
1144
1508
 
1145
1509
  ```json
1146
- { "shapeType": "rectangle", "shapeSizeUnit": "px", "shapeWidthPx": 760, "shapeHeightPx": 220, "borderRadius": 64,
1147
- "shapeFillColor": "rgba(255,255,255,0)",
1148
- "layerEffects": [{ "id": "glass", "eid": "layer-glass", "enabled": true, "params": { "glassPreset": "liquid" } }] }
1510
+ {
1511
+ "id": "depthScan",
1512
+ "eid": "layer-scan-1",
1513
+ "enabled": true,
1514
+ "params": {
1515
+ "scnSpeed": 83.33,
1516
+ "scnPhase": 4,
1517
+ "scnEase": 0,
1518
+ "scnWidth": 1.1,
1519
+ "scnSplit": 2.4,
1520
+ "scnFeather": 0.35,
1521
+ "scnGrain": 22,
1522
+ "scnPixel": 1,
1523
+ "scnColorMode": 0,
1524
+ "scnColor": "#b8ff52",
1525
+ "scnRamp": "linear-gradient(90deg, #001b2e 0%, #0aa6b8 40%, #b8ff52 70%, #ffffff 100%)",
1526
+ "scnGamma": 1,
1527
+ "scnLow": 1.5,
1528
+ "scnHigh": 99.5,
1529
+ "scnBorder": 100,
1530
+ "scnBrightness": 125,
1531
+ "scnSource": 0,
1532
+ "scnAmount": 100,
1533
+ "scnView": 0
1534
+ }
1535
+ }
1149
1536
  ```
1150
1537
 
1151
- - `params.glassPreset` sets every parameter at once: `liquid` (clear pane, curled edge — the usual choice), `frosted` (blurred milky pane), `slab` (thick crystal block with a small chamfer; walls show when tilted), `opal` (milky tile), `magnifier` (dome lens that enlarges), `fisheye` (concave lens that shrinks), `prism` (cut edge with a spectral split), `fluted` (reeded ribs), `hammered` (dimpled cast glass), `smoked` (dark tint), `etched` (sandblasted face inside a clear border), `gel` (tinted bubble). An unknown id loads as `liquid`. Turn the pane off with `enabled: false` (settings kept) or remove the entry.
1152
- - Send a transparent `shapeFillColor` with it. The fill draws on top of the pane, so an opaque fill hides the glass; a faint one tints it. `borderWidth` / `borderColor` draw a rim line over the pane.
1153
- - Only `rectangle` (with any `borderRadius`, per-corner radii included), `ellipse` and `circle` shapes take glass; other shape types, clip-mask shapes and non-shape layers ignore the entry. The pane fades with the layer's `opacity`, keeps only what its `trackMatte` keeps, and is cut by its Layer Mask (a `layerMask` entry in the same `layerEffects` list): the masked-away part shows no glass and casts no shadow.
1154
- - Every param below is optional; one you leave out takes the preset's value when the project loads (and is stored from then on). Send only the ones you change. `%` sizes are of the shape's shorter side, so one preset reads the same on a button and on a full-width card.
1155
-
1156
- | Param | Range | Meaning |
1157
- |---|---|---|
1158
- | `glassProfile` | `circle` · `squircle` · `chamfer` · `dome` · `concave` | Edge profile (round, squircle, flat cut), or a whole-pane lens: `dome` enlarges, `concave` shrinks |
1159
- | `glassBevel` | 0.5–40 % | Width of the bent edge band (the rim cap of a lens) |
1160
- | `glassHeight` | 0–50 % | Bevel height — how steep the edge is, so how strongly it bends |
1161
- | `glassDepth` | 0–50 % | Thickness under the bevel; shows as side walls on a tilted pane |
1162
- | `glassCorner` | 2–6 | Corner smoothing: 2 = circular corners, 4–5 ≈ continuous (squircle) corners |
1163
- | `glassIor` | 1–2.4 | Index of refraction (1.5 ≈ glass) |
1164
- | `glassDispersion` | 0–1 | Spectral split at the bent edges |
1165
- | `glassMagnify` | 0.5–2.5 | Extra zoom of what is behind the whole pane |
1166
- | `glassFrost` | 0–60 px | Blur of what is behind |
1167
- | `glassGrain` | 0–1 | Sandblasted grain in the frost |
1168
- | `glassClearBorder` | 0–20 % | Clear polished border around a frosted face |
1169
- | `glassPattern` | `none` · `fluted` · `hammered` · `ripple` | Surface pattern |
1170
- | `glassPatternScale` / `glassPatternDepth` / `glassPatternAngle` | 4–160 px / 0–40 px / 0–180° | Pattern size, relief and rotation |
1171
- | `glassTint` | `#rrggbb` | Glass colour |
1172
- | `glassTintAmount` / `glassTintDepth` | 0–1 / 0–1 | Tint strength; extra darkening through thick parts |
1173
- | `glassMilk` | 0–1 | Milky white scatter |
1174
- | `glassSaturation` / `glassBrightness` | 0–2 / −0.5–0.5 | What is behind, as seen through the pane |
1175
- | `glassEdgeShade` | 0–1 | Darkening toward the rim |
1176
- | `glassLightAngle` / `glassLightElevation` | 0–360° / 5–85° | Light direction: degrees counter-clockwise from screen right (120 = upper left), height above the pane |
1177
- | `glassSpecular` / `glassShininess` | 0–3 / 4–400 | Highlight strength / tightness |
1178
- | `glassRim` / `glassRimWidth` | 0–2 / 0.5–20 px | Lit rim line |
1179
- | `glassReflection` / `glassSoftness` | 0–1.5 / 0–1 | Studio reflection on the curved edge / how soft highlights and reflections are |
1180
- | `glassShadow` / `glassShadowBlur` / `glassShadowOffset` | 0–0.8 / 0–30 % / 0–20 % | The pane's shadow on what is behind |
1538
+ - Reads the `depthMap` entry's map. Renders nothing without one.
1539
+ - Accepts the Overlay Effects **Active Range** — optional top-level `start`/`end` in clip-local seconds (NOT inside `params`), as on a windowed Overlay Effect. Outside the window the pass is skipped entirely. Inside it the SWEEP CLOCK IS REBASED, so the slice starts at `scnPhase` wherever the window sits rather than being caught mid-travel; keyframe tracks stay on the clip clock.
1540
+ - `scnColorMode` picks how the slice is coloured:
1541
+ - `0` **RGB Split** (default) — three slices at `phase − split` / `phase` / `phase + split`, which reads as chromatic fringing on the contour.
1542
+ - `1` **Flat Color** — one slice in `scnColor`. `scnSplit` is ignored.
1543
+ - `2` **Gradient** — one slice widened to `width + split`, with `scnRamp` laid across it far-to-near. `scnSplit` still means "how far the colours spread through depth".
1544
+ - `scnColor` is a hex string; `scnRamp` is a CSS `linear-gradient(...)` string.
1545
+ - The sweep is a pure function of clip time: `phase = scnPhase + scnSpeed × seconds`, wrapped to 0–1. `scnEase: 1` (Hold Ends) applies a closed-form remap so it crawls at the extremes.
1546
+ - `scnBorder` (0–100%) rejects the depth CLIFF at the subject's silhouette. At 0 the silhouette becomes a permanent outline that never moves, which reads as a filter rather than a scan.
1547
+ - `scnView` `1` (Depth Map) and `2` (Original) are diagnostics that **render and export exactly as shown** — set it back to `0` before exporting.
1548
+ - `scnAmount: 0` is a byte-exact bypass.
1549
+ - Keyframable as same-name `propertyKeyframes` tracks: `scnPhase`, `scnWidth`, `scnSplit`, `scnFeather`, `scnGrain`, `scnGamma`, `scnLow`, `scnHigh`, `scnBorder`, `scnBrightness`, `scnSource`, `scnAmount`. `scnSpeed` is deliberately static (phase is `start + speed × t`, so an animated rate teleports the slice), as are `scnPixel` and the enums.
1550
+ - **On a `model3d` layer** no `depthMap` entry is needed: the slice runs through the model's EXACT depth inside its 3D pass. `scnSpace` picks the axis — `0` Camera depth (distance from the camera across the model's own depth range, default), `1` / `2` / `3` a plane sweeping along the model's own X / Y / Z, so the line runs over the body however it is posed. `scnPixel`, `scnBorder` and `scnView` do nothing there.
1181
1551
 
1182
- Keyframe tracks named like the params: `glassIor`, `glassDispersion`, `glassMagnify`, `glassFrost`, `glassTintAmount`, `glassMilk`, `glassLightAngle`, `glassShadow` — e.g. `propertyKeyframes.glassFrost` frosts a pane in. Animate position, scale and rotation through the layer transform as usual; the refraction follows the pane.
1552
+ ## Fresnel Rim entry
1183
1553
 
1184
- ### Chroma Key entry
1554
+ Rim light from the subject's SHAPE.
1185
1555
 
1186
1556
  ```json
1187
1557
  {
1188
- "id": "chromaKey",
1189
- "eid": "layer-ck",
1558
+ "id": "fresnelRim",
1559
+ "eid": "layer-rim-1",
1190
1560
  "enabled": true,
1191
1561
  "params": {
1192
- "ckKeyColor": "#3fb14a", "ckMethod": 0,
1193
- "ckBalance": 40, "ckAdapt": 40, "ckTolerance": 0.3, "ckLumaWeight": 0.5, "ckChromaGuard": 1.8,
1194
- "ckGain": 115, "ckClipBlack": 8, "ckClipWhite": 70,
1195
- "ckShrink": 0, "ckFeather": 0,
1196
- "ckSpill": 100, "ckSpillBias": 30, "ckSpillLuma": 70, "ckSpillTintAmt": 0, "ckSpillTint": "#c9a58a",
1197
- "ckWrapAmt": 0, "ckWrapWidth": 12, "ckWrapColor": "#ffe9c9",
1198
- "ckView": 0, "ckAmount": 100
1562
+ "frColorA": "#ff4fd8",
1563
+ "frColorB": "#7b3cff",
1564
+ "frIntensity": 5.2,
1565
+ "frWidth": 42,
1566
+ "frSoftness": 40,
1567
+ "frHalo": 1.2,
1568
+ "frBody": 25,
1569
+ "frPulse": 0,
1570
+ "frDirMix": 0,
1571
+ "frAngle": 0,
1572
+ "frAmount": 100
1199
1573
  }
1200
1574
  }
1201
1575
  ```
1202
1576
 
1203
- - Keys out a backdrop of **any colour**, not only green. **`ckKeyColor` is the screen colour and must match the footage** — a generic green keys nothing on a blue or white plate, so never leave the default. Sample it from the media before writing the entry: fetch the file (or render a still — see `/render-still` in `video-rendering.md`) and average a patch of pixels near the frame border, which is where a screen shows and a subject usually does not.
1204
- - **`ckMethod: 0` (Auto) picks the keyer from the key colour** and is the right choice almost always: a saturated key runs **Colour Difference** (the Keylight family — the key hue is rotated onto green internally, so blue, red, magenta and yellow screens key exactly like green, and an unevenly lit cloth keys from one pair of clip levels); a very light or very dark key runs **Luma** (`ckTolerance` + `ckChromaGuard`); a mid grey runs **3D Colour Distance** (`ckTolerance` + `ckLumaWeight`). `1`–`4` force one (`1` diff, `2` chroma distance in CbCr, `3` luma, `4` 3D).
1205
- - Colour Difference controls: **`ckBalance`** compares the key channel against the stronger (`0`) or weaker (`100`) of the other two — **lower it to ≈35 when dark skin or a dark subject goes semi-transparent** on a yellow-green screen; **`ckAdapt`** scales the expected screen level by each pixel's brightness so a dark corner of the screen still reads as screen (raise toward `80` for a badly lit cyc).
1206
- - **Matte levels** apply to every keyer: `ckGain` multiplies screen-ness (raise to clear noise on the screen), `ckClipBlack` / `ckClipWhite` are the levels on the matte (raise Clip Black to clean the screen, lower Clip White to make the subject solid). Then **`ckShrink`** chokes the matte (negative erodes, positive grows — a grown matte reveals the source pixel, never a black fringe, and never past the layer box) and **`ckFeather`** softens it; both in **composition px**.
1207
- - **Spill**: `ckSpill` clamps the key hue out of the subject (screen light on edges and hair), `ckSpillBias` picks how much of the other channels' envelope survives, `ckSpillLuma` puts the removed brightness back as grey, and `ckSpillTintAmt` + `ckSpillTint` replace it with a colour instead. **Automatically off for a hue-less key** (white / black / grey) — there is no hue to remove.
1208
- - **Light wrap** (`ckWrapAmt`, `ckWrapWidth` px, `ckWrapColor`) tints the matte's edge with a colour — the colour of whatever will sit behind the layer. It is a colour, not the scene: a layer effect cannot see the layers below.
1209
- - **`ckView` renders into the output**: `1` writes the matte (grey), `2` a black / grey / white status view. Leave `0` (Result) in any project meant for export.
1210
- - `ckAmount: 0` is an exact bypass (`0`–`100` mixes with the unkeyed plate). Every number except the key colour and the two selects is keyframable as a plain `propertyKeyframes.ck*` track.
1211
- - Runs **FIRST** of all per-layer stages, in place on the isolated plate: masks, Rig, Grow Fill, DOF, Running Glow, the warps, Motion Blur / Trail and track mattes all see the keyed silhouette. Works on any `video` / `image` / `sequence` / `grid` layer, including camera-baked 3D plates (it reads pixels, not the layer box).
1212
- - Starting values that measured best against ground-truth mattes (all with the sampled `ckKeyColor`): coloured screen, fine hair → `ckGain 100, ckClipBlack 10, ckClipWhite 85`; dark subject on a yellow-green screen → `ckBalance 35, ckClipWhite 62`; compressed / webcam footage → `ckClipBlack 14, ckClipWhite 80, ckShrink -0.6, ckFeather 1.2`; unevenly lit cyc → `ckAdapt 80, ckGain 135, ckClipBlack 12`; white backdrop → `ckMethod 3, ckTolerance 0.38, ckChromaGuard 3, ckGain 120, ckClipBlack 14, ckClipWhite 85, ckSpill 0`; black backdrop → `ckMethod 3, ckTolerance 0.2, ckChromaGuard 2, ckGain 130, ckClipBlack 8, ckClipWhite 80, ckSpill 0`; grey backdrop → `ckMethod 4, ckTolerance 0.34, ckLumaWeight 1.4, ckGain 130, ckClipBlack 8, ckClipWhite 70, ckSpill 0`.
1577
+ - **3D model:** the real surface rim (1 − n·v from the model's own normals) inside its 3D pass, with its own even halo. It turns with the model and lights edges inside the outline too. No depth map. Fields: `frWidth` (rim width), `frSoftness` (hard band → raw falloff), `frBody` (0–100 %, the model's own light under the rim — 100 leaves it untouched; the presets dim it so the rim reads).
1578
+ - **Image / video:** needs the `depthMap` entry. Only EDGES glow — wherever depth falls away behind a pixel — fading inward over `frRimPx` (px of a 1080-tall frame); `frEdgeMin` is the smallest depth drop that counts. Like Depth Scan, a window (`frWindow` ± `frFeather`) around `frPhase` picks WHICH depth's edges light, swept by `frSpeed` (%/s) with `frEase` (0 Linear, 1 Hold Ends); `frGamma`, `frLow`, `frHigh` shape the depth; `frBgDim` (0–100 %) dims what is not lit.
1579
+ - Both: `frColorA` edge colour, `frColorB` falloff colour, `frIntensity` (0–6), `frHalo` (0–3, 0 skips the halo pass), `frPulse` %, `frDirMix` % with `frAngle` ° keep the rim to one side, `frAmount` (0 is an exact bypass).
1580
+ - Presets: Backlight, Neon Edge, Ember, Glacier.
1581
+ - Accepts the Active Range (top-level `start`/`end`). Keyframable: `frIntensity`, `frWidth`, `frHalo`, `frBody`, `frDirMix`, `frAngle`, `frPhase`, `frRimPx`, `frWindow`, `frAmount`.
1582
+
1583
+ ## Real 3D on 3D model layers
1584
+
1585
+ On a `model3d` item, these Overlay Effects have a REAL 3D version that renders inside the model's own 3D pass — reading its surface normals, its position in its own space and its real depth — instead of filtering the flat picture the pass hands back:
1586
+
1587
+ `rgbSplit`, `pixelate`, `crtMatrix`, `halftone`, `objectDither`, `ascii`, `fractalNoise`, `fill`, `colorama`, `gradientBeam`, `gradientSweep`, `objectContour`, `objectCurtain`, `bloom`, `auraBlur`, `lightRays`, `neonGlow`, `linesDraw`, `edgeDetect`, `flashBlink`, `glitch`, `objectSlitScan`, `pixelStretch`, `shapeRulers`, `curves`.
1588
+
1589
+ - In `capabilities.json` these effects carry `real3dOnModels: true`; 3D-only params and options carry `model3d: true`.
1590
+ - The 3D version runs only when the entry says `"real3d": true` AND its id is in the list. The editor sets it when one of these is ADDED to a model layer; `real3d` switches an entry between the two. Any other effect on a model stays on the flat chain.
1591
+ - Same `params`, Mix, Active Range and `fx:<eid>:*` keyframes as the flat effect. `clip` and the Object family's tracking params are ignored in 3D — the model IS the subject.
1592
+ - What changes in 3D, in short: patterns (Mosaic Layer grid, Pixel Matrix, Halftone, ASCII, Dither, Fractal Noise) are locked to the SURFACE and turn with the model; lighting effects (Halftone dot size, Fill's shading, Colorama, Flash) use the model's real light and normals; RGB Split re-films the model from two shifted cameras (real parallax); Bloom thresholds the displayed light but blooms the HDR light; Beam / Sweep / Contour / Glitch / Slit Scan / Pixel Stretch run along the MODEL's own axes; Neon Glow, Lines Draw and Edge Detect trace its real outline, creases and depth steps; Light Rays' origin sits in the model's box and the body occludes its own rays; Shape Rulers draws its real 3D bounding box; Curves applies to the HDR light in tone space. Bloom's Threshold / Soft Knee are judged on the displayed light (same meaning as on the flat effect), and what blooms is the HDR light, so a strong highlight glows more than a clipped one.
1593
+ - 3D-only values (the editor resets them when an entry leaves 3D): Colorama `input` `5` Height / `6` Depth / `7` Facing; Mosaic `grid` `2` **Voxels** (the model rebuilt from cubes on a grid in its own space — `size` is the cube size in px, the silhouette turns blocky too); Light Rays `source` `3` **Behind** (a light disc behind the body, rays escape round its silhouette); Flash Blink `lightAngle` (0–360°, default 135 — the side of the body the flash lands on).
1594
+ - Light that leaves the silhouette (halos, streaks, the ruler box, RGB Split's copies) is composited at the model's centre depth, so it is occluded by — and occludes — other 3D layers like the model itself.
1595
+ - Cost: nothing until a model carries one of these. The passes and their buffers load and allocate on first use.
1213
1596
 
1214
- ### Layer Mask entry
1597
+ # GreenLight Dash — Layer masks
1598
+
1599
+ > Part of the `greenlight-dash` skill. Read [video-effects.md](#greenlight-dash--video-editor-layer-effects-and-overlay-effects) for the Layer Effects storage contract. A roto result from the tracker is an ordinary mask; its data contract is in tracking-data.md (GreenLight Dash only).
1600
+
1601
+ ## Layer Mask entry
1215
1602
 
1216
1603
  ```json
1217
1604
  {
@@ -1247,9 +1634,9 @@ Keyframe tracks named like the params: `glassIor`, `glassDispersion`, `glassMagn
1247
1634
  - **On an `adjustment` item the mask scopes the grade instead of cutting pixels**: inside mask coverage the layers below are graded/effected as usual, outside it the ungraded composite shows through — a MIX, not an alpha cut (an adjustment has no pixels of its own). Same entry schema, fold rule, keyframe tracks and `mskAmount` semantics (`mskAmount: 0` = mask off = grade everywhere). Works at the top level and inside a sequence. The mask box is a % of the adjustment plane's box, which spans the frame by default and follows the item's own transform/keyframes.
1248
1635
  - **Framing follows the VISIBLE area.** On a masked `video`/`image`/`motion` layer the item's border (`borderWidth`/`borderColor`/`borderStyle`) and `shadows` are drawn along the masked shape (like a Chroma-keyed layer's), not around the rectangle the mask cut away; `shapeRulers` frames the bounds of what the mask leaves visible; `neonGlow` and `linesDraw` run along the visible outline. With `mskAmount` below 100 the cut-away part still shows, so these fall back to the whole layer. Shapes and text keep their own framing.
1249
1636
  - **The masks ride top-level on the entry** in the `masks` array (the `pins` precedent), max 16. `mid` must match `^[A-Za-z0-9_-]{1,24}$`, one entry per mid.
1250
- - **Too many points?** A rotoscoped or traced outline is usually far denser than it needs to be. `POST /api/paths/smooth` reduces any of these point lists (see *Smoothing / reducing any path* in `video-paths.md`) — use `mode: "reduce"` plus `kept_indexes` when the mask path is keyframed, so every key loses the same points and the morph stays 1:1.
1637
+ - **Too many points?** A rotoscoped or traced outline is usually far denser than it needs to be. `POST /api/paths/smooth` reduces any of these point lists (see [Smoothing / reducing any path](video-paths.md#smoothing--reducing-any-path--post-apipathssmooth)) — use `mode: "reduce"` plus `kept_indexes` when the mask path is keyframed, so every key loses the same points and the morph stays 1:1.
1251
1638
  - Each mask's `path` is **byte-identical in shape to `item.shapePath`**: points in unit-box space (±0.5 = the edge of the mask's own box, +y DOWN), handles stored as offsets from their anchor. A mask always fills, so `closed` is always `true`. At least 3 points, else the mask is dropped on load.
1252
- - ⚠️ **Write `hIn`/`hOut` and `manual: true` on every anchor** — an absent or zero-length handle is a corner, and nothing auto-smooths a stored path (that pass runs only when a human drags a point), so a bare `x`/`y` list cuts a **polygon**. Smooth anchor = `hOut = (P[next] − P[prev]) / 6`, `hIn = −hOut`, wrapping at both ends since a mask is closed; a circular quarter-arc = `0.5523 × radius` perpendicular to the radius (`0.2761` in the ±0.5 box — the four-point circle above). Anchors and handles clamp to ±8. Ready-made outlines with handles resolved: `GET /api/video-editor/capabilities` → `paths.shapePresets` (see `video-shapes.md`).
1639
+ - ⚠️ **Write `hIn`/`hOut` and `manual: true` on every anchor** — an absent or zero-length handle is a corner, and nothing auto-smooths a stored path (that pass runs only when a human drags a point), so a bare `x`/`y` list cuts a **polygon**. Smooth anchor = `hOut = (P[next] − P[prev]) / 6`, `hIn = −hOut`, wrapping at both ends since a mask is closed; a circular quarter-arc = `0.5523 × radius` perpendicular to the radius (`0.2761` in the ±0.5 box — the four-point circle above). Anchors and handles clamp to ±8. Ready-made outlines with handles resolved: `GET /api/video-editor/capabilities` → `paths.shapePresets` (see [video-shapes.md](video-shapes.md)).
1253
1640
 
1254
1641
  | Field | Unit | Range | Meaning |
1255
1642
  |---|---|---|---|
@@ -1265,101 +1652,25 @@ Keyframe tracks named like the params: `glassIor`, `glassDispersion`, `glassMagn
1265
1652
  - **`grow` is a RATE, not a distance.** Its track is INTEGRATED over clip time (like `afSpeed` and `repScrollX`), so a keyed ramp accelerates the edge smoothly instead of teleporting it. A constant `grow` needs no keyframes at all — it is a reveal on its own.
1266
1653
  - **Keyframes ride `msk:<mid>:<chan>` tracks**, never edited stored fields: channels are `x`, `y`, `w`, `h`, `rot`, `feather`, `expand`, `opacity`, `grow` (numeric, same units as the table) plus **`msk:<mid>:path`, which is NON-SCALAR** — each key's value is a JSON point list, exactly like the shape layer's `shapePath` track. `mskAmount` is an ordinary registry track. Tracks for a deleted mask are dropped on load.
1267
1654
  - ⚠️ **Every key of a `msk:<mid>:path` track MUST hold the same point count.** The morph blends point-by-point and clamps to the shorter list, so a mismatch silently corrupts the geometry. Any edit that changes the count (insert, delete, applying a preset at a different resolution) has to be applied to every key AND to the stored `path`.
1268
- - ⚠️ **A dense (per-frame / roto) `msk:<mid>:path` track must use `Linear` easing on every key** — the evaluator applies the LOW key's easing, so eased per-frame keys stutter at 30–60Hz. Key positions round to 4 decimal places (`roundKeyPosition`), which keeps adjacent frames distinct at 60fps.
1269
- - **RotoScope (video layers, in the Layer Mask panel section)** traces a subject (Person / Object / Colour modes, box region seed) and writes the whole outline animation as one `msk:<mid>:path` track on a mask named `Roto`, max 128 points, Linear keys, mask box = the media's contain-fit rect with x/y/w/h/rot never keyed. Auto-trace-this-frame is FREE; Track forward is PRO. Re-running replaces the `Roto` mask instead of adding another.
1655
+ - ⚠️ **A dense (per-frame / roto) `msk:<mid>:path` track must use `Linear` easing on every key** — the evaluator applies the LOW key's easing, so eased per-frame keys stutter at 30–60Hz. Key positions are kept to 4 decimal places, which keeps adjacent frames distinct at 60fps.
1656
+ - **RotoScope (video layers, part of Layer Mask)** traces a subject (Person / Object / Colour modes, box region seed) and writes the whole outline animation as one `msk:<mid>:path` track on a mask named `Roto`, max 128 points, Linear keys, mask box = the media's contain-fit rect with x/y/w/h/rot never keyed. Tracing the current frame is FREE; tracking forward through the clip is PRO. Re-running replaces the `Roto` mask instead of adding another.
1270
1657
  - **The fold rule.** The stack folds top-down in `masks` array order from an EMPTY base: `add` = `clamp(acc + m)`, `subtract` = `acc·(1−m)`, `intersect` = `acc·m`, `difference` = `acc + m − 2·acc·m`. **But if the FIRST enabled mask's mode is `subtract` or `intersect`, the base starts FULL instead** — otherwise a lone Subtract mask would blank the layer rather than cut a hole in it. A leading `difference` is treated as `add`. Order therefore matters: swapping two masks changes the result for Intersect and Difference.
1271
1658
  - `mskAmount` (0–100 %, keyframable) mixes toward **no mask**: 0 leaves the layer untouched, 100 cuts fully. `mskQuality` 0 Fast / 1 High raises the coverage raster's resolution (a finer plate for wide feathers and hard edges under magnification).
1272
1659
  - **Where it runs**: masks cut the layer's isolate BEFORE everything else on it — After Effects' order (Masks → Effects → Transform → Track Matte → Blend). So Running Glow traces the MASK edge, camera DOF gathers against the masked silhouette, "Clip to layer shape" clips to the mask, velocity Motion Blur smears the masked look, and a Track Matte still gets the last word. A layer can carry both a mask and a track matte, and they compose as AE does.
1273
1660
  - Known limits: with ACCUMULATION motion blur the mask lands on the already-averaged plate (crisp edge, blurred content); Neon Glow's path FX still traces the UNMASKED outline; a masked-away region still occludes in the 3D depth pass; on split text the box is the whole mesh quad, so the mask cuts the plate uniformly.
1274
1661
 
1275
- ### Mockup Frame entry
1276
-
1277
- ```json
1278
- {
1279
- "id": "mockupFrame",
1280
- "eid": "layer-mockup",
1281
- "enabled": true,
1282
- "params": {
1283
- "mfDevice": "mbp16", "mfFinish": "", "mfOrient": "native",
1284
- "mfSizeMode": 0, "mfFit": 70,
1285
- "mfOffsetX": 0, "mfOffsetY": 0, "mfRot": 0,
1286
- "mfMediaFit": 0, "mfMediaScale": 100, "mfMediaX": 0, "mfMediaY": 0,
1287
- "mfMediaRot": 0, "mfMediaUpright": 1, "mfScreenBg": "#0b0d12",
1288
- "mfScrollX": 0, "mfScrollY": 0, "mfScrollSpeedX": 10, "mfScrollSpeedY": 10, "mfScrollWrap": 0,
1289
- "mfGlare": 35, "mfShadow": 55, "mfShadowBlur": 60,
1290
- "mfShadowY": 26, "mfShadowSpread": 0,
1291
- "mfStand": 1, "mfNano": 0, "mfBand": 1,
1292
- "mfVariant": "safari", "mfTheme": "dark", "mfUrl": "greenlightdash.com"
1293
- }
1294
- }
1295
- ```
1296
-
1297
- - Draws a device chassis AROUND the layer and re-seats the layer's own pixels in its screen. FREE. The chassis is PATH GEOMETRY re-drawn at the isolate's live pixel size, so a camera push finds more detail instead of a softer edge — which is why it holds up from 360p to 4K where a picture of a phone would not.
1298
- - `mfDevice` picks the chassis: `browser`, `iphone17`, `iphone17air`, `pixel`, `ipad13`, `mbp16`, `mba15`, `imac24`, `imacSpaceGrey`, `xdr`, `watch`. `mfFinish` picks a colour from that device's own list and is ignored by the single-finish frames.
1299
- - `mfSizeMode` `0` fits the whole chassis inside the layer box so nothing clips; `1` matches the chassis SCREEN to the layer box, which keeps the artwork at its original size and lets the chassis overflow — that is the case that wants `mfFit` around 70.
1300
- - `mfOrient` (`native` / `portrait` / `landscape`) rotates the chassis and carries its screen with it, exactly as real hardware does. `mfMediaUpright` (default `1`) counter-rotates the artwork and swaps the cover/contain box, so a portrait phone can still hold a landscape clip.
1301
- - `mfMediaFit`: `0` Cover / `1` Contain / `2` Stretch, with `mfMediaScale` / `mfMediaX` / `mfMediaY` / `mfMediaRot` placing the picture inside the screen and `mfScreenBg` filling what it does not cover.
1302
- - **Animated screen scroll.** `mfScrollX` / `mfScrollY` (0/1) add `speed × clip time` to `mfMediaX` / `mfMediaY`; `mfScrollSpeedX` / `mfScrollSpeedY` are % of the screen per second (+X moves the artwork LEFT, i.e. scrolls right; +Y moves it UP, i.e. scrolls down a page). The speeds are RATES: keyframed, the offset is the integral of the track, so a speed change never jumps. `mfScrollWrap` (0/1) tiles the artwork so a scroll never runs off into `mfScreenBg` — set it for an endless scroll; leave it 0 to scroll a tall screenshot once (use Cover fit + `mfMediaScale`).
1303
- - `mfVariant` (`safari` / `chrome` / `arc` / `minimal`), `mfTheme` (`light` / `dark`) and `mfUrl` apply to the `browser` chassis only. `mfStand`, `mfNano` and `mfBand` are per-device extras (0/1).
1304
- - The framed device is ONE image: the layer's own 3D tilt (`rotateX` / `rotateY`) keystones the chassis with the screen, and Mockup Frame runs FIRST in the geometry chain, so `regionDistort`, `bend`, `meshWarp`, `bezierWarp`, `droste`, `pryzm`, `cornerPin` and `repetile` on the same item reshape the whole device (listing order in `layerEffects` does not matter). With `mfSizeMode` `1` those stages work on the layer box grown to cover the overflowing chassis. A framed layer is rendered SEATED into the screen (the artwork's own pixels are moved into the chassis screen on the layer's plane before masks, fills, glow and overlay effects run), so those effects act on the seated artwork and a big or tilted layer never needs pixels outside the frame. Overlay effects that resample through the layer box (CRT Matrix, VHS), Layer Mask, Depth Scan and Auto Fill are exact on a 3D-tilted layer too — the box is the plane's projective map, not an affine frame (10.9.207).
1305
- - The numbers keyframe as same-name tracks; the selects and strings do not.
1306
- - Not the same thing as a **device mockup layer** — that is a rendered `video` clip carrying a `mockupConfig` (see `video-motion.md`). This is a layer effect on artwork you already have on the timeline.
1307
-
1308
- ### Complete regular-item example
1309
-
1310
- ```json
1311
- {
1312
- "id": "photo-item",
1313
- "clipId": "photo-clip",
1314
- "effects": [
1315
- { "id": "grain", "eid": "photo-grain", "enabled": true, "mix": 25, "params": {} }
1316
- ],
1317
- "layerEffects": [
1318
- {
1319
- "id": "runningGlow",
1320
- "eid": "photo-runner",
1321
- "enabled": true,
1322
- "params": { "glowAmount": 80, "glowMaskMode": 1 }
1323
- },
1324
- {
1325
- "id": "depthMap",
1326
- "eid": "photo-depthmap",
1327
- "enabled": true,
1328
- "params": { "dmInvert": 0 },
1329
- "depthMap": { "w": 384, "h": 216, "data": "data:image/png;base64,..." }
1330
- },
1331
- {
1332
- "id": "depth3d",
1333
- "eid": "photo-depth",
1334
- "enabled": true,
1335
- "params": { "depthStrength": 120, "depthFocus": 45 }
1336
- },
1337
- {
1338
- "id": "depthScan",
1339
- "eid": "photo-scan",
1340
- "enabled": true,
1341
- "params": { "scnColorMode": 2, "scnWidth": 1.4, "scnSplit": 3, "scnBrightness": 140 }
1342
- }
1343
- ]
1344
- }
1345
- ```
1346
-
1347
- ## Adjustment-layer LUT
1662
+ # GreenLight Dash — Colour correction and LUTs
1348
1663
 
1349
- **Adjustment-layer LUT.** An `adjustment` item may carry a dedicated .cube colour LUT, separate from its `effects` chain, applied right after the layer's grade and before any stacked effects:
1664
+ > Part of the `greenlight-dash` skill. These are item fields, not effects: `colorCorrection` on a video or image item, `lut` on an adjustment item. Read [video-projects.md](video-projects.md) for the item schema and the `filter*` grade fields.
1350
1665
 
1351
- ```json
1352
- {
1353
- "lut": { "name": "Teal-Orange", "size": 33, "domainMin": 0, "domainMax": 1, "data": "<base64 RGB lattice>" },
1354
- "lutAmount": 100
1355
- }
1356
- ```
1666
+ ## Contents
1357
1667
 
1358
- `data` is the parsed lattice (size³ RGB bytes, red-fastest order, base64; max size 33 — the editor resamples bigger .cube files on import), so the look is self-contained in the project JSON. `lutAmount` (0–100 %) is the intensity and is keyframable via a standard `lutAmount` track. **Don't author `data` by hand** — parse a real .cube file into the lattice, or copy a `lut` object from another item. On load, a malformed `lut` (wrong size/length) is silently dropped.
1668
+ - [Color Correction (`item.colorCorrection`)](#color-correction-itemcolorcorrection)
1669
+ - [Adjustment-layer LUT](#adjustment-layer-lut)
1359
1670
 
1360
1671
  ## Color Correction (`item.colorCorrection`)
1361
1672
 
1362
- A per-layer colour fix in the Lumetri mould for `video` and `image` items. It runs on the layer's own pixels inside its clip shader — before FILTERS, Layer/Overlay Effects, masks and the transition — so it costs no extra pass and appears identically in preview and every render. The stages always run in this order: **white balance → tone → levels → color → wheels → HSL**, then `mix` blends the result back over the original.
1673
+ A per-layer colour fix in the Lumetri mould for `video` and `image` items. It runs on the layer's own pixels inside its clip shader — before FILTERS, Layer/Overlay Effects, masks and the transition — so it costs no extra pass and appears identically in preview and every render. The stages always run in this order: **details / sharpen → white balance → tone → levels → color → wheels → HSL**, then `mix` blends the result back over the original.
1363
1674
 
1364
1675
  Use it, not the `filter*` fields, to fix a photo or clip: FILTERS are display-space multipliers that clip and shift hue, while this works in linear light, keeps hue when brightening, and carries per-channel levels for removing a cast. Keep FILTERS for stylised looks (sepia, invert, blur).
1365
1676
 
@@ -1378,7 +1689,8 @@ Use it, not the `filter*` fields, to fix a photo or clip: FILTERS are display-sp
1378
1689
  },
1379
1690
  "color": { "on": true, "saturation": 110, "vibrance": 20 },
1380
1691
  "wheels": { "on": true, "shadows": { "x": 0.3, "y": -0.2, "l": 0 }, "midtones": { "x": 0, "y": 0, "l": 0 }, "highlights": { "x": -0.1, "y": 0.15, "l": 0 } },
1381
- "hsl": { "on": true, "hue": [0, 0, 0, 0, 0, 0, 0, 0], "sat": [0, 0, 0, -30, 0, 15, 0, 0], "lum": [0, 0, 0, 0, 0, -20, 0, 0] }
1692
+ "hsl": { "on": true, "hue": [0, 0, 0, 0, 0, 0, 0, 0], "sat": [0, 0, 0, -30, 0, 15, 0, 0], "lum": [0, 0, 0, 0, 0, -20, 0, 0] },
1693
+ "detail": { "on": true, "details": 20, "sharpen": 35 }
1382
1694
  }
1383
1695
  ```
1384
1696
 
@@ -1387,10 +1699,11 @@ Use it, not the `filter*` fields, to fix a photo or clip: FILTERS are display-sp
1387
1699
  - **Levels** — Photoshop levels on 0–255 values: per-channel `r` / `g` / `b` first, then the `rgb` master. `inB` < `inW` (kept ≥ 2 apart), `gamma` 0.1–9.99 (above 1 brightens the midtones), `outB` / `outW` the output range. Per-channel black/white points are the classic way to remove a cast.
1388
1700
  - **Color** — `saturation` 0–200 (100 = unchanged, 0 = greyscale); `vibrance` −100…100 boosts the least saturated colours most and protects skin tones.
1389
1701
  - **Wheels** — `shadows` / `midtones` / `highlights` each `{ x, y, l }`: `(x, y)` is a direction on the colour wheel inside the unit disc, laid out like a vectorscope (x = blue-yellow Cb axis, y = red-cyan Cr axis; red is up-left, blue right, green down-left); radius is strength. `l` (−100…100) lifts or lowers that tonal range.
1390
- - **HSL** — eight bands in this fixed order: Red, Orange, Yellow, Green, Aqua, Blue, Purple, Magenta (centred at 0°, 30°, 60°, 120°, 180°, 240°, 270°, 300°). `hue` shifts the band up to ±30°, `sat` scales its saturation (−100 = grey), `lum` darkens / brightens it. Greys are never affected.
1702
+ - **HSL** — eight bands in this fixed order: Red, Orange, Yellow, Green, Aqua, Blue, Purple, Magenta (centred at 0°, 30°, 60°, 120°, 180°, 240°, 270°, 300°). Each list holds eight values from −100 to 100 (values outside are clamped on load): `hue` shifts the band (±100 = the full ±30°), `sat` scales its saturation (−100 = grey), `lum` darkens / brightens it. Greys are never affected.
1703
+ - **Details** (the panel's last section) — `details` −100…100 is local contrast at a ~2–4 px scale: above 0 brings out texture (skin, fabric, foliage), below 0 smooths fine texture and keeps the edges (−30…−60 softens skin). `sharpen` 0…100 is an unsharp mask at 1 px: 20–50 is a normal sharpen, 100 is very strong. Both act on brightness only, are measured in composition pixels (the preview and the export match), keep the mean brightness, and leave a cut-out's edge alone (no halo). Auto and the pickers ignore them.
1391
1704
  - Each stage's `on: false` bypasses it and keeps its values; `enabled: false` mutes the whole correction; `mix` (0–100) blends it in.
1392
1705
  - Omit the field (or leave everything at the defaults shown in the ranges above) for no correction — a layer without one costs nothing. On load, values are clamped to these ranges, and an object with nothing off its defaults is dropped.
1393
- - Every number is keyframable as a `cc:<path>` track in `propertyKeyframes` — `path` is the value's address inside the object (`cc:wb.temp`, `cc:tone.exposure`, `cc:levels.r.inB`, `cc:wheels.shadows.x`, `cc:hsl.sat.5`, `cc:mix`). A track overrides the static value, so an animated correction needs no stored `colorCorrection` at all; the `on` / `enabled` switches are not animatable. Example — a grade that warms up over the clip:
1706
+ - Every number is keyframable as a `cc:<path>` track in `propertyKeyframes` — `path` is the value's address inside the object (`cc:wb.temp`, `cc:tone.exposure`, `cc:levels.r.inB`, `cc:wheels.shadows.x`, `cc:hsl.sat.5`, `cc:detail.sharpen`, `cc:mix`). A track overrides the static value, so an animated correction needs no stored `colorCorrection` at all; the `on` / `enabled` switches are not animatable. Example — a grade that warms up over the clip:
1394
1707
 
1395
1708
  ```json
1396
1709
  "propertyKeyframes": {
@@ -1401,12 +1714,15 @@ Use it, not the `filter*` fields, to fix a photo or clip: FILTERS are display-sp
1401
1714
  }
1402
1715
  ```
1403
1716
 
1404
- ## Render and editing rules
1717
+ ## Adjustment-layer LUT
1718
+
1719
+ An `adjustment` item may carry a dedicated .cube colour LUT, separate from its `effects` chain, applied right after the layer's grade and before any stacked effects:
1405
1720
 
1406
- 1. `GET` the project immediately before editing and preserve unrelated fields/channels.
1407
- 2. Write Overlay Effects only to `effects`; write Running Glow and the Depth effects only to `layerEffects`.
1408
- A Depth effect without a `depthMap` entry renders nothing — add the source alongside the consumer.
1409
- 3. Keep every `eid` stable across edits and reorders.
1410
- 4. Remove associated keyframe tracks when deleting an effect.
1411
- 5. `PUT` the complete `data` object back with `expected_version` from the GET response. On 409, reload and reapply only the intended edit; never drop the version check.
1412
- 6. Render with `/render-onserver` or the desktop renderer, never the fast `/render`.
1721
+ ```json
1722
+ {
1723
+ "lut": { "name": "Teal-Orange", "size": 33, "domainMin": 0, "domainMax": 1, "data": "<base64 RGB lattice>" },
1724
+ "lutAmount": 100
1725
+ }
1726
+ ```
1727
+
1728
+ `data` is the parsed lattice (size³ RGB bytes, red-fastest order, base64; max size 33 — the editor resamples bigger .cube files on import), so the look is self-contained in the project JSON. `lutAmount` (0–100 %) is the intensity and is keyframable via a standard `lutAmount` track. **Don't author `data` by hand** — parse a real .cube file into the lattice, or copy a `lut` object from another item. On load, a malformed `lut` (wrong size/length) is silently dropped.