Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Free

Light Leak Shader for React

Film light leaks drawn on the GPU: red, orange and magenta blooms bleed in from the edges of a dark, grainy frame, burn to cream and fade. For React with Base UI, when a portfolio, a photographer's site or a film festival wants a warm, analog hero.

Framework

Light Leak Shader draws the light leaks at the end of a roll of film. Blooms of red and orange, and with a third source a soft magenta, bleed in from the left and top edges of a near-black frame, swell until their cores burn to cream, and fade again, never more than two at once. Each leak is a soft glow stretched along its edge, warped by slow noise and streaked like light slipping between the layers of the film; the exposures add up in linear light, run through a filmic tone curve, and get a faint halation rim and fine grain that re-rolls 24 times a second. A WGSL fragment shader on WebGPU draws every pixel each frame.

Use it behind a portfolio, a photographer's or filmmaker's site, a festival page or an album launch: anywhere a warm, analog mood fits better than clean gradients. For grain alone, in plain CSS, use the Grain background; for warm light falling through cloud, Cloud Rays Shader. It runs only while it's on screen, holds a still frame with reduced motion, and shows a CSS gradient of the same leaks in browsers without WebGPU.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Usage

import { LightLeakShader } from "@/components/ui/light-leak-shader";
<LightLeakShader className="flex min-h-[32rem] items-center justify-center">
  <h1 className="text-5xl font-semibold text-white">Ship faster</h1>
</LightLeakShader>

Light Leak Shader is a container: your content sits on top of the frame, in normal flow. It fills its own box, so give it a height or content. For a backdrop behind the whole page, render it empty with className="fixed inset-0 -z-10". The frame is near-black and the leaks stay near the left and top edges, so white text reads in both color schemes: in the middle of the frame it stays above 16:1 nineteen times out of twenty, and never drops below 4.2:1. Keep copy away from the left and top edges, where a leak burns to cream.

Demos

Cyanotype

A light backgroundColor switches the leaks from light to stain: instead of adding light, they multiply the frame by their color, like dye on paper. Here leakColor="#2fd3ff" stains the edges cyan and glowColor="#8a5cff" deepens to violet where the exposure is strongest, on cream. hotColor, the burnt-out cream, only shows on a dark frame.

Preview width

In a card

The leaks scale with the box's height, so they stay in proportion on a small surface. leaks={3} adds the third, magenta-leaning source on the lower left, so something is always swelling. The leaks take turns: never more than two show at once, and a new one starts at most every 2.3 seconds.

Preview width

Performance

About 0.3 ms of GPU time a frame at 1280×720 on an M2 Max laptop; slower GPUs take several times longer. The leaks are soft, so quality={0.5} hides well, but it makes the grain coarser. The animation stops when the shader scrolls out of view, and the browser pauses it in a hidden tab. Every shader on a page shares one GPU device and one frame loop, and vgpu (about 50 KB gzipped) loads only when the first one mounts.

Browser support

The shader needs WebGPU: Chrome and Edge, Safari 26 and later, and Firefox on Windows and macOS. Elsewhere, and until the first frame, the box shows a CSS gradient of the same leaks, which follows your color props, so the layout never jumps. data-state on the root reads unsupported there, if you want to style around it. See Shader Canvas.

Accessibility

The leaks are decoration: they're hidden from assistive tech and let every click through to your content. With reduced motion the shader draws one still frame, an orange leak along the top and a red one rising on the left, and stops. Nothing flashes: a leak swells over 1.3 to 1.8 seconds and fades over 2.7 to 3.6, and the grain moves each pixel's brightness by a few percent at most. White text in the middle of the frame stayed above 4.2:1 in every render we measured; near the left and top edges, a leak's core can burn to cream behind it.

FAQ

Can I change the colors?

Yes. leakColor is a leak's edge, glowColor its warm middle, hotColor the cream it burns to, and backgroundColor the frame. They take any CSS color, var(--primary) included, and follow a theme switch. On a light backgroundColor the leaks become stains in leakColor and glowColor, as in the cyanotype demo.

How do I get more or fewer leaks?

leaks sets how many sources take turns, from 1 to 3: one on the left, one on the top, and a third, magenta-leaning one lower on the left. With one, the frame rests dark between swells; with three, something is always glowing, but never more than two leaks show at once.

How do I make the leaks stronger or subtler?

intensity is the exposure: lower keeps the leaks dimmer and redder, close to the edges; 2 burns them to cream and lets them reach further into the frame. grain sets the film grain, from 0 (clean) to 1 (coarse).

How do I make it faster or slower?

speed scales time: at 1 each source swells every 7 seconds, at 0.5 every 14. paused holds the frame, and time draws one fixed moment, in seconds, which is handy for a poster.

Will it slow my page down?

It costs about 0.3 ms of GPU time a frame at 1280×720 on a laptop, and the CPU only sets a few numbers each frame. It stops when it's off screen. On a slow GPU or a large screen, quality={0.5} renders a quarter of the pixels; the leaks look the same and the grain gets a little coarser.

What happens in browsers without WebGPU?

The box keeps its CSS gradient: a red glow on the left edge and an orange one at the top over the frame color, in your color props, so the page still looks finished. Nothing errors: the root's data-state reads unsupported.

API Reference

LightLeakShader

data-slot="light-leak-shader"

PropType
backgroundColor?string

The frame behind the leaks. A light one turns the leaks into stains that tint it. Any CSS colour. @default "#0d0a09".

Default
"#0d0a09"
fallback?string

What shows until the first frame and in browsers without WebGPU: a CSS background value (gradients, sizes, a colour) that looks like the shader. It can use the colour props' --shader-<field> variables.

glowColor?string

The warm middle of a leak, between its edge and its burnt-out core. Any CSS colour. @default "#ff9a3c".

Default
"#ff9a3c"
grain?number

Film grain, from 0 (clean) to 1 (coarse). @default 0.35.

Default
0.35
hotColor?string

The hottest part of a leak, where it burns out. Any CSS colour. @default "#fff1d6".

Default
"#fff1d6"
intensity?number

Exposure, from 0 (no leaks) to 2 (burnt out): how far the leaks reach and how hot they get. @default 1.

Default
1
interactive?boolean

Whether u.pointer follows the pointer. @default false.

leakColor?string

The leak's edge colour, where it first bleeds into the frame. Any CSS colour. @default "#ff4d1a".

Default
"#ff4d1a"
leaks?number

How many leak sources take turns, from 1 to 3. Never more than two show at once. @default 2.

Default
2
paused?boolean

Holds the current frame. @default false.

quality?number

Render scale, from 0.25 to 1, of the screen's pixels (capped at 2×). @default 1.

speed?number

Animation speed: 1 is normal, 0 holds the frame. @default 1.

time?number

A fixed moment, in seconds: draws it and doesn't animate.

render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Forwarded to the part.

All 101 shaders