Media Shader

v1.0.1
FreeShowcase & MediaWebGL

An ultra-premium WebGL-based media distortion component that creates beautiful interactive liquid chromatic ripples, wave physics, and refractions on images or videos on hover.

Media Shader

Installation

Step 1. Initialize ProjectOne-time setup

Run once in your project root to configure paths and dependencies:

npx vectorvesper init
Step 2. Add ComponentInstalls source & assets

Downloads and registers the component in your project:

npx vectorvesper add media-shader
Requires Tailwind CSS to be configured in your project.

Usage

Import

import { ShaderImage } from "@/components/vv/media-shader";

Usage

<ShaderImage />

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.

"use client";

import dynamic from "next/dynamic";

const ShaderImage = dynamic(
  () => import("@/components/vv/media-shader").then((m) => m.ShaderImage),
  { ssr: false }
);
This component respects prefers-reduced-motion and provides a graceful fallback.
Tailwind CSS must be configured in your project for styles to apply correctly.

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.

PropTypeDefaultDescription
presetPresetName | 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.
intensitynumber1.0 for all three presetsHow strong the distortion is. 0 leaves the media untouched.
noiseScalenumber1.0 liquid-chromatic · 3.2 curl-distortion · 3.0 focus-loupeScale of the pattern driving the distortion. Lower gives broad slow shapes; higher a finer grain.
idlenumber0.5 liquid-chromatic · 0.14 curl-distortion · 0.5 focus-loupeHow much the effect moves on its own with no pointer near it. 0 makes it react only to the cursor.
paramsreadonly 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.

PropTypeDefaultDescription
altstring""Alternative text. Set it unless the image is purely decorative.
imgPropsComponentPropsWithoutRef<"img">An escape hatch for attributes you would rather not spread onto the component itself.

Presets

PresetDescriptionKey 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.

PropTypeDefaultDescription
autoPlaybooleantrueStarts on mount.
loopbooleantrueRepeats when it ends.
mutedbooleantrueRequired for autoplay to be allowed at all.
playsInlinebooleantrueStops mobile browsers taking it fullscreen.
videoPropsComponentPropsWithoutRef<"video">An escape hatch for attributes you would rather not spread onto the component.

<setMediaShaderResolution />

hook

Sets 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

Swap an image tag
import { ShaderImage } from "@/components/vv/media-shader/MediaShader";

<ShaderImage src="/work/cover.webp" alt="Cover, morning light" className="w-full" />
A background video with a calmer effect
<ShaderVideo
  src="/reel.mp4"
  preset="curl-distortion"
  intensity={0.4}
  idle={0}
  className="absolute inset-0 h-full w-full object-cover"
/>
Lower the ceiling on a heavy page
import { setMediaShaderResolution } from "@/components/vv/media-shader/MediaShader";

setMediaShaderResolution(1);
The idle drift keeps running under reduced motion. Pass idle={0} when the preference is set — the distortion stays, but only responds to deliberate cursor movement.
Every shaded element on the page shares one drawing context and one frame budget, so several of them cost far less than several independent ones. Use setMediaShaderResolution to lower the ceiling globally rather than tuning each element.
Client components. They open a GPU drawing context on mount, which does not exist during a server render.

Source

/**
 * Media Shader — Vector Vesper
 * https://vectorvesper.dev/components
 *
 * Copyright (c) 2026 Vector Vesper
 * Released under the MIT License. This notice must be retained in copies and
 * substantial portions of the file. https://vectorvesper.dev/license
 */
export * from "./MediaShader";
View on GitHubReport an issue