Noisy/Grainy backgrounds and gradients in CSS

A quick guide on how to create a noisy background and gradient in CSS.

Recently, there's been a trend of using noisy backgrounds, whether static, animated, or combined with gradients. The simplest implementation is to use a big background image, but that's not the most performant solution and can negatively impact UX, especially on slow internet connections.

So, here's a quick guide on creating a noisy background and gradient in CSS.

We'll use SVG filters, specifically feTurbulence, to create noise. The baseFrequency and numOctaves parameters determine the amount of noise. For more information about SVG Filters, check out this article.

<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
  <filter id="noise" x="0" y="0">
    <feTurbulence
      type="fractalNoise"
      baseFrequency="0.65"
      numOctaves="3"
      stitchTiles="stitch"
    />
    <feBlend mode="screen" />
  </filter>
  <rect width="500" height="500" filter="url(#noise)" opacity="0.5" />
</svg>

Let's start by creating a pseudo-element that we can reuse in many elements of our app. It includes an inlined SVG of the noise pattern we created with feTurbulence. It will be much more performant to load a few lines of SVG than a big background image. From the network perspective but also parsing time, it's a huge difference. If you don't like this pattern, you can create your own here: https://grainy-gradients.vercel.app/

.noise:before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='500' height='500'%3E%3Cfilter id='noise' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeBlend mode='screen'/%3E%3C/filter%3E%3Crect width='500' height='500' filter='url(%23noise)' opacity='0.5'/%3E%3C/svg%3E");
  mix-blend-mode: soft-light;
}

And that's almost it! Now, we only need to apply the class to an element, and we're done. Here's the full code:

<div className="h-96 w-full relative noise bg-gradient-to-b from-white to-pink-500/50" />

And here's how it looks:

One drawback I found with this solution is that you can't properly animate it. If you want the background to progressively change from transparent to full opacity, you'll have to use JS and modify the alpha value of the background color. This is more expensive than using simple CSS keyframes, but it's an acceptable tradeoff for me.

I was too lazy to convert the code to bare CSS, so it's in TailwindCSS. If you prefer to use CSS, you can use a TailwindCSS to CSS converter like this one.


Published on July 6, 2024 2 min read