Custom HTML-in-canvas presentationsv4.0.456
Build a presentation that runs the entering and exiting scenes through a WebGL2 fragment shader using the experimental HTML-in-canvas APIs.
HTML-in-canvas requires Chrome Canary with chrome://flags/#canvas-draw-element enabled in the browser. For server-side renders, pass --allow-html-in-canvas (or Config.setAllowHtmlInCanvasEnabled(true)). It does not work in Firefox or Safari.
When to use this
Use a custom HTML-in-canvas presentation when:
- Your effect needs both scenes blended pixel-by-pixel through a fragment shader (zoom blur, dissolves, displacement maps, ripple, page curl, etc.)
- The CSS-only approach in Custom presentations cannot express the effect
If a CSS clip-path, transform, filter or mask can express the effect, prefer the CSS-based custom presentation. It works in every browser and renderer.
Concept
Remotion captures both scenes via drawElementImage() and passes them to your shader as two ElementImage textures (prevImage and nextImage) along with the transition time (0 → 1). Your shader writes the blended result to an offscreen WebGL2 canvas, which Remotion then composites into the frame.
You implement a single function — HtmlInCanvasShader<Props> — and pass it to makeHtmlInCanvasPresentation() to get back a TransitionPresentation constructor.
Boilerplate
my-shader.tsximport type {HtmlInCanvasShader } from '@remotion/transitions'; import {makeHtmlInCanvasPresentation } from '@remotion/transitions'; export typeMyShaderProps = {intensity ?: number; }; constVERTEX_SHADER = `#version 300 es in vec2 a_pos; out vec2 v_uv; void main() { v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5); gl_Position = vec4(a_pos, 0.0, 1.0); }`; constFRAGMENT_SHADER = `#version 300 es precision highp float; uniform sampler2D u_prev; uniform sampler2D u_next; uniform float u_time; in vec2 v_uv; out vec4 outColor; void main() { // u_time = 1 → fully prev, u_time = 0 → fully next outColor = mix( texture(u_next, v_uv), texture(u_prev, v_uv), u_time ); }`; export constmyShader :HtmlInCanvasShader <MyShaderProps > = (canvas ) => { constgl =canvas .getContext ('webgl2', {premultipliedAlpha : true}); if (!gl ) { throw newError ('WebGL2 unavailable'); } // Compile + link program, create textures, bind a fullscreen quad... // (see "Source references" below for a full implementation) return {clear : () => {gl .clearColor (0, 0, 0, 0);gl .clear (gl .COLOR_BUFFER_BIT ); },cleanup : () => { // Release WebGL resources here },draw : ({prevImage ,nextImage ,width ,height ,time ,passedProps }) => { // Upload prevImage / nextImage to textures via gl.texElementImage2D // Set uniforms, draw the quad }, }; }; export constmyPresentation =makeHtmlInCanvasPresentation (myShader );
Use it like any other presentation:
MyComp.tsximport {linearTiming ,TransitionSeries } from '@remotion/transitions'; import {AbsoluteFill } from 'remotion'; constSceneA :React .FC = () => <AbsoluteFill style ={{backgroundColor : '#0b84f3'}} />; constSceneB :React .FC = () => <AbsoluteFill style ={{backgroundColor : 'pink'}} />; export constMyComp :React .FC = () => { return ( <TransitionSeries > <TransitionSeries .Sequence durationInFrames ={60}> <SceneA /> </TransitionSeries .Sequence > <TransitionSeries .Transition presentation ={myPresentation ({})}timing ={linearTiming ({durationInFrames : 30})} /> <TransitionSeries .Sequence durationInFrames ={60}> <SceneB /> </TransitionSeries .Sequence > </TransitionSeries > ); };
API reference
The signature of makeHtmlInCanvasPresentation() and the HtmlInCanvasShader<Props> callback contract — clear, cleanup, draw({prevImage, nextImage, width, height, time, passedProps}) — are documented on its own page.
Adapting GLSL transitions from gl-transitions.com
gl-transitions.com is a curated catalog of community-contributed fragment shaders licensed under MIT — a great starting point.
To port one to a Remotion HTML-in-canvas presentation:
ReplacegetFromColor(uv) with texture(u_prev, uv) and getToColor(uv) with texture(u_next, uv).progress to Remotion's reversed convention with float progress = 1.0 - u_time; at the top of main().transition() body inside void main() { outColor = transition(v_uv); } (with out vec4 outColor; declared).Example implementations
See the following example implementations:
zoom-blur.tsxzoom-in-out.tsx— adapted from a gl-transitions shader
See also
makeHtmlInCanvasPresentation()— API reference- HTML-in-canvas
- Custom presentations — the CSS-based path
- Contributing a new presentation