remotion-dev/remotion · critical · Error

Failed to create WebGL texture

Error message

Failed to create WebGL texture

What it means

Thrown when `gl.createTexture()` returns null during lightTrail setup. The texture is the source sampling surface. Per-spec failure means context loss, OUT_OF_MEMORY, or texture-handle exhaustion — the most common real cause is leaking textures by not cleaning up effect state across many setups.

Source

Thrown at packages/effects/src/light-trail/light-trail-runtime.ts:88

};

const createProgram = (
	gl: WebGL2RenderingContext,
	vertexSource: string,
	fragmentSource: string,
): WebGLProgram => {
	const vs = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
	const fs = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
	const program = linkProgram(gl, vs, fs);
	gl.deleteShader(vs);
	gl.deleteShader(fs);
	return program;
};

const createRgbaTexture = (gl: WebGL2RenderingContext): WebGLTexture => {
	const texture = gl.createTexture();
	if (!texture) {
		throw new Error('Failed to create WebGL texture');
	}

	gl.bindTexture(gl.TEXTURE_2D, texture);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	gl.bindTexture(gl.TEXTURE_2D, null);
	return texture;
};

export const setupLightTrail = (target: HTMLCanvasElement): LightTrailState => {
	const gl = target.getContext('webgl2', {
		premultipliedAlpha: true,
		alpha: true,
		preserveDrawingBuffer: true,
	});
	if (!gl) {

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Always pair `setupLightTrail` with `cleanupLightTrail` (GL resources are not GC'd reliably).
  2. Cache the setup keyed by params (the effect already does this — make sure you are not bypassing it).
  3. On `webglcontextlost`, call cleanup and rebuild on restore.
  4. Cap concurrent active WebGL effects per worker.

Example fix

// before
useEffect(() => { setupLightTrail(canvas); }, []); // no cleanup

// after
useEffect(() => {
  const state = setupLightTrail(canvas);
  return () => cleanupLightTrail(state);
}, []);
Defensive patterns

Strategy: fallback

Validate before calling

const texturePoolHealthy = (gl: WebGL2RenderingContext): boolean => {
  const probe = gl.createTexture();
  if (probe) { gl.deleteTexture(probe); return true; }
  return false;
};

Try / catch

useEffect(() => {
  let state: LightTrailState | null = null;
  try { state = setupLightTrail(canvas); } catch (e) { state = null; }
  return () => { if (state) cleanupLightTrail(state); };
}, []);

Prevention

When it happens

Trigger: Repeatedly calling `setupLightTrail` without matching `cleanupLightTrail`; a frame loop that re-allocates state every render; context loss mid-setup.

Common situations: Forgetting cleanup in a hot-reload loop in Studio; long render sessions on Lambda that accumulate textures; many effects stacked and each setup leaks one texture.

Related errors


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