Add cursor-following fluid WebGPU distortion over an existing image with the Shaders library's ImageTexture and CursorRipples components. Use when a hero, gallery, or media panel needs a water-ripple mouse effect; when replacing a drifting CSS spotlight or flashlight reveal; or when a prompt says to borrow only the shader interaction from a Shaders.com reference while preserving the current brand, image, copy, and layout.
78
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
shaders and import from the active framework subpath.Shader canvas.CursorRipples after ImageTexture so it post-processes that image.toneMapping="aces" on the root.When the user asks for only the shader effect, do not copy a reference's ribbon, blob, glow, typography, layout, copy, colors, or identity. Do not substitute the Shaders Water component: CursorRipples is the interactive image-displacement effect.
data-reveal-hover, mask-image: radial-gradient, duplicated images, requestAnimationFrame, and manual pointer listeners.shaders version and current framework API when the package may have changed.npm install shadersImport from shaders/react, shaders/vue, shaders/svelte, or shaders/solid. For Vite projects, never add shaders to optimizeDeps.exclude; its CommonJS dependencies need Vite pre-bundling. No optimizeDeps entry is normally required.
Before a commercial release, verify the current Shaders license terms.
Keep the component order and values below:
<Shader toneMapping="aces">
<ImageTexture url={imageUrl} objectFit="cover" />
<CursorRipples decay={7.3} radius={0.6} />
</Shader>Use this as the baseline before tuning intensity, chromaticSplit, or edges. Do not add SolidColor, Blob, Form3D, GaborNoise, Glow, or another generator unless the user explicitly requests the reference's generated artwork.
For React, copy and adapt assets/react/cursor-ripple-shader.tsx and assets/react/cursor-ripple-media.css.
Mount the shader only after the browser confirms WebGPU and motion is allowed:
const CursorRippleShader = lazy(() => import("./cursor-ripple-shader"));
const [shaderEnabled, setShaderEnabled] = useState(false);
const [shaderReady, setShaderReady] = useState(false);
useEffect(() => {
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
const sync = () => {
const enabled = "gpu" in navigator && !reduceMotion.matches;
setShaderEnabled(enabled);
if (!enabled) setShaderReady(false);
};
sync();
reduceMotion.addEventListener("change", sync);
return () => reduceMotion.removeEventListener("change", sync);
}, []);Keep the semantic image in the wrapper, then overlay the lazy shader:
<figure className="cursor-ripple-media">
<img
className="cursor-ripple-media__fallback"
src={imageUrl}
alt={meaningfulAlt}
width={width}
height={height}
/>
{shaderEnabled ? (
<Suspense fallback={null}>
<CursorRippleShader
imageUrl={imageUrl}
ready={shaderReady}
onReady={() => setShaderReady(true)}
/>
</Suspense>
) : null}
</figure>For Next.js, an ssr: false dynamic import is also valid. Do not hide the fallback until the shader calls onReady; a failed WebGPU initialization must leave the page legible and complete.
position: relative, a definite size, overflow: hidden, and isolation: isolate.0 and the canvas at z-index 1.position: absolute; inset: 0; width: 100%; height: 100%.pointer-events: none; Shaders listens globally and converts pointer coordinates using the canvas bounds.aria-hidden="true"; the fallback image owns the accessible name.ImageTexture. Use objectFit="cover" for full-bleed media.Delete the old implementation rather than leaving it hidden:
mask-image and custom reveal variables;requestAnimationFrame pointer easing;pointerenter, pointermove, pointerleave, and resize bookkeeping created only for the flashlight;Keeping both systems causes coordinate drift, extra GPU work, and confusing fallbacks.
onReady callback for a short opacity handoff.disableTelemetry when telemetry is not required.prefers-reduced-motion: reduce.Run the project's lint, production build, rendered tests, and git diff --check. Then verify in a real browser:
canvas[data-renderer="shaders"] exists.data-reveal-hover or radial-mask layer is absent.CursorRipples after ImageTexture; it requires a child/input surface.onReady.Report the affected media, Shaders package version, fallback behavior, reduced-motion behavior, build/test results, and live interaction verification. Distinguish a locally ready effect from a published deployment.
46abf78
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.