{"record":{"id":"742de7895bce4bb8","repo":"remotion-dev/remotion","slug":"failed-to-create-webgl-buffer-742de7","errorCode":null,"errorMessage":"Failed to create WebGL buffer","messagePattern":"Failed to create WebGL buffer","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/effects/src/noise-displacement.ts","lineNumber":478,"sourceCode":"\tconst fs = compileShader(gl, gl.FRAGMENT_SHADER, NOISE_DISPLACEMENT_FS);\n\tconst program = linkProgram(gl, vs, fs);\n\tgl.deleteShader(vs);\n\tgl.deleteShader(fs);\n\n\tconst vao = gl.createVertexArray();\n\tif (!vao) {\n\t\tthrow new Error('Failed to create WebGL vertex array');\n\t}\n\n\tgl.bindVertexArray(vao);\n\n\tconst data = new Float32Array([\n\t\t-1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1,\n\t]);\n\n\tconst vbo = gl.createBuffer();\n\tif (!vbo) {\n\t\tthrow new Error('Failed to create WebGL buffer');\n\t}\n\n\tgl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n\tgl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);\n\n\tconst aPos = gl.getAttribLocation(program, 'aPos');\n\tconst aUv = gl.getAttribLocation(program, 'aUv');\n\tgl.enableVertexAttribArray(aPos);\n\tgl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);\n\tgl.enableVertexAttribArray(aUv);\n\tgl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);\n\n\tgl.bindVertexArray(null);\n\n\tconst texture = gl.createTexture();\n\tif (!texture) {\n\t\tthrow new Error('Failed to create WebGL texture');\n\t}","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/effects/src/noise-displacement.ts#L460-L496","documentation":"Thrown inside setupNoiseDisplacement (packages/effects/src/noise-displacement.ts:478) when gl.createBuffer() returns null while allocating the fullscreen-quad vertex buffer for the noiseDisplacement effect. A null return is the WebGL spec's signal that the WebGL2 context is lost, out of GPU memory, or has too many live buffer objects, so the buffer could not be created. Because setup runs on the first apply of the effect, this aborts the current Remotion render frame with no in-band fallback.","triggerScenarios":"Applying noiseDisplacement({...}) and rendering/previewing at the instant the canvas WebGL2 context is lost (GPU reset, WEBGL_lose_context), or when GPU memory is exhausted; running enough concurrent WebGL2 effects that the per-context live-buffer budget is hit.","commonSituations":"Headless Chrome rendering on a GPU-less CI host under SwiftShader memory pressure; a long Studio session leaking GL contexts; Chrome's GPU process crashing mid-render; Windows ANGLE/D3D hitting resource limits with many parallel compositions.","solutions":["Reduce the number of concurrently-live WebGL2 effects/compositions and ensure each effect's cleanup callback runs between compositions so buffers are reclaimed.","Render on a host with a working GPU, or launch headless Chrome with WebGL enabled (e.g. --use-gl=angle / --enable-webgl) so the context is not starved or lost.","Listen for the canvas 'webglcontextlost' event and treat it as transient: abort the frame and re-render after 'webglcontextrestored'.","If the environment cannot supply a stable WebGL2 context, render the composition without the noiseDisplacement effect as a fallback."],"exampleFix":"// before: effect setup throws and aborts the whole render\nimport {noiseDisplacement} from '@remotion/effects';\n\n// after: probe WebGL2 health before relying on the effect, fall back otherwise\nconst probe = document.createElement('canvas');\nconst gl = probe.getContext('webgl2');\nconst canUse = !!gl && !!gl.createBuffer(); // null => context cannot allocate\nconst effects = canUse ? [noiseDisplacement({center: [0.5, 0.5], radius: 0.4})] : [];","handlingStrategy":"try-catch","validationCode":"// Probe that WebGL2 can allocate a buffer before relying on noiseDisplacement\nfunction canAllocateNoiseDisplacementBuffer(): boolean {\n  try {\n    const c = document.createElement('canvas');\n    const gl = c.getContext('webgl2');\n    if (!gl || gl.isContextLost()) return false;\n    const buf = gl.createBuffer();\n    const ok = !!buf;\n    if (buf) gl.deleteBuffer(buf);\n    return ok;\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  scene.push(noiseDisplacement({center: [0.5, 0.5], radius: 0.4}));\n} catch (err) {\n  if (/Failed to create WebGL buffer/.test(String(err?.message))) {\n    // skip the effect; render without it\n  } else {\n    throw err;\n  }\n}","preventionTips":["Run effect cleanup between compositions so GL buffers are reclaimed.","Cap the number of simultaneous WebGL2 effects in one render.","Render with GPU-enabled headless Chrome and handle webglcontextlost events.","Probe allocation health on the render host before committing a GPU-heavy render."],"tags":["webgl","gpu","noise-displacement","effects","context-loss","rendering"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}