Skip to content
Added 13 Sept 2026
Back to library
grid-distortion
Grid Distortion
Patterns & Textures

Grid Distortion

A ruled grid where the space bends toward the pointer, not the lines. Two cells away the grid is exactly where it was.

Patterns & Texturesgriddistortionlensinteractivepointershader
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/grid-distortion.tsx
'use client'

/**
 * Grid Distortion — a ruled grid that bends toward the pointer.
 *
 * The grid is not moved; the *space* is. Every pixel resolves its own
 * position, pulls it toward the pointer by a gaussian falloff, and then
 * asks which grid cell that pulled position lands in. Lines therefore bend
 * continuously rather than translating, and the deformation is local — two
 * cells away the grid is exactly where it was.
 *
 * Pointer-reactive, so it opts into tracking. On a touch screen there is no
 * pointer and the grid rests at centre with only its idle wave running,
 * which is a reasonable thing to look at rather than a broken one.
 *
 * Anti-aliasing is by hand. `fwidth` needs `OES_standard_derivatives` on
 * WebGL 1, and a design that silently fails to compile on a driver without
 * it is worse than one whose lines are a shade soft.
 */

import * as React from 'react'

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

export const GRID_DISTORTION_PROGRAM: GLProgram = {
  id: 'grid-distortion',
  kind: 'webgl',
  speed: 1,
  pointer: true,
  light: { c0: '#0f172a', c1: '#6366f1', c2: '#e11d48', bg: '#f8fafc' },
  dark: { c0: '#64748b', c1: '#818cf8', c2: '#fb7185', bg: '#050810' },
  fragment: `
vec4 render(vec2 uv) {
  vec2 p = aspect(uv);
  vec2 m = aspect(u_pointer);

  vec2 d = p - m;
  float pull = exp(-dot(d, d) * 3.4);

  // Toward the pointer, hardest at the centre of the falloff. The epsilon
  // keeps normalize() defined at exactly the pointer's position.
  vec2 q = p - normalize(d + vec2(1e-5)) * pull * 0.34;

  // A slow idle wave so the grid is alive before anyone touches it.
  q += 0.045 * vec2(sin(q.y * 2.0 + u_time * 0.4), cos(q.x * 2.0 - u_time * 0.3));

  float cells = 14.0;
  vec2 g = q * cells;
  vec2 f = abs(fract(g) - 0.5);
  float line = min(f.x, f.y);

  // Line width in cell units, widened by roughly one device pixel.
  float aa = 1.6 * cells / u_resolution.y;
  float grid = 1.0 - smoothstep(0.018, 0.018 + aa * 2.0, line);

  vec3 col = mix(u_bg, mix(u_c0, u_c1, pull), grid);
  col += u_c2 * pull * grid * 0.55;

  // The lens itself, faintly, so the distortion has a source.
  col += u_c1 * pull * 0.10;

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

export function GridDistortion({ className }: { className?: string }) {
  return <ShaderSurface program={GRID_DISTORTION_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 grid-distortion

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