Skip to main content

Custom HTML-in-canvas presentationsv4.0.456

Build a presentation that runs the entering and exiting scenes through a shader using the HTML-in-canvas APIs.

warning

HTML-in-canvas requires Google Chrome with chrome://flags/#canvas-draw-element enabled in the browser.

When to use this​

Use this when you cannot implement the presentation in CSS.
Prefer CSS because it has wider browser support.

Concept​

Remotion rasterizes both scenes and passes them to your shader as two OffscreenCanvas sources (prevImage and nextImage) along with the transition time (0 → 1). Upload them as textures using texImage2D() and write the blended result to the supplied offscreen WebGL2 canvas. Remotion composites that canvas into the frame.

You implement a single function — HtmlInCanvasShader<Props> — and pass it to makeHtmlInCanvasPresentation() to get back a TransitionPresentation constructor.

Boilerplate​

my-shader.tsx
import type {HtmlInCanvasShader} from '@remotion/transitions'; import {makeHtmlInCanvasPresentation} from '@remotion/transitions'; export type MyShaderProps = { intensity?: number; }; const VERTEX_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); }`; const FRAGMENT_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 const myShader: HtmlInCanvasShader<MyShaderProps> = (canvas) => { const gl = canvas.getContext('webgl2', {premultipliedAlpha: true}); if (!gl) { throw new Error( 'WebGL2 unavailable. See https://remotion.dev/docs/troubleshooting/webgl2-context.', ); } // 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.texImage2D // Set uniforms, draw the quad }, }; }; export const myPresentation = makeHtmlInCanvasPresentation(myShader);

Use it like any other presentation:

MyComp.tsx
import {linearTiming, TransitionSeries} from '@remotion/transitions'; import {AbsoluteFill} from 'remotion'; const SceneA: React.FC = () => <AbsoluteFill style={{backgroundColor: '#0b84f3'}} />; const SceneB: React.FC = () => <AbsoluteFill style={{backgroundColor: 'pink'}} />; export const MyComp: 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:

Replace getFromColor(uv) with texture(u_prev, uv) and getToColor(uv) with texture(u_next, uv).
2
Map progress to Remotion's reversed convention with float progress = 1.0 - u_time; at the top of main().
3
Wrap the transition() body inside void main() { outColor = transition(v_uv); } (with out vec4 outColor; declared).

Example implementations​

See the following example implementations:

See also​