Skip to content
Added 13 Sept 2026
Back to library
ripple-grid
Ripple Grid
Patterns & Textures

Ripple Grid

A lattice of dots swelling with two waves — a slow one from the centre and a tight ring that arrives with the pointer. Each dot samples at its own cell, so it stays a grid.

Patterns & Texturesgridrippledotswaveinteractiveshader
lines of CSS
24lines of CSS
dependencies
0dependencies
browser, for years
Everybrowser, for years
no motion at all
Noneno motion at all
Backdrop
Fragment shader, 3 files, no dependencies. The first one is this design. The other two are the runtime, shared by every shader effect in the catalog — copy them once and the next design you add is a single file. The HTML and CSS on this page are the preview surface and the gradient shown if WebGL is unavailable; they are not a substitute for these.
components/ripple-grid.tsx
'use client'

/**
 * Ripple Grid — a lattice of dots that swells with concentric waves.
 *
 * The grid stays put and the *dots* change size, which is the opposite
 * choice from `grid-distortion` and reads completely differently: a
 * distorted grid looks like a lens, a pulsing one looks like a surface
 * responding. Two waves drive it — a slow one from the centre that never
 * stops, and a tighter ring around the pointer that arrives when it does.
 *
 * Each dot samples the wave at its own cell centre rather than per pixel,
 * so a dot is one size all over instead of being a smooth blob — that is
 * what keeps it reading as a grid of elements and not as a gradient with
 * holes in it.
 */

import * as React from 'react'

import { ShaderSurface } from './shader-surface'
import type { GLProgram } from './shader-runtime'

export const RIPPLE_GRID_PROGRAM: GLProgram = {
  id: 'ripple-grid',
  kind: 'webgl',
  speed: 1,
  pointer: true,
  light: { c0: '#2563eb', c1: '#7c3aed', c2: '#06b6d4', bg: '#f1f5f9' },
  dark: { c0: '#3b82f6', c1: '#a855f7', c2: '#22d3ee', bg: '#04070f' },
  fragment: `
vec4 render(vec2 uv) {
  vec2 p = aspect(uv);
  vec2 m = aspect(u_pointer);

  float cells = 22.0;
  vec2 g = p * cells;
  vec2 id = floor(g);
  vec2 c = fract(g) - 0.5;

  // The cell's centre, back in aspect space. Sampling the waves here and
  // not at the pixel is what makes each dot a single uniform size.
  vec2 centre = (id + 0.5) / cells;

  float r1 = length(centre);
  float r2 = length(centre - m);

  float wave = 0.5 + 0.5 * sin(r1 * 7.0 - u_time * 1.5);
  float ring = exp(-r2 * r2 * 4.5) * (0.5 + 0.5 * sin(r2 * 15.0 - u_time * 3.0));

  float amp = clamp(wave * 0.55 + ring * 0.85, 0.0, 1.0);

  float d = length(c);
  float radius = 0.10 + 0.34 * amp;
  float dotMask = smoothstep(radius, radius - 0.06, d);

  vec3 col = mix(u_bg, mix(u_c0, u_c1, amp), dotMask);
  col += u_c2 * dotMask * ring * 0.75;

  return vec4(col, 1.0);
}
`,
}

export function RippleGrid({ className }: { className?: string }) {
  return <ShaderSurface program={RIPPLE_GRID_PROGRAM} className={className} />
}

What's included

  • The markup and the stylesheet, in whichever framework you pick
  • No runtime dependencies

Works with

  • React
  • Vue
  • Svelte
  • Tailwind
  • styled-components
  • Plain CSS

npx hoverlab add ripple-grid

Or over MCP, from your editor's agent — no account needed.

License

Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.

Was this useful?

Motion safety

Not profiled. Canvas / WebGL effect: its deliverable is a shader, and the motion profile only reads CSS.

34 more effects in Patterns & Textures

All of them free to read, copy and install — no account, no locked tiles, no watermarked preview. The whole catalog is open, and so are the API and the CLI.

Browse Patterns & Textures

Shipping one commercially

Copying the code is free. Putting it in client work or a paid product is what Pro is for — the licence, not the access.

  • A commercial licence for everything in the catalog
  • Unlimited bundle exports, in Vue, Svelte and Tailwind
  • One payment — no subscription, nothing to renew
Pro — $79 once