Skip to main content
assetsforwordpress

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”

This has been translated internally as “make the logo bigger but also smaller.

Local Man Installs 47 Plugins, Calls It a Stack

The site now loads in the time it takes to question all your life choices.

Client Asks for “Just a Small Favor

The favor is a complete e-commerce build, due Friday, for exposure.

Instructions

Open the JS script in the Supa Code Block element and edit the options at the end of the script. Each option is explained below:

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 mouse
  • 1 → 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 speed
  • 0.5 → half speed, dreamier motion
  • 2 → twice as fast
  • 0 → effectively frozen in time (though note: passing 0 is falsy, so the o.speed || 0 fallback 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.