Liquid Ripple Image

£5

A liquid image hover effect: dragging the cursor over an image distorts the pixels like water, rippling from the pointer and settling back to a crisp, calm pool.

intermediate
3 more details
svg-filtershover-effectcursor-effects
Liquid Ripple Image - GSAP animation effect preview

This demo reads better at your own screen size than in the frame below:

About this effect

A liquid distortion effect that turns any image into water under the cursor. An inline SVG feTurbulence and feDisplacementMap warp the pixels, and GSAP feeds the filter from pointer velocity, so a slow drag ripples gently while a fast flick spikes a big watery splash.

Read the full effect overview

The disturbance eases back to a perfectly crisp rest with no snap, and a soft highlight tracks the pointer so the ripple reads localised over a global displacement. The filter is wired up only by JavaScript, so with no script or under reduced motion the image renders sharp and undistorted.

What's included

8 items
  • SVG feTurbulence + feDisplacementMap distortion driven entirely by GSAP, no canvas or WebGL
  • Displacement scale tracks cursor speed: slow drag ripples softly, fast flick spikes a splash
  • Turbulence baseFrequency shifts under speed so the ripple texture gets busier as you move faster
  • Pointer-tracking highlight and a subtle image swell layer over the same input for depth
  • Smooth power-style settle back to a crisp scale-zero rest, so calm and disturbed read as opposites
  • Pointer events cover mouse, pen and touch; a tap drops a one-shot splash that calms on its own
  • Filter is gated behind JavaScript, so no-JS and reduced-motion visitors get the sharp original image
  • Data-attribute config for splash strength, resting frequency, gain, an optional idle micro-ripple and timed idle drops so the surface reads without a pointer

Perfect for

5 use cases
  • Hero and portfolio images that reward the visitor for touching them
  • Product or artwork showcases where a tactile, watery hover adds a premium feel
  • Photography and agency sites wanting an award-style interaction on a single key image
  • Landing-page feature images that invite play with a clear "drag to ripple" prompt
  • Event or music pages where a liquid, reactive visual matches the mood

How it works

2 sections

An inline SVG filter pairs turbulence with a displacement map whose strength starts at zero, so the image is crisp until JavaScript runs. Pointer speed feeds a decaying energy value, and each frame that value is written onto the distortion without ever rebuilding the filter.

The energy decays smoothly toward zero so the water calms rather than snapping, a tap injects a one-shot impulse, and reduced motion skips the whole wiring so the un-filtered image is the fallback.

Difficulty Intermediate
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Paid effect

Purchase to unlock the code.

Buying Liquid Ripple Image opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£5 Standard license, unlimited projects

Browse free effects

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add before the closing </body> tag:

Code snippet omitted: it ships with the download.

3. Add the effect HTML anywhere in your <body>:

The wrapper needs data-liquid-ripple. Inside it, a .ripple-frame holds a .ripple-svg whose scene group carries the filter, plus the filter definition itself. The class names below are what the script queries, so keep them.

Code snippet omitted: it ships with the download.

That is all you need. An optional .ripple-splash-btn button anywhere on the page (the demo puts one in its toolbar) gives keyboard users a way to trigger the splash.

Using It With Your Own Design

What the script needs from your markup. A wrapper carrying data-liquid-ripple, and inside it the .ripple-frame, .ripple-svg, .ripple-scene (with data-filter-id naming the filter), .ripple-turb and .ripple-disp nodes exactly as in Quick Start. Those class names are the effect's API: the script writes scale and baseFrequency onto the two filter primitives every frame and attaches the filter to the scene group by id. .ripple-glow and .ripple-splash-btn are optional and are simply skipped when absent. Give every instance on the page its own filter id.

Rippling only part of the picture. The demo displaces the sea but not the beach. That is two copies of the same <image>: a sharp one drawn first (.ripple-base, outside the scene group, never filtered) and the filtered copy inside .ripple-scene with mask="url(#lr-water)". The mask is a white path covering the water, drawn in viewBox units and softened with a small feGaussianBlur so the boundary reads as wet sand rather than a cut. Trace the path for your own photograph (a few cubic segments along the edge is enough), or delete the base image and the mask attribute to ripple the whole frame. The script never touches either, so the mask is purely markup.

What is only the demo. The dark grey ground, the toolbar prompt, the hairline frame with its 8px radius, and the .ripple-overlay headline and caption sitting on the dark water are the demo's composition. Delete or restyle all of it; the ripple does not depend on any of it. The demo's assets/img/shore.webp is a placeholder photograph, replace it with yours.

CSS the effect relies on. .ripple-frame must be position: relative with overflow: hidden: the displacement pushes pixels past the image edge and the frame clips them, and the glow is positioned inside it. .ripple-svg needs transform-origin: 50% 50% because the script scales it up to 3% as the swell. The frame's touch-action: pan-y is what keeps a phone scrolling past the image (see Mobile below); do not set it to none unless the image is a self-contained element the page can scroll around. There is no pre-hide gate: the filter is attached by the script, so without JavaScript the image is simply sharp and no CSS restore is needed.

Using your own image

Two important details:

  1. Oversize the artwork. At full splash the displacement pushes the pixels outward; if the image only just fills the frame you will see gaps at the edges. Place it larger than the viewBox (the demo draws its image from x="-100" through width="1000" on an 800-wide box) so the edges always have material to ripple.
  2. Same-origin images only for <image href>. A cross-origin image inside an SVG filter can taint the canvas and silently drop the filter in some browsers. Host the image yourself (the demo ships a local assets/img/shore.webp) rather than hotlinking a remote URL.

Swap assets/img/shore.webp for your own photo, or replace the <image> with an SVG drawing (shapes, gradients). Either way, high-contrast horizontal edges (a horizon, wave foam) ripple most vividly.

Options

Set these as data attributes on the data-liquid-ripple wrapper.

Attribute Values Default Description
data-ripple-strength Any number (px) 62 Peak displacement a hard flick can reach. Above ~90 the edges tear; below ~30 the splash reads timid.
data-ripple-frequency 0.0040.02 0.009 Resting turbulence frequency. Higher makes finer, busier ripples.
data-ripple-gain Any number 0.85 How strongly cursor speed maps to displacement. Raise for a twitchier surface.
data-ripple-idle true, false false Adds a gentle idle micro-ripple. Off by default so rest reads crisp.
data-ripple-drop Seconds, 0 to disable 0 Drops a stone at a random point every N seconds while the pointer is away from the image, so the surface reads without interaction. Stops for good once the visitor disturbs the surface themselves. The demo uses 4.

Accessibility

This effect is built to degrade cleanly:

  • No JavaScript: the filter is never attached, so the image shows crisp and undistorted. The distortion is purely an enhancement.
  • Reduced motion: under prefers-reduced-motion: reduce the script skips all wiring; the image stays static and sharp and the glow is hidden.
  • Keyboard: the optional .ripple-splash-btn triggers the same splash a tap does, so the signature moment is reachable without a pointer. Give it a clear label.
  • Touch: pointer events cover touch, so a sideways finger drag ripples and a tap splashes. The frame keeps touch-action: pan-y, so a vertical swipe scrolls the page as normal.

Mobile / Touch

On coarse pointers a sideways drag produces the same ripple, and a tap drops a one-shot splash that settles on its own. The frame keeps touch-action: pan-y, so a vertical swipe over the image scrolls the page rather than rippling it: the image never traps a visitor who is trying to scroll past. If the image is a self-contained element with room to scroll around it and you want vertical drags to ripple too, set touch-action: none on .ripple-frame in your own CSS.

Dependencies

Required:

  • GSAP 3.12+ (core only, no plugins)

Optional:

  • None

Credits

Demo photograph (aerial surf on a dark shore) from Pexels, used under the Pexels License and baked to a local assets/img/shore.webp (no hotlinking). Replace it with your own image before shipping.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

Everything in the catalogue, plus everything we release next.