# Effects

> Attach GPU effects to layers, whole compositions, regions and media chains; order them; drive their props with signals.

Effects are typed classes. Construct one with props, attach it, and the GPU runs it each frame. The full list with every prop is in the [Effects reference](/docs/reference/effects/).

```ts
import { Blur, Curves, FilmGrain, Vignette } from "framefields";          // or "framefields/effects"
```

## Where to attach

```ts
// 1. One layer (chain as many as you like, in order)
Layer.video("assets/footage/plate.mp4", { fit: "cover" })
  .withEffect(new Curves({ master: [{ x: 0, y: 0.05 }, { x: 0.5, y: 0.52 }, { x: 1, y: 0.95 }] }))
  .withEffect(new Vignette({ strength: 40, radius: 0.85, softness: 0.6 }));

// 2. The whole frame
comp.apply(new FilmGrain({ strength: 12, size: 1.5, animated: true }));

// 3. A region of a plate (rectangle, rounded, or a tracked object)
comp.section({
  source: "assets/footage/interview.mp4",
  x: 400, y: 200, width: 300, height: 300, borderRadius: 150,
  effects: [new Blur({ strength: 25, blurType: "Gaussian" })],
});

// 4. A clip outside the layer tree
import { ApplyLUT, Crop, Media } from "framefields";
const clip = Media.video("assets/footage/raw_log.mp4")
  .apply(new ApplyLUT({ lutUrl: "assets/luts/cinematic.cube", intensity: 1 }))
  .apply(new Crop({ cropType: "rectangle", topPercentage: 10, heightPercentage: 80 }));
await clip.renderFrame({ atMs: 1500 });
```

`.withEffects([a, b])` attaches several at once. Lowercase factories work too: `Effect.blur({ strength: 9 })`.

## Value ranges

Ranges come from each effect's schema; out-of-range values warn (set `FRAMEFIELDS_STRICT_PROPS=1` to throw). Many "strength" props are **0–100**, not 0–1:

| Effect | Prop | Range | Default | Tasteful |
| --- | --- | --- | --- | --- |
| `Vignette` | `strength` | 0–100 | 50 | 20–45 |
| `FilmGrain` | `strength` | 0–100 | 15 | 8–20 |
| `FilmGrain` | `size` | 0.5–4 | 1.5 | 1.2–1.8 |
| `Blur` | `strength` | 0–100 | 5 | — |
| `ColorBalance` | `cyanRed` / `magentaGreen` / `yellowBlue` | −100–100 | 0 | ±4–12 |
| `ColorKey` | `similarity`, `smoothness` | 0–1 | 0.4, 0.1 | — |
| `Modulate` | `brightness`, `contrast`, `saturation` | multiplier | 1 | 0.9–1.2 |

## Order

1. Tonal grading first: `Curves`, `Levels`, `ColorBalance`, `SelectiveColor`, `ApplyLUT`.
2. Then optics: `Blur`, `DepthOfField`, `Vignette`.
3. Grain last, as the outermost pass: `FilmGrain`.

For a still export, set `animated: false` on `FilmGrain` so the frame is deterministic across renders of different lengths.

## Drive props with signals

Any numeric effect prop accepts a signal instead of a number. The uniform updates per frame without rebuilding the pipeline.

```ts
import { Blur, Layer, Signal } from "framefields";

const breathe = Signal.sine({ frequency: 0.5, min: 0, max: 20 }); // Hz, sampled at the composition frame

Layer.image("assets/images/bg.jpg").withEffect(new Blur({ strength: breathe, blurType: "Gaussian" }));
```

Vision signals plug in the same way, e.g. `new Vignette({ strength: vision.segmentation.subject.coverage })`. See [Vision](/docs/guides/vision/).

## Temporal effects (`Trails`, `Datamosh`)

Temporal effects process frame history to create motion persistence, light painting, and optical flow artifacts:

```ts
import { Datamosh, Layer, Trails } from "framefields";

// Motion echoes, feedback tunnels, and light painting
Layer.video("assets/footage/dancer.mp4").withEffect(
  new Trails({
    decay: 0.92,       // history retained per frame (0.8 = short echoes, 0.97 = long light trails)
    mode: "blend",     // "blend" | "over" | "add" (glow accumulation) | "max"
    zoom: 1.01,        // history scale per frame (pulls past into a tunnel)
    rotation: 0.5,     // history rotation per frame in degrees
    hueShift: 2,       // psychedelic hue rotation per frame
  }),
);

// Pixel smearing along motion vectors
Layer.video("assets/footage/action.mp4").withEffect(
  new Datamosh({
    keyframes: [0, 60, 120], // clean I-frames; pixels drag along optical flow between them
    strength: 1.5,           // how far pixels are dragged along motion
    bleed: 0.04,             // clean picture mixed back in each frame
  }),
);
```

## Families

| Family | Effects |
| --- | --- |
| Grading | `Curves`, `Levels`, `ColorBalance`, `SelectiveColor`, `GradientMap`, `ShadowsHighlights`, `ApplyLUT`, `ExtractLUT`, `Modulate`, `ChannelSplitter`, `ChannelMerger` |
| Texture & lens | `FilmGrain`, `Vignette`, `Blur`, `MotionBlur`, `UnsharpMask`, `HighPass`, `HalftoneScreen`, `TileOffset`, `TemporalDeflicker` |
| Distortion | `DisplacementMap`, `Liquify`, `MeshWarp`, `CornerPin`, `Rotate`, `Flip`, `KenBurns`, `ResizerScaler` |
| Temporal | `Trails`, `Datamosh` |
| 3D & light | `Relight3D`, `SSAO`, `PBRGlass`, `DepthOfField`, `RefractionCaustics3D`, `CameraParallax3D` |
| Mattes & keying | `ColorKey`, `Crop`, `RefineEdge`, `MaskMath`, `PatchHeal` |
| Generators & style | `NoiseGenerator`, `ProceduralVFX`, `Paint`, `LayerStyle` |
| Audio | `AudioFade`, `AudioCompressor`, `AudioParametricEq`, `AudioReverb`, `AudioDelay`, `AudioNoiseGate`, `StereoPanning`, `AudioSignalExtractor` |
| Vision | `Vision` (prefer `withVision`, see [Vision](/docs/guides/vision/)) |

## Restraint

Grain and vignette should be felt, not seen. If a viewer notices the effect before the content, halve it.
