Liquid✨ Premium

Mesh Distortion AI

A lattice of 13 × 13 points linked by thin lines, drawn on a canvas, that ripples gently on its own; within 100 px of the mouse, points are pulled toward it by up to 20 px and the net pinches under the cursor, lines included. A hue that slides from blue to violet, a canvas sharp on Retina, a loop that stops off screen, a still net that lights up under the mouse when the user reduces motion. A net of points, not a mesh gradient; no artificial intelligence.

CanvasMeshInteractif

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Liquid has 31 effects, including 6 free. Explore the category →

3 usage examples

A tensioned shade sail — a terrace sail maker's configurator — Mesh Distortion AI example 1

① A tensioned shade sail — a terrace sail maker's configurator

WhenTop view of the charcoal sail stretched between four rings above a wooden deck; the mouse travels over it and pushes it like the wind.
WhyPoints that gather around the cursor then return show a fabric that is supple yet taut; the ±3 px ripple is enough to make it breathe.
SettingsRoot cut into a quadrilateral (clip-path: polygon(…)) on a charcoal #3a3f47 background; sand-colored points (hue 34 instead of 240, saturation 55%, lightness 78%); lines at 40% opacity; a blurred drop shadow and four CSS rings.
A goal net — the "Results" page of an amateur soccer club — Mesh Distortion AI example 2

② A goal net — the "Results" page of an amateur soccer club

WhenScore "3 – 1" in a banner; the net fills the goal, and the ball, which follows the mouse, distorts it where it goes in.
WhyThe regular lattice looks like netting; the local pinch around the cursor mimics the ball pushing into the net.
SettingsTransparent root over CSS turf, framed by 7 px white posts; white net (saturation 0, lightness 94%), lines at 50% opacity; a CSS ball placed at the mouse position by the scene's wiring.
A balcony cat net — a protective netting maker's product page — Mesh Distortion AI example 3

③ A balcony cat net — a protective netting maker's product page

WhenBalcony at dusk, buildings and railing in the background; on top, the net that the cat's paw (the cursor) pushes without getting through.
WhyThe net dips under pressure then springs back: the proof of strength the customer expects, with no video.
SettingsTransparent root over the whole frame, above a backdrop made of CSS gradients; light gray net (saturation 0, lightness 88%), lines at 42%; a CSS paw that follows the mouse.

How it works

The .fx-0420-root frame is 300 px tall and full width, and the canvas fits it (display: block, 100% × 100%); internal size = displayed size × devicePixelRatio, drawing in CSS pixels through setTransform (measured: 1280 × 600 for 640 × 300 at density 2). The lattice covers the whole canvas: 12 intervals each way, about 53 × 25 px cells on 640 × 300, with edge points sitting on the edge. A ResizeObserver follows the frame, and so does the lattice.

Each frame is drawn in two passes. First, each point's position: its place in the grid, plus a ±3 px ripple (3 sin(a + 0.5 × column) horizontally, 3 cos(a + 0.5 × row) vertically, a moving by 0.015 per frame, a cycle of about 7 s at 60 frames per second), plus, if the mouse is within 100 px, a shift toward it of 20 × (1 − distance / 100) px. Then the 1 px lines linking each distorted point to its right and lower neighbors, distorted as well, and finally the 2 px radius points on top. Points close to the cursor gather on it: seen from above, the net dips as if pressed. The hue follows 240 + 30 sin(a + 0.3 × (column + row)): a violet band crosses the blue diagonally.

The mouse is read through mousemove on the container. Until it has entered, and as soon as it leaves (mouseleave), its position is stored far away from the frame: no point is pulled and the net returns to its ripple alone. The canvas is cleared on every frame, so its background is the root's, or your page's if the root is transparent. The requestAnimationFrame loop stops when an IntersectionObserver sees the canvas leave the screen (measured: 0 frames per second off screen against 120 on screen) and resumes when it comes back.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, the net no longer ripples or gets pulled; it is redrawn only when the mouse moves, and points within 100 px lighten and turn pink, their lines more opaque, without moving. Measured in Chromium 151, Firefox 153 and WebKit 26: 0% of pixels changed at rest, the area under the mouse lights up as it passes.
  • Keyboard and screen readers: the net only reacts to the mouse and carries no information. If the distortion illustrates a point (a net's strength, a fabric's give), say it in text next to it, as in the examples.
  • Touch: with no hover, a tap sends a single mousemove: the net pinches at the touched point and stays there until the next tap, while the ripple goes on. To follow a finger, see the FAQ.
  • Contrast: lines are at 15% opacity, mere mood strokes on a dark background; the points carry the shape. On a light or busy backdrop, raise the line opacity (see Customize).

Browser compatibility

2D canvas + 2.9 KB of JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26: same ripple, same pinch under the mouse, return when it leaves, internal size doubled at density 2, stable height after 20 resizes, loop stopped off screen, still net under reduced motion. Measured cost: 0.2 ms of JavaScript per frame in Chromium, about 1 ms in Firefox. ResizeObserver exists since Chrome 64, Firefox 69 and Safari 13.1.

Chrome 74+✓ Full
Firefox 69+✓ Full
Safari 13.1+✓ Full
Edge 79+✓ Full
Mobile iOS 13.4+✓ Ripple; by finger, pinch on tap only
Android Chrome 74+✓ Ripple; by finger, pinch on tap only

Without JavaScript, the frame stays bluish black, with no net. Without IntersectionObserver, the loop also runs off screen; without ResizeObserver, the lattice follows window resizing only.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0420-root">
  <div class="ai-mesh-container">
    <canvas class="ai-mesh-canvas" width="393" height="280"></canvas>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Density (JS — const h = 12) 12 intervals: 13 × 13 = 169 points 20 gives 441 points and a fine net; 8, 81 points and a coarse mesh. Cost follows the number of points.
Radius and strength (JS — m = 100 and 20 * (1 - g / m)) 100 px, up to 20 px of pull 160 px and 30 px for a wide, deep dip; 60 px and 10 px for a fingertip press.
Direction (JS — u += r / g * t, M += d / g * t) pull toward the mouse Replace both += with -=: points flee the cursor and the net bulges around it.
Ripple (JS — 3 * Math.sin(a + .5 * n), 3 * Math.cos(a + .5 * t) and a += .015) ±3 px, a cycle of about 7 s Put 0 instead of the two 3s for a perfectly still net at rest; 0.03 for a doubles the speed.
Colors (JS — 240 + 30 * Math.sin(…), hsla(${k}, 70%, ${L}%, 0.8)) hue 210 to 270, saturation 70%, lightness 60% Hue 34 and lightness 78% give sand; saturation 0 and lightness 94% a white net. Lines have their own hsla(${k}, 60%, 50%, ${A}).
Line opacity (JS — A = .15) 15% 0.4 to 0.5 makes the net readable on a light or busy backdrop, as in all three examples.
Height and background (CSS — .fx-0420-root { height: 300px; background: #0a0a0f }) 300 px, bluish black The canvas is cleared on every frame: background: transparent lays the net over your backdrop.

FAQ

No, despite the catalog card: no gradient fills the cells. It is a net of 169 points and thin lines that distorts. For a mesh gradient, each cell would have to be filled with a color interpolated between its four corners (or move to WebGL); the point grid and its distortion would stay the same.
Because the distortion reads as pressure seen from above: cells tighten where you press, like a net or fabric being pushed in. Points right next to the cursor gather on it. For the opposite effect (a dome that pushes away), flip the sign of the shift: see Customize, "Direction" row.
Listen to pointermove and pointerleave instead of mousemove and mouseleave (same clientX / clientY coordinates), and add touch-action: none on the container: the finger pinches the net as it slides, but the page no longer scrolls while the finger is on it. So keep a frame that does not cover the whole screen.