| name | dither-background |
| description | Create a dark monochrome procedural background with enlarged square pixels and visible Bayer-style ordered dithering. Use when a page needs an atmospheric near-black dither field, broad organic waves or cloud masses, and restrained gray-white highlights behind framed UI, hero content, or data overlays. |
Dither Background
Use When
- A dark interface needs an atmospheric monochrome background layer.
- The visual should show enlarged square pixels and visible ordered dithering.
- The design calls for organic waves, cloud-like masses, or procedural depth without colorful gradients.
- The background should support framed UI, hero content, or data overlays.
Visual Target
- Near-black base with charcoal midtones, soft gray buildup, and occasional white highlights.
- Clearly visible square pixel cells, not tiny film grain.
- 4x4 Bayer-style dither pattern or equivalent ordered thresholding.
- Broad organic waves or cloud-like masses, not random TV noise.
- Vignetted edges so the brighter mass sits centrally or off-axis.
HTML And CSS
<canvas class="dither-background" data-dither-background></canvas>
.dither-background {
position: fixed;
inset: 0;
z-index: 0;
width: 100vw;
height: 100vh;
background: #030303;
pointer-events: none;
}
.page-content {
position: relative;
z-index: 1;
}
Canvas Recipe
Use a real canvas when motion or procedural depth is needed.
const BAYER_4X4 = [
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5,
].map((value) => (value + 0.5) / 16);
function smoothstep(edge0, edge1, value) {
const t = Math.max(0, Math.min(1, (value - edge0) / (edge1 - edge0)));
return t * t * (3 - 2 * t);
}
function noise2(x, y) {
const value = Math.sin(x * 127.1 + y * 311.7) * 43758.5453123;
return value - Math.floor(value);
}
function valueNoise(x, y) {
const ix = Math.floor(x);
const iy = .(y);
fx = x - ix;
fy = y - iy;
ux = fx * fx * ( - * fx);
uy = fy * fy * ( - * fy);
a = (ix, iy);
b = (ix + , iy);
c = (ix, iy + );
d = (ix + , iy + );
(
a * ( - ux) * ( - uy) +
b * ux * ( - uy) +
c * ( - ux) * uy +
d * ux * uy
);
}
() {
value = ;
amplitude = ;
frequency = ;
( octave = ; octave < ; octave++) {
value += (x * frequency, y * frequency) * amplitude;
frequency *= ;
amplitude *= ;
}
value;
}
() {
(!canvas) {};
ctx = canvas.(, { : });
(!ctx) {};
reduceMotion = .().;
cell = options. || ;
maxDpr = options. || ;
width = ;
height = ;
cols = ;
rows = ;
rafId = ;
palette = options. || [
[, , ],
[, , ],
[, , ],
[, , ],
[, , ],
[, , ],
];
() {
dpr = .(. || , maxDpr);
width = .(, .);
height = .(, .);
canvas. = .(width * dpr);
canvas. = .(height * dpr);
canvas.. = ;
canvas.. = ;
ctx.(dpr, , , dpr, , );
cols = .(width / cell);
rows = .(height / cell);
}
() {
nx = (x / cols - ) * ;
ny = (y / rows - ) * ;
distance = .(nx * nx * + ny * ny * );
vignette = - (, , distance);
drift = reduceMotion ? : time * ;
wave =
.(nx * + ny * + drift) * +
.(nx * - + ny * - drift * ) * ;
cloud = (nx * + drift * , ny * - drift * );
ridge = (, , cloud + wave);
offAxisMass = (, , .(nx + , ny - ));
.(, .(, ridge * vignette * + offAxisMass * ));
}
() {
seconds = time * ;
ctx. = ;
ctx.(, , width, height);
( y = ; y < rows; y++) {
( x = ; x < cols; x++) {
threshold = [(y % ) * + (x % )];
brightness = (x, y, seconds);
stepped = .(.(, .(, brightness + threshold * )) * palette.);
color = palette[.(palette. - , stepped)];
ctx. = ;
ctx.(x * cell, y * cell, cell, cell);
}
}
(!reduceMotion) rafId = (render);
}
() {
(rafId);
();
();
}
();
();
.(, handleResize);
{
(rafId);
.(, handleResize);
};
}
cleanupDither = (
.(),
{
: ,
: ,
}
);
Tuning Knobs
- Cell size:
5px-10px; larger cells make the Bayer matrix more legible.
- Palette: near-black, charcoal, soft gray, rare white highlights only.
- Shape: tune
wave, cloud, ridge, and offAxisMass to create broad masses.
- Vignette: increase edge falloff when foreground readability needs more quiet.
- Motion: keep drift slow; use low time multipliers and avoid flicker.
- Performance: increase
cellSize or cap maxDpr before simplifying the field.
Composition Notes
- Put the canvas behind the interface with
pointer-events: none.
- Use it as atmosphere behind framed UI, hero copy, or data overlays.
- Keep foreground contrast controlled and typography clean.
- Let one main bright mass define the composition; avoid even full-screen brightness.
Avoid
- Rainbow gradients, colorful noise, or soft blurry blobs without dither structure.
- Tiny grain or static-like speckle where the square matrix disappears.
- Bright full-screen white noise that competes with foreground type.
- Random per-frame noise that flickers instead of drifting.
- Covering the entire viewport with equally bright cells.
Quick Checks
- The square Bayer pattern is visible from normal viewing distance.
- The field forms broad organic waves or cloud masses.
- The palette stays monochrome and restrained.
- Edges recede into near-black.
- Foreground UI remains readable without heavy overlays.