Colour and grading
Footage rarely comes out of the camera looking the way the video should: too dark, a little blue, flat. The colour effects fix that in the file, as <Effect> children of a clip, or of an <Adjustment> to grade everything beneath it for a stretch of time. They run as shaders in the preview and the export alike, and every param can be animated by a <Keyframe> inside the effect, naming the param: <Keyframe id="k1" param="stops" at={0} value={0} />.
<Clip id="interview" src="asset:interview">
<Effect id="expose" type="exposure" params={{ stops: 0.5 }} />
<Effect id="balance" type="whiteBalance" params={{ temperature: 0.15 }} />
<Effect id="tone" type="curves" params={{ rgb: [[0, 0.03], [0.25, 0.22], [0.75, 0.8], [1, 1]] }} />
<Effect id="pop" type="vibrance" params={{ amount: 0.3 }} />
</Clip>Effects run in the order they appear in the file, so write them in the order a colourist works: correct the light and the white first, then shape the tones, then give the picture its look.
Correct
exposure brightens or darkens by stops, from -5 to 5, the way a camera's exposure does: +1 doubles the light. It works where light is linear, so highlights keep their colour instead of washing out as they would under a plain brightness filter.
whiteBalance removes a colour cast. temperature (-1 to 1, a stop of colour temperature each way) warms the picture toward amber or cools it toward blue; tint (-1 to 1) moves it toward magenta or green. It adapts the picture as if it had been lit by a different daylight, so greys stay grey while the cast goes. Where WebGL is missing it falls back on a CSS approximation.
import { Clip, Composition, Effect, Track } from "@cutcraft/kit";
// One shot cooled toward blue, then warmed toward amber, by white balance.
export default function Video() {
return (
<Composition fps={30} width={960} height={540}>
<Track id="main">
<Clip id="cool" src="asset:product" in={0} out={1.5} fit="cover">
<Effect id="cold" type="whiteBalance" params={{ temperature: -0.6 }} />
</Clip>
<Clip id="warm" src="asset:product" in={1.5} out={3} fit="cover">
<Effect id="hot" type="whiteBalance" params={{ temperature: 0.6 }} />
</Clip>
</Track>
</Composition>
);
}levels sets the black point and the white point (black, white, 0 to 1) and bends the midtones with gamma (above 1 lifts them).
shadowsHighlights lifts the shadows or holds back the highlights (shadows, highlights, -1 to 1, up to two stops each way). It judges each pixel by the light around it (radius, the blur's standard deviation in pixels, 10 by default), so a dark area lifts as a whole and keeps its detail and contrast.
Shape the tones
curves maps input brightness to output brightness through points you give, for all channels (rgb) and for each (red, green, blue). A point is [input, output], both 0 to 1, left to right. The curve is a smooth monotone spline through the points, so it never overshoots between them. Two points [[0, 0], [1, 1]] change nothing, which is each curve's default.
// A gentle S: deeper shadows, brighter highlights, and slightly warmer whites.
<Effect id="s" type="curves" params={{ rgb: [[0, 0], [0.25, 0.2], [0.75, 0.82], [1, 1]], blue: [[0, 0], [1, 0.95]] }} />vibrance adds saturation to muted colours and leaves strong ones nearly alone (amount, -1 to 1), which is kinder to skin than saturation.
Give it a look
duotone maps the picture's brightness from one colour (dark) to another (light), mixed with the original by amount.
A look from a LUT
A look made in a grading tool usually leaves it as a 3D LUT in a .cube file. Add it to the manifest like media, then grade with the built-in lut effect:
npx cutcraft add teal-orange.cube<Effect id="look" type="lut" params={{ src: "asset:teal-orange", amount: 0.8 }} />amount mixes the graded colour with the original, from 0 to 1. The LUT is read with trilinear filtering; a table whose DOMAIN_MIN and DOMAIN_MAX are not 0 and 1 is resampled onto 0 to 1 when it loads. 1D tables are not read.
Where there is no WebGL
The colour effects need WebGL, which every browser the player runs in has, and the export always has. Where it is missing, exposure, vibrance and levels fall back on a CSS filter that comes close, and the others are left out with a warning. Effects that read a file (lut) work on clips, nested sequences and adjustments, not yet on overlays.