A touch of noise is the easiest way to stop a flat-colour background from looking
lifeless. This generator builds that grain as an SVG feTurbulence filter, so the
texture is resolution-independent, weighs almost nothing, and drops straight into
your CSS as a background-image.
How it works
SVG ships with a procedural noise filter. The feTurbulence primitive produces
Perlin or fractal noise, and a feColorMatrix desaturates it to a neutral grain:
<filter id="noise">
<feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="3" stitchTiles="stitch"/>
<feColorMatrix type="saturate" values="0"/>
</filter>
<rect width="100%" height="100%" filter="url(#noise)" opacity="0.15"/>
baseFrequency sets how dense the grain is, numOctaves adds detail layers, and
the rectangle’s opacity keeps the effect subtle. The whole SVG is then
URL-encoded into a data:image/svg+xml URI so it can sit inside url().
Fractal noise vs turbulence — which to choose
The type attribute on feTurbulence controls the character of the grain:
- fractalNoise produces smooth, continuous blobs of tone — like the surface of a well-worn piece of paper or the velvety texture of matte paint. It is the right choice for subtle background depth on hero sections, cards, and UI surfaces.
- turbulence folds the noise into sharp creases, giving a more dramatic, cloudy, or marbled appearance. It reads more as “texture” than “grain” and works well for illustrative backgrounds and generative art effects.
For most UI work, fractalNoise at low opacity is almost invisible yet transforms a flat #F3F3F3 slab into something that feels printed rather than digital.
Tuning the parameters
| Parameter | Low value | High value |
|---|---|---|
| baseFrequency | Coarse, chunky grain | Fine, tight grain — 0.65–0.9 is typical |
| numOctaves | Smooth, uniform | Richer, more natural — 3–4 is usually enough |
| opacity on rect | Barely visible | Dominates the background — keep below 0.25 |
A good starting point for most UI surfaces: baseFrequency="0.75", numOctaves="3", opacity="0.12". Crank frequency up toward 0.9 for a tight film-grain look on dark backgrounds. Drop it to 0.4 for a broader, painterly feel.
Tips and notes
Keep opacity low — somewhere around 0.05 to 0.2 reads as texture rather than
static. Use stitchTiles="stitch" (already included) so the noise tiles seamlessly
if the element repeats. Pair the noise layer with a background-color underneath
it, since the grain itself is transparent grey and needs a colour to sit on. Avoid
animating baseFrequency, as recomputing turbulence each frame is one of the more
expensive filter operations in a browser.
Using the output in CSS
The copied background-image value is already a valid data URI. Use it in a stack with your background colour:
.hero {
background-color: #1a1a2e;
background-image: url("data:image/svg+xml,...");
background-repeat: repeat;
}
Because SVG noise is vector, it stays sharp on high-density (Retina) screens without the blurriness you get from scaling a raster PNG grain texture. The file size is typically under 500 bytes regardless of the element’s display dimensions.