Glitch Cut

Tear a hard cut into displaced slices with channel split and corrupted blocks

Experimental — html-in-canvas

This transition post-processes the real pixels of both scenes through the experimental html-in-canvas browser API. The preview above shows the true effect in Chrome 149+ with chrome://flags/#canvas-draw-element enabled (nested canvases need 152.0.7944.0+). Every other browser — Safari, Firefox, Chrome without the flag — falls back to a CSS approximation of the same cut, so the page and your video still play.

Rendering is supported through the CLI, Studio, Lambda and SSR with --gl=angle, or --gl=swangle on a machine without a GPU. Chrome may change or discontinue the API; the CSS fallback is what keeps your composition renderable if that happens.

Installation

$ pnpm dlx shadcn@latest add @remocn/glitch-cut

Usage

glitchCut() is a Remotion transition presentation. Pass it to a TransitionSeries.Transition between two sequences. The frame breaks into horizontal slices that slide apart, the colour channels separate, and blocks of the frame corrupt into the wrong scene — then each slice hands over to the incoming scene on its own beat around the midpoint, so the cut itself is torn rather than flat. Both ends of the transition settle to untouched pixels, so nothing bleeds into the scenes on either side.

It is built for short timings. linearTiming({ durationInFrames: 12 }) is the recommended default; past roughly 20 frames the tearing stops reading as an artifact and starts reading as an effect.

import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { glitchCut } from "@/components/remocn/glitch-cut";
 
export const MyVideo = () => (
  <TransitionSeries>
    <TransitionSeries.Sequence durationInFrames={45}>
      <SceneA />
    </TransitionSeries.Sequence>
    <TransitionSeries.Transition
      timing={linearTiming({ durationInFrames: 12 })}
      presentation={glitchCut()}
    />
    <TransitionSeries.Sequence durationInFrames={45}>
      <SceneB />
    </TransitionSeries.Sequence>
  </TransitionSeries>
);

Installing it also installs lib/remocn/canvas-presentation.tsx, the shared scaffolding behind every canvas transition. It owns the support check, the WebGL2 scene shader that receives both sides of the cut as textures, and the routing that swaps in the CSS fallback where the browser has no html-in-canvas.

Determinism

Every pixel is a pure function of the transition's progress. The slice displacement, the block corruption and the per-slice handover all come from a hash seeded on the slice index and on a 22-step clock derived from progress — no wall-clock time, no state carried between frames. The same frame renders identically on every machine and in every chunk, so parallel and server-side renders come out seam-free.

Fallback

Where html-in-canvas is unavailable the presentation renders a CSS approximation instead: the frame is clipped into bands that shift horizontally on the same seeded clock, each band tinted alternately red and cyan to stand in for the channel split, and each band hands over to the incoming scene at its own staggered cut point. It reads as the same edit — a torn hard cut — without touching pixels. slices still drives how many bands there are; blockNoise has no effect there.

Props

glitchCut(props) accepts:

PropTypeDefaultDescription
intensity
number1Overall displacement. Scales how far slices and corrupted blocks slide sideways. 0 leaves the geometry intact and keeps only the channel split.
slices
number24How many horizontal slices the frame is cut into. Low counts read as chunky tape damage, high counts as fine scanline tearing.
rgbSplit
number1Chromatic separation of the red and blue channels at the peak of the glitch. 0 keeps the colour clean.
blockNoise
number0.6How much of the frame corrupts into displaced blocks that leak the other scene. 0 keeps the tearing purely horizontal.