AI CSS Animations

Film Grain Background

An old footage look: fine grain that jitters every few frames, a soft vignette, a faint flicker and the odd scratch. The grain is SVG turbulence rendered once, then moved in steps.

Also known as: film grain overlay css, animated noise background, grain texture animation.

HTML

<div class="bgx-film-grain" aria-hidden="true">
  <span class="bgx-film-grain-noise"></span>
  <span class="bgx-film-grain-scratch"></span>
</div>

CSS

.bgx-film-grain {
  --c: var(--bgx-color, #3b82f6);
  font-size: var(--bgx-size, 16px);
  position: absolute;
  inset: 0;
  overflow: hidden;
  container-type: size;
  background: radial-gradient(ellipse 85% 80% at 50% 45%, color-mix(in srgb, var(--c) 32%, #1c1d21), color-mix(in srgb, var(--c) 6%, #060607) 85%);
}
.bgx-film-grain-noise {
  position: absolute;
  inset: -25%;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' preserveAspectRatio='none'%3E%3Cfilter id='g' x='0' y='0' width='100%' height='100%'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 4 0 0 0 -2.1'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23g)'/%3E%3C/svg%3E") 0 0 / 14em 14em;
  opacity: 0.4;
  animation: bgx-film-grain-jitter 0.7s steps(1) infinite;
}
.bgx-film-grain-scratch {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 1px;
  background: linear-gradient(transparent 5%, rgb(255 255 255 / 0.4) 25% 70%, transparent 95%);
  opacity: 0;
  animation: bgx-film-grain-scratch 3.4s steps(1) infinite;
}
.bgx-film-grain::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  animation: bgx-film-grain-flicker 1.3s steps(1) infinite;
}
@keyframes bgx-film-grain-jitter {
  0%   { transform: translate(0, 0); }
  10%  { transform: translate(-3%, -5%); }
  20%  { transform: translate(-7%, 2%); }
  30%  { transform: translate(4%, -8%); }
  40%  { transform: translate(-2%, 6%); }
  50%  { transform: translate(-6%, 3%); }
  60%  { transform: translate(7%, 1%); }
  70%  { transform: translate(1%, 5%); }
  80%  { transform: translate(3%, 8%); }
  90%  { transform: translate(-5%, 4%); }
}
@keyframes bgx-film-grain-scratch {
  0%   { transform: translateX(23cqw); opacity: 0.9; }
  6%   { transform: translateX(24cqw); opacity: 0.5; }
  12%  { opacity: 0; }
  50%  { transform: translateX(68cqw); opacity: 0.7; }
  54%  { transform: translateX(67cqw); opacity: 0.35; }
  60%  { opacity: 0; }
  100% { transform: translateX(67cqw); opacity: 0; }
}
@keyframes bgx-film-grain-flicker {
  0%   { opacity: 0.08; }
  20%  { opacity: 0; }
  35%  { opacity: 0.12; }
  45%  { opacity: 0.03; }
  60%  { opacity: 0.09; }
  80%  { opacity: 0; }
  90%  { opacity: 0.05; }
}

@media (prefers-reduced-motion: reduce) {
  .bgx-film-grain, .bgx-film-grain *, .bgx-film-grain::before, .bgx-film-grain::after, .bgx-film-grain *::before, .bgx-film-grain *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --bgx-color on the element or a parent. Currently previewing #3b82f6.
  • Pattern scale: set --bgx-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 16px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar animated backgrounds