Noise & Texture Lab
Generate film grain, or turn an image into a dither, halftone or ASCII
/* Film grain overlay. Give the parent position: relative;
the ::after covers it without eating clicks. */
.grain {
position: relative;
}
.grain::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22128%22%20height%3D%22128%22%20viewBox%3D%220%200%20128%20128%22%3E%20%3Cfilter%20id%3D%22n%22%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%20%3CfeTurbulence%20type%3D%22fractalNoise%22%20baseFrequency%3D%220.701%22%20numOctaves%3D%223%22%20stitchTiles%3D%22stitch%22%20seed%3D%227%22%2F%3E%20%3CfeColorMatrix%20type%3D%22saturate%22%20values%3D%220%22%2F%3E%20%3C%2Ffilter%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20filter%3D%22url(%23n)%22%20opacity%3D%220.40%22%2F%3E%20%3C%2Fsvg%3E");
background-size: 128px 128px;
mix-blend-mode: soft-light;
}relative after:absolute after:content-[''] after:inset-0 after:pointer-events-none after:bg-[url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22128%22%20height%3D%22128%22%20viewBox%3D%220%200%20128%20128%22%3E%20%3Cfilter%20id%3D%22n%22%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%20%3CfeTurbulence%20type%3D%22fractalNoise%22%20baseFrequency%3D%220.701%22%20numOctaves%3D%223%22%20stitchTiles%3D%22stitch%22%20seed%3D%227%22%2F%3E%20%3CfeColorMatrix%20type%3D%22saturate%22%20values%3D%220%22%2F%3E%20%3C%2Ffilter%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20filter%3D%22url(%23n)%22%20opacity%3D%220.40%22%2F%3E%20%3C%2Fsvg%3E")] after:bg-[length:128px_128px] after:mix-blend-soft-light<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
<filter id="n" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency="0.701" numOctaves="3" stitchTiles="stitch" seed="7"/>
<feColorMatrix type="saturate" values="0"/>
</filter>
<rect width="100%" height="100%" filter="url(#n)" opacity="0.40"/>
</svg>Now use it on something
Every effect, block, page and template in the catalog is styled against tokens shaped exactly like these — so they take your values rather than fighting them.
npx shadcn@latest add https://hoverlab.dev/r/hoverlab.json
fractalNoise is the film-grain one. turbulence folds the noise back on itself and reads as marble or water.
Grain size, inverted: low values are big soft blotches, high values are fine sand. The slider is logarithmic because that is how the change is perceived.
Layers of detail stacked at doubling frequencies. One is flat static; more adds structure, with diminishing returns past four.
Baked into the SVG itself, so the one background-image rule is the whole effect. Grain wants to be felt, not seen — most surfaces sit under 50%.
Desaturates the noise so it acts as pure luminance grain. Off, feTurbulence emits RGB static — colourful confetti rather than film.
How the grain meets the surface underneath. soft-light and overlay keep the surface colour; multiply darkens, screen lightens, normal just paints on top.
Edge length of the repeating tile. stitchTiles makes the seams invisible, so smaller tiles mean a shorter data URI at no visual cost — go bigger only if you can spot the repeat.
Keep this grain. This browser remembers what you last had open. A free account remembers the ones you name, on every machine you use.
- PlaceholdersPlaceholder images and initial avatars as SVG data URIs. No third-party request, works offline, and the avatar colour is derived from the name so it is stable everywhere.
- Section DividerWaves, tilts, notches and arcs for the seam between two full-width bands, as scalable SVG. Emits currentColor so one snippet works in both themes, with the baseline gap and the antialiased hairline already handled.
- SVG ToolkitThe four SVG jobs that are always four different websites — optimise, convert to JSX or a data URI, generate a pattern, generate a wave — with one source shared between them, so the file you clean is the file you convert.