Browse through Dribbble or Behance, and you’ll find designers using a simple technique to add texture to an image: noise. Adding noise makes otherwise solid colors or smooth gradients, such as shadows, more realistic. But despite designers’ affinity for texture, noise is rarely used in web design.
In this article, we’ll generate colorful noise to add texture to a gradient with only a small amount of CSS and SVG. Alright, let’s make some noise!
Interactive playground
Check it out here. The quickest way to understand what’s happening is to play with the parameters that make up the layers.
The trick: SVG noise and CSS gradients
The core technique in this article is built on top of a Stack Overflow answer by Chris Pachl to the question: Can you add noise to a CSS gradient?
The trick is to use an SVG filter to create the noise, then apply that noise as a background. Layer it underneath a gradient, boost the brightness and contrast, and that’s it — you have gradient that gradually dithers away.
The key ingredients
Here’s what we’re working with under the hood:
- SVG turbulence: This is our noise filter.
- Background with gradient and SVG: Next, we drop that filter into CSS as a background image that combines the filter with a CSS gradient.
- Boost brightness and contrast: Then we turn to CSS
filter
to increase the brightness and contrast of the noise. - Blend gradients: Finally, we optionally use
mix-blend-mode
to further filter out colors and blend gradients together.
Let’s go into detail on each of these parts.
Using SVG turbulence
Within the realm of SVG, we can define filters, and one such filter lets us create Perlin noise. It’s called <feTurbulence>
and we can define attributes, such as whether it is “turbulence” or “noise” and how fine or coarse it is. Bence Szabó explains it in much more detail as he demonstrates how it can be used to create patterns.
<svg viewBox="0 0 200 200" xmlns='http://www.w3.org/2000/svg'>
<filter id='noiseFilter'>
<feTurbulence
type='fractalNoise'
baseFrequency='0.65'
numOctaves='3'
stitchTiles='stitch' />
</filter>
<rect width='100%' height='100%' filter='url(#noiseFilter)' />
</svg>
This SVG example creates a filter and renders a <rect>
element that we can use for our grainy gradients. Notice that the SVG <filter>
is defined separately from the <rect>
, and the <rect>
simply references it.
Play around with changing some of the properties of <feTurbulence>
We’re going to save this SVG as a separate file. We reference an external link to grab the SVG in the demos throughout in this article. In practice, though, you would reference a local file or your own CDN. It doesn’t work to reference the SVG by its id
in CSS, for some quirky reason, but you can inline the SVG, as we show in the playground demo. We don’t do this in the demos for legibility reasons.
background
with SVG and a gradient
Creating a CSS After we have the SVG file stored somewhere we can reference it by a URL or path, we can now use it in a CSS background
, combined with a gradient.
.noise {
/* ... */
background:
linear-gradient(to right, blue, transparent),
url(https://grainy-gradients.vercel.app/noise.svg);
}
Order matters here. In this particular example, we want a solid color (i.e. no noise) to transition into noise and then into another color. We also want one end of the gradient to be transparent so that the noise shows through.
Like this:
However, this isn’t particularly nice because the noise is too muddled. We need to fray it and make it grainier. We can do that by…
Boosting the brightness and contrast
Adding a CSS filter
makes the noise more stark, pushing the most faded colors towards white or black. The filter applies to the entire <div>
, so the leftmost blue is a different blue than the pure blue we started with.
.noise {
/* ... */
background:
linear-gradient(to right, blue, transparent),
url(https://grainy-gradients.vercel.app/noise.svg);
filter: contrast(170%) brightness(1000%);
}
You can play around with how contrast and brightness affect the gradient. Boosting the brightness and contrast pushes out the muddled grays in the follow demo.
The noise is not uniform in color
If you zoom in, you’ll notice that the noise is made up of many colors. The SVG filter was colorful to begin with, and increasing the brightness and contrast emphasized certain colors. Although hardly noticeable, if this confetti is unwelcome, we can continue to filter out colors with CSS blending (i.e. mix-blend-mode
and background-blend-mode
).
CSS blending
Let’s make a grainy gradient that transitions between two colors. CSS blending allows us to stack layers of color. In the next example, we’re adding another <div>
to the markup, positioning it over the original gradient, then applying mix-blend-mode: multiply;
to smooth things out.
<section>
<div class="isolate">
<div class="noise"></div>
<div class="overlay"></div>
</div>
</section>
.noise {
/* ... */
background:
linear-gradient(20deg, rebeccapurple, transparent),
url(https://grainy-gradients.vercel.app/noise.svg);
contrast(170%) brightness(1000%);
}
.overlay {
/* ... */
background: moccasin;
mix-blend-mode: multiply;
}
We can use the CSS isolation
property to create a new stacking context and choose what gets blended. If we were to leave out isolation
in the next example, the gradient and overlay would blend with the background color. Try it in the Pen and comment out that line!
/* Same as before */
.isolate {
isolation: isolate;
/* ... */
}
Some use cases
We’ve looked at a pretty simple example of how to make a noisy gradient, but where might you use one? Let’s consider several use cases.
Light and shadows, with grain
Where do gradients naturally occur? Light and shadows, for one. We can take advantage of the CSS property mix-blend-mode
to smoothly blend gradients and selectively filter the colors we want to see in the noise.
In the “shadow” example, we create a dark gradient, and invert it to create the effect in the “light” example. In both cases, mix-blend-mode
allows us to blend it with other gradients.
Holographic foil
The drastic brightness and contrast boost creates a rainbow effect that’s reminiscent of holographic foil.
Taking things further
Try the playground and mess around with the different parameters to see how they affect the texture.
Beyond that, here are some ways to continue fiddling with this technique:
- Use a different SVG: All of the gradients in this article use the same SVG, but you can toy with the parameters that generates the noise to adjust the coarseness as well as the look and feel in the playground.
- Try different gradients: Besides
linear-gradient
, CSS offers four more types of gradients. Can you name them? (Here’s one.) - Add more layers: With CSS blending, you can stack as many any layers as you’d like and blend them down.
- Apply different SVG filters: There are all kinds of filters, including Gaussian blur and different types of lighting. Plus, they can be referenced in a CSS filter and applied to any element in addition to SVG.
What else can you think of? Please let us know what you discover in the comments.
Browser support
We can’t escape talking about browser support here. The core of this technique is supported by all modern browsers. As you might expect, it does not work in Internet Explorer. That said, Internet Explorer does support SVG as a background in CSS (just not with the actual CSS filter
property).
SVG as a CSS background image
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
5 | 24 | 9 | 16 | 5 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
126 | 127 | 3 | 4.2-4.3 |
CSS filter effects
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
18* | 35 | No | 79 | 6* |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
126 | 127 | 4.4* | 6.0-6.1* |
I’ve also noticed that Blink-based browsers (e.g. Chrome) and WebKit-based one (e.g. Safari) implement mix-blend-mode
slightly differently, so please be sure to test across browsers if using CSS blending. In my own projects, I’ve used browser-specific media queries to manually reconcile the visual differences with small tweaks to CSS.
That’s it! Now that you have a grasp of SVG filters and how to combine them with CSS filters as a background, you have yet another neat visual effect to add depth and texture to a design.
Wow! Really cool. I have been playing around with different techniques to create a grainy effect but never thought of svg filters…
Note that adding noise isn’t just just for artistic purposes. Adding subtle noise can also be useful to combat banding when you use long gradients with subtle color changes.
This is really cool (and useful!).
Is it possible to use this technique / effect with CSS box-shadow? Looking for a way to add noise to a drop shadow
Hey Ian – I don’t think you’ll be able to use it with box-shadow, but I tried to create the effect of a box-shadow in this Pen: https://codepen.io/cjimmy/pen/wvexYoJ (I’ve only tested it in Safari. Results may differ with other browsers)
The main idea is that you can add more
linear-gradient()
s to create a shape and then invert it. I made a border of a box with dark grainy gradients: dark borders, light inside. You can then inverted it: dark rectangle inside, light outside. To remove the light part withmix-blend-mode
.It may not be exactly what you’re seeking, but perhaps it inspires a better method for you. (If so, please do share!)
I don’t know where, I don’t know how, but I’m going to use this.
Can you use two colours that look familiar for Grainy Gradients?
Ever since I read this article, I can’t help but wonder if you could use this grainy noise effect as a mask on the edge of a div.
If any SVG wizards want to give it a whirl, let me know!
If only this trick didn’t have to suck performance-wise…
By relying on extreme contrast and brightness, this methods forgoes a huge range of colors.
Using a mask is a much more effective way of achieving this effect.
I needed a more simpliest solution, so i used only one :before css line to achieve this: https://jsfiddle.net/PetrHaluza/jre3dxhq/