Sustained Glitch

Hold a scene under scheduled bursts of broadcast corruption, resting clean in between

Experimental — html-in-canvas

This filter re-reads the real pixels of the scene it wraps 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, 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 fallback is what keeps your composition renderable if that happens.

Installation

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

Usage

SustainedGlitch is a wrapper. Put a scene inside it and the frame stays clean until a burst fires: slices tear sideways on their own beats, the colour channels pull apart, and blocks fill with picture dragged in from somewhere else in the frame. The burst decays over a few frames and the scene goes back to untouched until the next one.

This is the one filter in the pack that is not a steady texture. That is the point — a constant glitch stops reading as a fault and starts reading as a material.

import { SustainedGlitch } from "@/components/remocn/sustained-glitch";
 
export const MyScene = () => (
  <SustainedGlitch>
    <Scene />
  </SustainedGlitch>
);

How the bursts are scheduled

Time is cut into slots — one per second at frequency={1}, shorter as you raise it. Most slots fire a burst, at a position and length seeded inside the slot, and the whole schedule is a function of the frame number, so it is identical on every render.

Two knobs shape it. frequency sets how often the frame can break; intensity scales how hard it breaks when it does, and at 0 nothing fires at all.

Landing a hit on a beat

intensity is a plain number read every frame, so interpolate it. To put damage on a specific beat, raise frequency until bursts are effectively continuous and let the envelope decide when they are visible.

import { interpolate, useCurrentFrame } from "remotion";
import { SustainedGlitch } from "@/components/remocn/sustained-glitch";
 
export const MyScene = () => {
  const frame = useCurrentFrame();
  const intensity = interpolate(
    frame,
    [40, 44, 52],
    [0, 1.6, 0],
    { extrapolateLeft: "clamp", extrapolateRight: "clamp" },
  );
 
  return (
    <SustainedGlitch intensity={intensity} frequency={6}>
      <Scene />
    </SustainedGlitch>
  );
};

Props

PropTypeDefaultDescription
intensity
number1How hard a burst hits. Read every frame, so it can be interpolated to script the damage. At 0 no burst is visible and the scene passes through untouched.
frequency
number1How often the frame can break, in bursts per second at 30fps. Around 6 the slots are short enough that the tearing is effectively continuous, which is what you want when intensity is driving the timing instead.
slices
number24How many horizontal slices the frame tears into. Low counts read as chunky tape damage, high counts as fine scanline tearing.
seed
number1Shifts the whole schedule and the per-slice pattern. Change it when two scenes in one video should not break up in the same rhythm.
children
React.ReactNodeThe scene the filter wraps and re-reads every frame.

Notes

Between bursts the filter writes the scene through unchanged, so a scene wrapped at intensity={0} costs a texture read and nothing else visually.

The CSS fallback keeps the same shape — clean, then a burst of shifted bands, then clean — but its schedule is its own. If you have timed something against a specific burst in Chrome, check it once on the fallback path before shipping.

The corrupted blocks pull picture from a displaced coordinate, which is why this needs the canvas: the block does not tint or mask what was already under it, it shows a piece of somewhere else in the frame.