119

Dither Background

£10 or the whole Vault, £99

A GSAP and WebGL dither background: a lit sphere drawn in 1-bit Bayer dots, the pointer is the light, and a click inverts paper and ink through a stepped dissolve that doubles as a light/dark theme switch.

advanced
4 more details
webgl-shadertheme-switchpointer-trackingambient

This demo reads better at your own screen size than in the frame below:

Released

About this effect

A background in the 1-bit print look: a large sphere rising behind your content, drawn entirely in square dots from an 8x8 Bayer matrix. There are only three colours on screen, paper, ink and a vermilion accent that appears only in the brightest band, and nothing is ever blurred or faded between them. Bands of ridges run round the sphere like dunes and turn slowly.

Read the full effect overview

The pointer is the light. Move it and the shadow line swings across the sphere, the ridges catch it, and the dot pattern visibly reorganises over the form as one tone gives way to the next. The light follows with a little weight; the pattern it drives always stays crisp.

Press the toggle, or click anywhere on the stage, and paper and ink trade places: the day-printed sphere becomes a moon on a night sky. The change spreads outward from the click point as a dissolve that flips the dots one by one in Bayer order, a stepped front rather than a fade. Your page's own theme switches at the moment the front passes behind your content, so the effect doubles as a light/dark theme switch you can hook to your own toggle. Behind any text you mark, the pattern drops to the plain ground and its sparsest dots, so type sits on a quiet field.

Visitors without WebGL or JavaScript, and anyone who prefers reduced motion, see a baked dither frame in the current theme, and the toggle still switches it, instantly.

Dither Background - GSAP animation effect preview

What's included

14 items
  • A generative form, no photograph: a lit sphere in 1-bit Bayer dots, crisp at any screen density
  • Three levels only: paper, ink, and the accent in the brightest band
  • The pointer is the light: the shadow line swings across the sphere and the dot pattern reorganises as it moves
  • Click anywhere, or press the toggle, to invert paper and ink through a stepped dissolve that spreads from the click point
  • Doubles as a theme switch: the layer reports the current theme and announces each change as the dissolve passes behind your content
  • Calm zones: behind marked content only the ground and its sparsest dots remain
  • Paper, ink and accent set from three CSS custom properties
  • Cell size, light drift, the sphere's slow turn and overall intensity each set from one data attribute
  • Touch: drag to move the light, tap to invert from the tap point, and the light drifts between touches
  • Keyboard: the toggle is a native button and switches at once
  • Works in any positioned container, from a full-screen hero to a single card
  • Pauses off screen and in hidden tabs, and stops drawing entirely when drift and turn are switched off
  • Baked dither frames in both themes for reduced motion, no WebGL and no JavaScript
  • Small API: invert, set the theme, set the light, pause and play

Perfect for

5 use cases
  • Developer tools, studios and indie products that want the current 1-bit dither look as their hero
  • Sites that want their light/dark switch to be a moment rather than a flicker
  • Launch pages and waitlists with one line of type over a striking ground
  • Portfolio and editorial sites with a print or zine register
  • Event, music and release pages that want a graphic, poster-like background

How it works

3 sections

A three.js WebGL shader draws the scene each frame. The sphere is shaded as a lit surface, with ridged bands and a tight highlight, and the result is never shown directly: the layer is divided into square cells, each takes one tone from the cell's centre, and an 8x8 Bayer threshold decides whether the cell becomes ink, paper or accent. Cells are sized in whole screen pixels, so the dots stay sharp on any display.

GSAP plays the timed values: the load reveal, as the form emerges from the empty ground in steps, and the dissolve's progress. The shader turns that progress into a front that flips cells in Bayer order, and the page's theme switches when the front reaches your content. GSAP's media queries switch between pointer, touch and reduced-motion behaviour, and its context tears everything down. A frame loop on GSAP's ticker owns the continuous motion: the light's eased chase of the pointer, its drift between touches, and the sphere's slow turn.

The background stops drawing when it leaves the screen or the tab is hidden, and goes fully idle once settled when drift and turn are switched off.

Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Dither Background opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£10 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £99, one payment.

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

The inline script is not optional. The layer carries a baked dither frame for visitors without JavaScript or WebGL and for anyone who prefers reduced motion. Without this probe every visitor would download that frame and see it for the moment three.js takes to arrive. The probe runs before first paint and stamps html.gl when a canvas is coming; the stylesheet drops the frame under that class. has-js is only used by the demo's toggle button, which needs the script; drop it if you hide your toggle another way.

2. Put the layer inside any positioned container, before your content:

Code snippet omitted: it ships with the download.

data-dither-calm marks content that should sit on a quiet field (up to four elements per layer). data-dither-toggle on any button in the same container makes it invert the theme from the button.

3. Add before the closing </body> tag:

Code snippet omitted: it ships with the download.

Why the import map: three ships as ES modules only, and its old UMD build logs a deprecation warning on every page load. The shim imports it as a module, puts it on window, and then loads script.js as an ordinary script. If three cannot load, the script still runs on the baked frame, so the theme toggle keeps working.

Already using three.js as a module? Skip the shim and set window.THREE before script.js runs:

Code snippet omitted: it ships with the download.

Using It With Your Own Design

What the effect needs from your markup: a positioned container (relative, absolute or fixed) holding the [data-dither] layer and your content. The layer fills the container with position: absolute; inset: 0, at any size. Your content needs position: relative (or any z-index context) to sit above it. The layer takes no pointer events; the container listens instead, so the light follows the cursor over your text too. Clicks on links, buttons and form fields never invert.

What is only the demo's styling: everything under DEMO in style.css: the .stage box, the cue and the toggle's look. Replace it wholesale.

CSS the effect depends on:

  • The container must have a real size. The canvas is sized from the layer.
  • The .dither block (position: absolute; inset: 0, overflow: hidden, pointer-events: none, image-rendering: pixelated), its [data-dither-theme="dark"] rule, .dither__canvas, and the .gl .dither rule that drops the baked frame.
  • The four frames in img/ (light and dark, landscape and portrait).
  • On touch screens the container needs touch-action: pan-y so vertical swipes still scroll. The script sets it inline while it runs if you have not.

Colours are custom properties on .dither, read once at start:

Code snippet omitted: it ships with the download.

Any CSS colour works. Keep paper and ink far apart in brightness: the form is drawn in two of them, and the dots read as tone only when they contrast.

If you change the colours, re-bake the frames. The images in img/ are stored one pixel per cell (3px cells landscape, 4px portrait) and scaled up crisp. Recolour them in any image editor that can replace exact colours (each holds only the three demo colours), or screenshot your restyled layer and scale it down by the cell size with nearest-neighbour sampling.

Following the theme on your page. The layer always reflects the current theme in data-dither-theme (light or dark), switched in the frame the dissolve passes behind your content (your data-dither-calm boxes, or otherwise the container's other children). Follow it from CSS:

Code snippet omitted: it ships with the download.

or from script, with the dither:theme event, which bubbles:

Code snippet omitted: it ships with the download.

To drive the effect from a toggle you already have, call Dither.setTheme('dark') from it (or Dither.invert()), and let the event update the rest of the page.

Contrast. Behind data-dither-calm content the pattern drops to the plain ground and its sparsest dots (at most about one cell in eight in the other colour), and the accent never appears there, in either theme. Text in ink on paper, or paper on ink, keeps its contrast at the brightest moment. Text that sits directly on the dither without calm should carry its own solid backing, as the demo's cue and toggle do: then contrast never depends on which side of the dissolve is behind it.

Options

Attribute Values Default Description
data-dither (present) Marks the layer
data-dither-theme light, dark light Starting theme. The script keeps it current afterwards
data-dither-cell CSS px, 1 and up 3 Cell size. Touch screens use a third more (4px by default)
data-dither-drift 0 on 0 stops the light moving on its own. With no pointer it rests high on the left
data-dither-turn 0 and up 1 Speed of the sphere's slow turn. 0 holds it
data-dither-intensity 0 to 1 1 Scales the whole response: how far the light follows the pointer, the highlight and the glow. At 0 switches are instant
data-dither-calm (present) On content in the same container: quiets the pattern behind it (up to four elements)
data-dither-toggle (present) On a button in the same container: inverts the theme from the button

With both data-dither-drift="0" and data-dither-turn="0" the canvas stops drawing as soon as the light settles, and only wakes for input or a dissolve.

Behaviour

  • Load: the form emerges from the empty ground in Bayer steps over about 1.4 seconds.
  • Pointer: the light follows the cursor with weight. Its direction is the pointer's offset from the sphere, so the shadow line swings as you cross it, and a soft glow clears the ground around it. When the pointer leaves the container the light drifts on its own (or returns to rest, with drift off).
  • Click or toggle: paper and ink trade places through a dissolve that spreads from the click (or from the toggle) over 1.25 seconds. Inside the front, cells flip in Bayer order, so its edge is a stepped pattern. A second switch during a dissolve completes the first at once and starts from there.
  • Touch: the light drifts; drag to move it, and it returns to its drift 2.5 seconds after your finger lifts. A tap inverts from the tap point.

Accessibility

  • Reduced motion: no canvas. The layer shows the baked dither frame for the current theme, and switching the theme swaps the frame instantly. No autonomous motion of any kind.
  • Without WebGL: the same frames, and the toggle still works. Without JavaScript: the frame for the starting theme; the demo hides its toggle and cue.
  • Keyboard: toggles are native buttons. A keyboard press switches the theme at once rather than playing the dissolve.
  • Screen readers: the layer and its canvas are aria-hidden. If your toggle's label names the action (Dark, Light), update it from the dither:theme event or with CSS as the demo does.

Dependencies

Dependency Version Required
three.js 0.180.0 For the live canvas, as an ES module (see Quick Start)
GSAP core 3.12+ (the demo is built against 3.15.0) Yes: the load reveal and the dissolve's progress, the ticker the frame loop runs on, matchMedia branching and teardown

No GSAP plugins. GSAP plays the timed values; the frame loop owns the continuous ones (the light's chase and drift, the sphere's turn); the shader quantises everything into cells each frame.

Browser Support

Anything with WebGL, which is every current browser. Support is probed before three.js is asked for a renderer, so a visitor with WebGL disabled gets the baked frame and a clean console. :has() is used only by the demo's styling.

Performance

One draw call per frame, a single full-screen pass. The loop runs only while the layer is on screen and the tab is visible. Pixel ratio is capped at 2 (1.5 on touch devices) and always divides the screen's own ratio by a whole number, so cells stay sharp. To buy back frames on low-end hardware, raise data-dither-cell or set data-dither-drift="0" and data-dither-turn="0".

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.