Grain from the browser, not a texture file
An SVG turbulence filter inlined as a data URI: nothing to download, seamless tiling, reproducible from a seed.
Grain from the browser, not a texture file
An SVG turbulence filter as a data URI: nothing to download, it tiles seamlessly, and the same seed always gives the same grain.
What the grain is actually fixing
Add noise to a design and it looks tactile, which is the reason most people reach for it. The more useful reason is banding. A gradient across a large area has to step between colours that are only one value apart in eight-bit colour, and on a wide, shallow gradient those steps become visible as stripes. The picture is correct; there simply are not enough intermediate values to draw it smoothly.
A light layer of noise breaks the boundaries up. The steps are still there, but the eye can no longer find the edges, and the gradient reads as continuous. This is the same trick as dithering, and it is why a faint grain over a big background gradient is worth adding even when you do not want a visible texture — at low opacity the noise is invisible and the banding is gone.
The cost is paint, not bytes
Generated as an inline SVG filter, the grain adds essentially nothing to the page weight — no extra request, no image file, a few hundred characters in the stylesheet. That is the appeal, and it makes it tempting to apply generously.
The expense turns up elsewhere. A turbulence filter is computed rather than fetched, and across a very large surface — a full-page background, an element that animates or scrolls beneath other content — that computation is real work for the browser to repeat. On low-powered devices it is the sort of thing that quietly costs frames. Keep it to a fixed background rather than something being repainted, and treat a full-viewport grain as worth measuring rather than assuming.
How this is made
feTurbulence generates fractal noise in the browser from a seed. There is no image: the filter is inlined as a data URI in the CSS, so it costs one extra rule and no request.
stitchTiles="stitch" makes the tile edges match, so the texture repeats without a visible seam.
Frequency sets how fine the grain is — higher is finer. Octaves adds detail at successively smaller scales and costs render time; three is usually indistinguishable from six.
Keep it off text
Grain over body copy lowers legibility in a way that does not show up in a contrast ratio — the ratio is computed on flat colours and the noise is a per-pixel variation around them.
Put it on the surface behind the content, not on the content, and keep the opacity under about 0.15.
Some readers cannot tolerate it
High-frequency visual noise can trigger discomfort, nausea and migraine. There is no media query that covers it and prefers-reduced-motion does not apply, because the grain is not moving. The mitigation is restraint.