remotion-dev/remotion · error · Error

Failed to create levels shader

Error message

Failed to create levels shader

What it means

Thrown by `compileShader()` inside the levels effect when `gl.createShader()` returns `null`. The WebGL2 spec returns null only on context loss, an invalid enum, or resource/GPU exhaustion — the effect cannot build its shader program and bails before compiling.

Source

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

	vec3 normalized = clamp(
		(color - vec3(uBlackPoint)) / (uWhitePoint - uBlackPoint),
		0.0,
		1.0
	);
	vec3 corrected = pow(normalized, vec3(1.0 / uGamma));

	fragColor = vec4(corrected * alpha, alpha);
}
`;

const compileShader = (
	gl: WebGL2RenderingContext,
	type: number,
	source: string,
): WebGLShader => {
	const shader = gl.createShader(type);
	if (!shader) {
		throw new Error('Failed to create levels shader');
	}

	gl.shaderSource(shader, source);
	gl.compileShader(shader);
	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		const log = gl.getShaderInfoLog(shader);
		gl.deleteShader(shader);
		throw new Error(`Levels shader compile failed: ${log ?? '(no log)'}`);
	}

	return shader;
};

const createProgram = (gl: WebGL2RenderingContext): WebGLProgram => {
	const vertexShader = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER);
	const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER);
	const program = gl.createProgram();
	if (!program) {

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Reduce the number of live WebGL2 contexts — close other Studio tabs or lower concurrency (`--concurrency`).
  2. Ensure the renderer has a working GPU: avoid `--disable-gpu` in headless Chrome; use a GPU-enabled Chromium or swiftshader build that still exposes WebGL2.
  3. Listen for `webglcontextlost` and tear down/recreate the effect, or retry the render after the context is restored.
  4. If running in CI/Lambda, confirm the Chrome binary and GPU flags match Remotion's supported headless setup.
Defensive patterns

Strategy: try-catch

Validate before calling

function glReady(gl: WebGL2RenderingContext | null): boolean {
  if (!gl) return false;
  // Probe a throwaway shader allocation cheaply.
  const probe = gl.createShader(gl.VERTEX_SHADER);
  if (!probe) return false;
  gl.deleteShader(probe);
  return gl.isContextLost() === false;
}

Type guard

const hasWebGL2 = (): boolean => {
  const c = document.createElement('canvas');
  return !!c.getContext('webgl2');
};

Try / catch

try {
  levels({...})(...);
} catch (err) {
  if (/Failed to create levels shader/.test(String(err))) {
    // context loss / no GPU: skip effect or schedule a retry after restore
    console.warn('levels() unavailable, falling back to no-op', err);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: The WebGL2 context was lost (e.g. tab backgrounded, GPU driver reset), too many simultaneous WebGL contexts are open (browsers cap around 16), the GPU is unavailable in a headless/CI environment, or video memory is exhausted.

Common situations: Rendering many compositions concurrently on the same machine; running Remotion Lambda/headless Chrome with `--disable-gpu` or swiftshader; a long-running Studio tab that lost its context after suspend; opening dozens of effect previews at once.

Related errors


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