Media Shader
v1.0.1An ultra-premium WebGL-based media distortion component that creates beautiful interactive liquid chromatic ripples, wave physics, and refractions on images or videos on hover.
Installation
Run once in your project root to configure paths and dependencies:
npx vectorvesper initDownloads and registers the component in your project:
npx vectorvesper add media-shaderUsage
Import
Usage
WebGL — render it on the client only
On Next.js, load it with ssr: false from a Client Component, as below. Vite apps need nothing extra.
prefers-reduced-motion and provides a graceful fallback.Props
Drop-in replacements for an image or a video tag that put a distortion between the media and the screen. They accept everything the native element does, so swapping one in is a one-word change.
Shared Props
Common to all exported components in this suite.
| Prop | Type | Default | Description |
|---|---|---|---|
preset | PresetName | MediaShaderPreset | "liquid-chromatic" | Which distortion runs. Pass a name, or your own preset object. An unknown name logs a warning and falls back to liquid-chromatic rather than failing. |
intensity | number | 1.0 for all three presets | How strong the distortion is. 0 leaves the media untouched. |
noiseScale | number | 1.0 liquid-chromatic · 3.2 curl-distortion · 3.0 focus-loupe | Scale of the pattern driving the distortion. Lower gives broad slow shapes; higher a finer grain. |
idle | number | 0.5 liquid-chromatic · 0.14 curl-distortion · 0.5 focus-loupe | How much the effect moves on its own with no pointer near it. 0 makes it react only to the cursor. |
params | readonly number[] | — | Extra values handed to the preset's shader. The bundled presets ignore them; they exist for a custom preset. |
<ShaderImage />
Stands in for an img tag. Every img attribute passes through — src, alt, loading, sizes, srcSet.
| Prop | Type | Default | Description |
|---|---|---|---|
alt | string | "" | Alternative text. Set it unless the image is purely decorative. |
imgProps | ComponentPropsWithoutRef<"img"> | — | An escape hatch for attributes you would rather not spread onto the component itself. |
Presets
| Preset | Description | Key Overrides |
|---|---|---|
"liquid-chromatic" | Colour separates and drifts like oil on water. The default. | intensity=1.0noiseScale=1.0idle=0.5 |
"curl-distortion" | Tight swirling curls. The busiest of the three. | intensity=1.0noiseScale=3.2idle=0.14 |
"focus-loupe" | A lens that sharpens and magnifies under the cursor. | intensity=1.0noiseScale=3.0idle=0.5 |
<ShaderVideo />
Stands in for a video tag. Defaults to autoplaying, looping, muted and inline, which is what a background video almost always wants.
| Prop | Type | Default | Description |
|---|---|---|---|
autoPlay | boolean | true | Starts on mount. |
loop | boolean | true | Repeats when it ends. |
muted | boolean | true | Required for autoplay to be allowed at all. |
playsInline | boolean | true | Stops mobile browsers taking it fullscreen. |
videoProps | ComponentPropsWithoutRef<"video"> | — | An escape hatch for attributes you would rather not spread onto the component. |
<setMediaShaderResolution />
hookSets the pixel-ratio ceiling for every shaded element on the page at once. Call it before mounting if you want to trade sharpness for headroom.
Examples
import { ShaderImage } from "@/components/vv/media-shader/MediaShader";
<ShaderImage src="/work/cover.webp" alt="Cover, morning light" className="w-full" /><ShaderVideo
src="/reel.mp4"
preset="curl-distortion"
intensity={0.4}
idle={0}
className="absolute inset-0 h-full w-full object-cover"
/>import { setMediaShaderResolution } from "@/components/vv/media-shader/MediaShader";
setMediaShaderResolution(1);