Deft
Ask AI

Search

Search skills, authors and topics

remotion-dev / skills

remotion-markup

Structure Remotion scenes with markup patterns for layout, typography, and reusable video UI.

Install

npx skills add https://github.com/remotion-dev/skills --skill remotion-markup

Using <HtmlInCanvas> in Remotion

Renders children into a <canvas> so you can post-process them with the Canvas 2D API or WebGL.

Only works in Chrome 149+ with the chrome://flags/#canvas-draw-element flag enabled.
Give the user a notice.

Nesting

Nest <HtmlInCanvas> components only when HtmlInCanvas.isNestingSupported() returns true (Chrome 157.0.8080.0 or newer with HTML-in-canvas enabled). Older browsers reject nesting.

Enabling WebGL during renders

If you make use of WebGL during renders, you need to enable it:

From the CLI:

bash
npx remotion render --gl=angle

Set it as the default for Studio and CLI (advised):

ts
import { Config } from "@remotion/cli/config";
Config.setChromiumOpenGlRenderer("angle");

Basic usage

By default, draws to canvas with no effect applied:

tsx
import { HtmlInCanvas } from "remotion";
export const MyComp = () => {  return (    <HtmlInCanvas width={1280} height={720}>      <div style={{ fontSize: 80 }}>        Hello      </div>    </HtmlInCanvas>  );};

2D effect with onPaint

onPaint runs whenever the content updates. Call ctx.drawElementImage(elementImage, 0, 0) to draw the captured DOM.

tsx
import {  AbsoluteFill,  HtmlInCanvas,  type HtmlInCanvasOnPaint,  useCurrentFrame,  useVideoConfig,} from "remotion";import { useCallback } from "react";
export const Blur = () => {  const frame = useCurrentFrame();  const { width, height, fps } = useVideoConfig();
  const onPaint: HtmlInCanvasOnPaint = useCallback(    ({ canvas, elementImage }) => {      const ctx = canvas.getContext("2d");      if (!ctx) throw new Error("Failed to acquire 2D context");
      const blurPx = 4 + 18 * (0.5 + 0.5 * Math.sin((frame / fps) * Math.PI));
      ctx.reset();      ctx.filter = `blur(${blurPx}px)`;      ctx.drawElementImage(elementImage, 0, 0);    },    [frame, fps],  );
  return (    <HtmlInCanvas width={width} height={height} onPaint={onPaint}>      <AbsoluteFill        style={{          justifyContent: "center",          alignItems: "center",          fontSize: 120,        }}      >        <h1>          Hello        </h1>      </AbsoluteFill>    </HtmlInCanvas>  );};

WebGL effects

For WebGL, set up the context, program, and texture in onInit and return a cleanup function. Allocate the texture with gl.texImage2D(..., null) when elementImage changes size. Inside onPaint, upload with gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, elementImage) and draw. Older Chromium versions only expose gl.texElementImage2D(); use it when the new method is absent.

For a fully working minimal example, see https://github.com/remotion-dev/remotion/blob/main/packages/docs/components/demos/HtmlInCanvasDocsDemoWebGL.tsx.

Async onPaint

onPaint may be async. Remotion holds the frame open via delayRender() until the promise resolves. Useful for multi-pass effects with createImageBitmap.