remotion-dev/remotion · error · TypeError

"blackPoint" must be less than "whitePoint", but got ${JSON.

Error message

"blackPoint" must be less than "whitePoint", but got ${JSON.stringify(blackPoint)} and ${JSON.stringify(whitePoint)}

What it means

Thrown by the `levels()` WebGL2 effect's parameter validator when `blackPoint` is not strictly less than `whitePoint`. Both values are unit-interval [0,1] tonal endpoints (analogous to Photoshop's Levels), and an inverted or equal range would produce a degenerate (fully clipped or NaN) tonal remap, so the library rejects it eagerly with a TypeError before touching the GPU.

Source

Thrown at packages/effects/src/levels.ts:93

const resolve = (params: LevelsParams): LevelsResolved => ({
	blackPoint: params.blackPoint ?? DEFAULT_BLACK_POINT,
	whitePoint: params.whitePoint ?? DEFAULT_WHITE_POINT,
	gamma: params.gamma ?? DEFAULT_GAMMA,
});

const validateLevelsParams = (params: LevelsParams): void => {
	assertEffectParamsObject(params, 'Levels');
	assertOptionalFiniteNumber(params.blackPoint, 'blackPoint');
	assertOptionalFiniteNumber(params.whitePoint, 'whitePoint');
	assertOptionalFiniteNumber(params.gamma, 'gamma');

	const {blackPoint, whitePoint, gamma} = resolve(params);
	validateUnitInterval(blackPoint, 'blackPoint');
	validateUnitInterval(whitePoint, 'whitePoint');

	if (blackPoint >= whitePoint) {
		throw new TypeError(
			`"blackPoint" must be less than "whitePoint", but got ${JSON.stringify(blackPoint)} and ${JSON.stringify(whitePoint)}`,
		);
	}

	if (gamma < MIN_GAMMA) {
		throw new TypeError(
			`"gamma" must be >= ${MIN_GAMMA}, but got ${JSON.stringify(gamma)}`,
		);
	}

	if (gamma > MAX_GAMMA) {
		throw new TypeError(
			`"gamma" must be <= ${MAX_GAMMA}, but got ${JSON.stringify(gamma)}`,
		);
	}
};

const VERTEX_SHADER = /* glsl */ `#version 300 es

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Ensure `blackPoint` is strictly less than `whitePoint` (e.g. blackPoint: 0.1, whitePoint: 0.9).
  2. If animating both, clamp or re-order keyframes so the curves never cross at any frame.
  3. If you intentionally want full clipping, leave `blackPoint` at 0 and only lower `whitePoint`, or vice versa.
  4. Double-check that a missing `whitePoint` (default 1) is still greater than your chosen `blackPoint`.

Example fix

// before
levels({blackPoint: 0.8, whitePoint: 0.2})

// after
levels({blackPoint: 0.2, whitePoint: 0.8})
Defensive patterns

Strategy: validation

Validate before calling

const isValidLevels = (p) =>
  (p.blackPoint ?? 0) >= 0 &&
  (p.blackPoint ?? 0) <= 1 &&
  (p.whitePoint ?? 1) >= 0 &&
  (p.whitePoint ?? 1) <= 1 &&
  (p.blackPoint ?? 0) < (p.whitePoint ?? 1);

if (!isValidLevels(params)) throw new Error('blackPoint must be < whitePoint');

Type guard

const isLevelsSafe = (p: {
  blackPoint?: number;
  whitePoint?: number;
}): boolean =>
  (p.blackPoint ?? 0) < (p.whitePoint ?? 1);

Prevention

When it happens

Trigger: Calling `levels({blackPoint: 0.5, whitePoint: 0.5})`, `levels({blackPoint: 0.8, whitePoint: 0.2})`, or omitting `whitePoint` while setting `blackPoint` to 1 (since the default `whitePoint` is 1, equality trips the `>=` check). Also triggered by any computed/slider value where the two cross.

Common situations: Studio Visual Mode sliders dragged past each other; animation where `blackPoint`/`whitePoint` are interpolated with `interpolate()` and the keyframes cross; copy-pasted config from a node-based editor that allows inverted ranges; setting `blackPoint: 1` to clip everything without also raising `whitePoint`.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/fd6367177f635fdf. Report an issue: GitHub.