Sustained Glitch
Hold a scene under scheduled bursts of broadcast corruption, resting clean in between
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-glitchUsage
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
| Prop | Type | Default | Description |
|---|---|---|---|
intensity | number | 1 | How 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 | number | 1 | How 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 | number | 24 | How many horizontal slices the frame tears into. Low counts read as chunky tape damage, high counts as fine scanline tearing. |
seed | number | 1 | Shifts 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.ReactNode | — | The 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.