Animated Grainy Texture

Avatar of Geoff Graham
Geoff Graham on (Updated on )

The DayTrip website uses a neat effect on its page header that distorts the background image with an animated, grainy texture. The effect is subtle but creates a dusty, retro vibe.

The effect is very subtle. You can see the the difference where the effect is in place on the right and disabled on the left:

No effect (left) vs. Grainy effect (right)

We can create the same rustic effect with a single image and a little bit of CSS.

Step 1: Setting Things Up

First, let’s set up the our page header. We’re going to use a common pattern where a background image takes up the entire space.

.page-header {
  height: 100vh;
  background-image: url("/path/to/image.jpg");
}

Here’s an example to get us started:

See the Pen RpLKdx by Geoff Graham (@geoffgraham) on CodePen.

Step 2: Selecting a Texture

Next, we need an image with a grainy texture to it. You can create this yourself. Subtle Patterns also has a number of nice options, including this one that we’ll use for our demo. Note that the image does not need to be huge. Something in the neighborhood of 400px square will do the trick.

The idea is that we will overlay the grainy texture on top of the .page-header. We can use the :after pseudo-element on .page-header so there is no need to create another element.

.page-header {
  height: 100vh;
  background-image: url("/path/to/image.jpg");
}

.page-header:after {
  /* content is required when using :after */
  content: "";
  /* The grainy image */
  background-image: url("/path/to/grainy/image.jpg");
  /* Specify a height and width above and beyond the page header for movement */
  height: 300%;
  width: 300%;
  /* We're using opacity in place of a transparent image */
  opacity: 0.3;
  /* We'll need this when the animation kicks in to hold the position of the texture */
  position: fixed;
}

Note that we placed a height and width on the pseudo-element as well as a top and left so that it actually exceeds the boundary of the page header and is centered to it. We want to do this so that the grainy texture layer has room to move around without exposing the page header layer underneath. This means are layers are arranged more like this:

The top layer now exceeds the bounds of the page header

Now we have a nice big page header with a grainy image on top:

See the Pen evGvKg by Geoff Graham (@geoffgraham) on CodePen.

Step 3: Animating the Grainy Layer

The last thing we need to do is animate the grainy layer. This is the effect that we’re going after and gives the page header that retro, analog appeal.

The DayTrip site uses the following to define the animation keyframes:

@keyframes grain {
  0%, 100% { transform:translate(0, 0) }
  10% { transform:translate(-5%, -10%) }
  20% { transform:translate(-15%, 5%) }
  30% { transform:translate(7%, -25%) }
  40% { transform:translate(-5%, 25%) }
  50% { transform:translate(-15%, 10%) }
  60% { transform:translate(15%, 0%) }
  70% { transform:translate(0%, 15%) }
  80% { transform:translate(3%, 35%) }
  90% { transform:translate(-10%, 10%) }
}

It’s sort of tough to visualize what that code means, but it’s basically moving the top grainy layer around in a zig-zag pattern. Here’s an illustration of what that looks like at a smaller scale:

Now all we have to do is apply the keyframes to .page-header:after to see it take effect. We’ll set the animation to play for 8 seconds and loop infinitely:

.page-header:after {
  /* content is required when using :after */
  content: "";
  /* The animation */
  animation: grain 8s steps(10) infinite;
  /* The grainy image */
  background-image: url("/path/to/grainy/image.jpg");
  /* Specify a height and width above and beyond the page header for movement */
  height: 300%;
  width: 300%;
  /* We're using opacity in place of a transparent image */
  opacity: 0.3;
  /* We'll need this when the animation kicks in to hold the position of the texture */
  position: fixed;
}

Putting it All Together

Here’s the full snippet with all the pieces in place. Note that we are assuming the use of Autoprefixer for all vendor prefixing.

.page-header {
  height: 100vh;
  background-image: url("/path/to/image.jpg");
}

.page-header:after {
  animation: grain 8s steps(10) infinite;
  background-image: url("/path/to/grainy/image.jpg");
  content: "";
  height: 300%;
  left: -50%;
  opacity: 0.3;
  position: fixed;
  top: -100%;
  width: 300%;
}

@keyframes grain {
  0%, 100% { transform:translate(0, 0) }
  10% { transform:translate(-5%, -10%) }
  20% { transform:translate(-15%, 5%) }
  30% { transform:translate(7%, -25%) }
  40% { transform:translate(-5%, 25%) }
  50% { transform:translate(-15%, 10%) }
  60% { transform:translate(15%, 0%) }
  70% { transform:translate(0%, 15%) }
  80% { transform:translate(3%, 35%) }
  90% { transform:translate(-10%, 10%) }
}

See the Pen Animated Grainy Effect by Geoff Graham (@geoffgraham) on CodePen.