Iridescence
An iridescent shifting-wave shader background — a canvas 2D approximation of an iridescence shader, with optional mouse response.
A canvas 2D approximation of the classic iridescence shader's shifting waves, computed cell by cell and stretched with interpolation — no WebGL required.
npx shadcn@latest add https://webberui.com/r/iridescence.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<Iridescence />
Installation
npx shadcn@latest add https://webberui.com/r/iridescence.jsonOr, once registries are configured in components.json, install it as @webberui/iridescence.
Usage
Put your foreground content in children, and write the layout (centering, height) on className:
import { Iridescence } from "@/components/ui/iridescence";
<Iridescence className="flex h-[360px] items-center justify-center rounded-2xl">
<h1 className="text-4xl font-bold text-white">Hero heading</h1>
</Iridescence>Custom tint, slower speed, and mouse response enabled:
<Iridescence
color={[1, 1, 1]}
speed={0.6}
mouseReact
amplitude={0.2}
className="flex h-[400px] items-center justify-center rounded-2xl"
>
<p className="text-lg text-white">The full iridescent spectrum</p>
</Iridescence>color is an RGB tint with each channel in 0~1, and the whole image is multiplied by it: [1, 1, 1] keeps the full iridescence, and the darker it gets the more it converges on a single hue.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
color | [number, number, number] | A cool cyan-violet | Overall tint, each channel 0~1; [1, 1, 1] is full iridescence |
speed | number | 1 | Wave speed multiplier |
amplitude | number | 0.1 | How strongly the mouse offset affects the pattern (applies when mouseReact is on) |
mouseReact | boolean | false | Whether the pattern shifts with the mouse (mouse pointers only; touch has no effect) |
detail | number | 96 | Internal sampling edge length (px) — higher is finer but more expensive |
children | React.ReactNode | — | Foreground content, layered above the iridescence |
className | string | — | Appended to the outermost container's className |
How it works
- No WebGL. The shader output is computed cell by cell over a downsampled grid, written into
ImageData, and then stretched by the canvas withh-full w-full, relying on the browser's built-in bilinear interpolation to fill it in as smooth iridescence — avoiding heavy dependencies like three.js - The longer edge is fixed at
detailand the shorter edge scales by the container's aspect ratio, which means the sampling count adapts to the aspect ratio and the pattern never distorts - The drawing loop is created once on mount, and props like
color/speedare read live through refs, so changing a parameter never rebuilds the loop or causes a flicker - A
ResizeObserverwatches for container size changes and reallocates the grid; on unmount it callscancelAnimationFrameand stops observing - With
mouseReacton, the cursor position is normalized to0~1and, scaled byamplitude, translates the sampling coordinates so the pattern follows along; it only applies whenpointerType === "mouse" - The whole background canvas is
pointer-events-noneand sits at-z-10beneath the content (the container hasisolateto create its own stacking context), so foreground interaction is completely unaffected - When the user has "reduce motion" enabled at the system level, only a single static frame of iridescence is drawn, the animation loop never starts, and the DOM structure is unchanged