Skip to content

Effects

View .mdllms.txt

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.

import { Blur, Curves, FilmGrain, Vignette } from "framefields"; // or "framefields/effects"
// 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 }).

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
  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.

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

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.

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
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)

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