Background Cells
This effect fills the background with irregular, polygon-shaped tiles separated by thin borders — a bit like cracked glass or a mosaic pattern. The tiles gently drift and morph over time, react subtly to mouse movement with a soft parallax shift, and automatically respect the user’s prefers-reduced-motion setting by displaying a calm, static version instead.
Client Requests “Cleaner” and “More Premium”
Local Man Installs 47 Plugins, Calls It a Stack
Client Asks for “Just a Small Favor
Instructions
color1
Default: "#008C8C" (teal)
The color used to fill the cells. In the shader, this is the color that appears inside each cell shape (where the pixel is far from the cell edge). It accepts a hex string like "#FF5500" or a 3-digit shorthand like "#F50".
In your example, color1: "#1f1f1f" makes the cell interiors a dark gray.
color2
Default: "#F2E735" (yellow)
The color used for the borders/gaps between cells — the "grout" between the shapes. Wherever a pixel is close to a cell boundary (within the border thickness), this color shows through.
In your example, color2: "#171717" is slightly darker than color1, so the borders appear as subtle dark lines between cells — a very low-contrast, moody look.
size
Default: 1.5
Controls how large the individual cells appear on screen. Internally the shader computes scale = 14.0 / max(uSize, 0.05), so:
- Higher value → smaller scale factor → the pattern is divided into fewer, bigger cells
- Lower value → more cells packed on screen (smaller cells)
The value is clamped to a minimum of 0.05 to avoid division by zero.
In your example, size: 3.25 produces fairly large, chunky cells.
border
Default: 0.07
The thickness of the border between neighboring cells. The shader computes the distance between the nearest cell center (d1) and the second-nearest (d2) — the difference d2 - d1 is zero exactly on the border line between two cells. This option sets the threshold where the border color stops and the fill color begins.
- Higher value → thicker, more prominent borders
- Lower value → thin, hairline borders
It's clamped to a minimum of 0.005. Note it's not in pixels — it's relative to the cell distance field, so its visual thickness also depends on size.
roundness
Default: 0
How rounded the corners of the cells look, from 0 (sharp, angular Voronoi edges) to 1 (maximum roundness). Accepted range is clamped to 0–1.
It works by detecting "cornerness" (where three cells meet, using the third-nearest distance d3) and slightly shrinking the border there — the amount shrunk is roundness * 0.25 * cornerness. The result: corners of the polygonal cells get pulled in, making them look curved/organic.
In your example, roundness: 0.12 gives the cells a slightly softened, gentle corner treatment.
parallax
Default: 0.5
The strength of the mouse/touch parallax effect. When you move the pointer across the canvas, the entire cell pattern shifts slightly in the opposite direction, creating a sense of depth. Accepted range is clamped to 0–1.
0→ the pattern is completely static, ignoring the mouse1→ maximum shift (your example's setting)
The horizontal shift is mouse.x * parallax * 0.15 and the vertical shift is mouse.y * parallax * 0.075 — so vertical movement is deliberately half as strong as horizontal, a common parallax technique. The mouse position is also smoothed with easing (0.09 lerp per frame), so the motion feels floaty rather than jittery.
speed
Default: 1
The animation speed of the cells' organic movement. Each cell's center wobbles around over time (animated inside the shader with sin(iTime * 0.6 + ...)), making the whole pattern slowly morph and "breathe."
This value multiplies the elapsed time accumulated each frame (elapsed += dt * speed), so:
1→ normal speed0.5→ half speed, dreamier motion2→ twice as fast0→ effectively frozen in time (though note: passing0is falsy, so theo.speed || 0fallback keeps it at 0)
scale
Default: 1
A rendering resolution divisor for desktop/non-mobile devices. It's applied when computing the canvas backing buffer size: dpr = min(devicePixelRatio, 2) / scaleFactor.
1→ render at full device pixel ratio (sharp, crisp)2→ render at half resolution (the GPU fills fewer pixels, so it's faster, but the image is slightly softer and then stretched to fill the canvas)
This is a performance/quality tradeoff knob.
scaleMobile
Default: 3
The same idea as scale, but applied on mobile devices (detected via user-agent sniffing or a viewport narrower than 600px). Since mobile GPUs are weaker, this defaults to a more aggressive reduction: 3 means the canvas renders at roughly one-third the resolution.
It's purely a performance optimization — lower values = sharper visuals but heavier GPU load.
Bonus: What happens after the options are read
The returned object also gives you two methods worth knowing about:
setOptions(next)— merge new values at runtime (e.g.,cells.setOptions({ speed: 0.3 })), which immediately re-applies uniforms and re-renders.destroy()— stops the animation loop, removes all event listeners, and releases the WebGL context.
It also respects prefers-reduced-motion: if the user has reduced motion enabled in their OS settings, the animation renders a single static frame instead of looping, and mouse parallax snaps instantly rather than easing.