Shaders
You can use WebGL shaders in Remotion by rendering to a <canvas> element.
All animations must be driven by useCurrentFrame() to ensure deterministic rendering during both preview and final output.
Retrieving a WebGL context
Get a reference to a <canvas> element using useRef(), then call canvas.getContext("webgl") to obtain a WebGLRenderingContext. Initialize the GL state once (e.g. compile shaders, set up buffers), then update uniforms each frame based on the current time derived from useCurrentFrame() and useVideoConfig().
Example
The following component renders an animated shader.
The time uniform is computed from frame / fps, making the animation frame-accurate and deterministic.
BurlFigure.tsxtsximport {useCurrentFrame ,useVideoConfig ,AbsoluteFill } from 'remotion';import {useCallback ,useRef ,useEffect } from 'react';constVERTEX_SHADER = `attribute vec2 position;void main() {gl_Position = vec4(position, 0.0, 1.0);}`;constFRAGMENT_SHADER = `#ifdef GL_ESprecision mediump float;#endifuniform float time;uniform vec2 resolution;const float Pi = 3.14159;void main(){vec2 p = 0.001 * gl_FragCoord.xy;for(int i = 1; i < 7; i++){vec2 newp = p;newp.x += 0.6 / float(i) * cos(float(i) * p.y + (time * 20.0) / 10.0 + 0.3 * float(i)) + 400.0 / 20.0;newp.y += 0.6 / float(i) * cos(float(i) * p.x + (time * 20.0) / 10.0 + 0.3 * float(i + 10)) - 400.0 / 20.0 + 15.0;p = newp;}vec3 col = vec3(0.5 * sin(3.0 * p.x) + 0.5, 0.5 * sin(3.0 * p.y) + 0.5, sin(p.x + p.y));gl_FragColor = vec4(col, 1.0);}`;export constBurlFigure :React .FC = () => {constframe =useCurrentFrame ();const {fps ,width ,height } =useVideoConfig ();constcanvasRef =useRef <HTMLCanvasElement >(null);constglRef =useRef <{gl :WebGLRenderingContext ;timeLoc :WebGLUniformLocation ;resLoc :WebGLUniformLocation ;} | null>(null);constinitGl =useCallback ((canvas :HTMLCanvasElement ) => {constgl =canvas .getContext ('webgl');if (!gl ) return null;constcompile = (type : number,src : string) => {consts =gl .createShader (type )!;gl .shaderSource (s ,src );gl .compileShader (s );returns ;};constprogram =gl .createProgram ()!;gl .attachShader (program ,compile (gl .VERTEX_SHADER ,VERTEX_SHADER ));gl .attachShader (program ,compile (gl .FRAGMENT_SHADER ,FRAGMENT_SHADER ));gl .linkProgram (program );gl .useProgram (program );constbuf =gl .createBuffer ();gl .bindBuffer (gl .ARRAY_BUFFER ,buf );gl .bufferData (gl .ARRAY_BUFFER , newFloat32Array ([-1, -1, 1, -1, -1, 1, 1, 1]),gl .STATIC_DRAW );constpos =gl .getAttribLocation (program , 'position');gl .enableVertexAttribArray (pos );gl .vertexAttribPointer (pos , 2,gl .FLOAT , false, 0, 0);return {gl ,timeLoc :gl .getUniformLocation (program , 'time')!,resLoc :gl .getUniformLocation (program , 'resolution')!,};}, []);useEffect (() => {constcanvas =canvasRef .current ;if (!canvas ||glRef .current ) return;glRef .current =initGl (canvas );}, [initGl ]);useEffect (() => {constctx =glRef .current ;if (!ctx ) return;const {gl ,timeLoc ,resLoc } =ctx ;consttime =frame /fps ;gl .viewport (0, 0,gl .canvas .width ,gl .canvas .height );gl .uniform1f (timeLoc ,time );gl .uniform2f (resLoc ,gl .canvas .width ,gl .canvas .height );gl .drawArrays (gl .TRIANGLE_STRIP , 0, 4);}, [frame ,fps ]);return (<AbsoluteFill ><canvas ref ={canvasRef }width ={width }height ={height } /></AbsoluteFill >);};
Shader source: GLSL Sandbox.
Drawing a shader to a DOM element
WebGL shaders can only render into a <canvas> element.
You cannot apply a shader as a post-processing effect to arbitrary DOM elements.
Rendering
When rendering a video that uses WebGL, you need to pass --gl=angle to ensure the headless browser uses the GPU:
bashnpx remotion render MyComp --gl=angle
Without this flag, the headless browser may not have a WebGL context available.