AI CSS Animations

Old Film Flicker Text

A silent-movie title card: sepia frame, serif caps, a weaving gate, uneven brightness, jumping scratches and dust. Scratches and dust are background layers repositioned in steps.

Also known as: vintage film text effect, silent movie title card, film grain flicker.

THE END

HTML

<div class="txt-old-film-text"><span class="txt-old-film-text-title">THE END</span></div>

CSS

.txt-old-film-text {
  --c: var(--txt-color, #3b82f6);
  font-size: var(--txt-size, 32px);
  font-family: system-ui, sans-serif;
  font-weight: 700;
  line-height: 1.2;
  position: relative;
  display: inline-block;
  overflow: hidden;
  padding: 0.6em 1.3em;
  background: radial-gradient(ellipse at center, color-mix(in srgb, var(--c) 10%, #4a3f33), #15110d 85%);
  box-shadow: inset 0 0 0 0.14em #15110d, inset 0 0 0 0.18em color-mix(in srgb, var(--c) 10%, #cbbd9f);
  color: color-mix(in srgb, var(--c) 10%, #f3ead6);
  font-family: Georgia, "Times New Roman", ui-serif, serif;
  font-weight: 400;
  letter-spacing: 0.24em;
  animation: txt-old-film-text-exposure 0.9s steps(1, end) infinite;}
.txt-old-film-text-title {
  display: inline-block;
  margin-right: -0.24em;
  animation: txt-old-film-text-weave 0.48s steps(1, end) infinite;
}
/* two scratches */
.txt-old-film-text::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(rgb(243 234 214 / 0.55), rgb(243 234 214 / 0.55)) 22% 0 / 0.03em 100% no-repeat,
    linear-gradient(rgb(243 234 214 / 0.3), rgb(243 234 214 / 0.3)) 71% 0 / 0.02em 100% no-repeat;
  animation: txt-old-film-text-scratch 0.64s steps(1, end) infinite;
}
/* dust */
.txt-old-film-text::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle, rgb(243 234 214 / 0.8) 0.025em, transparent 0.04em) 15% 30% / 0.1em 0.1em no-repeat,
    radial-gradient(circle, rgb(0 0 0 / 0.7) 0.03em, transparent 0.045em) 80% 70% / 0.1em 0.1em no-repeat,
    radial-gradient(circle, rgb(243 234 214 / 0.6) 0.02em, transparent 0.035em) 55% 15% / 0.1em 0.1em no-repeat;
  animation: txt-old-film-text-dust 0.36s steps(1, end) infinite;
}
@keyframes txt-old-film-text-exposure {
  0%, 100% { opacity: 1; }
  22% { opacity: 0.86; }
  45% { opacity: 0.96; }
  67% { opacity: 0.9; }
}
@keyframes txt-old-film-text-weave {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(0.01em, -0.02em); }
  50% { transform: translate(-0.01em, 0.01em); }
  75% { transform: translate(0, -0.01em); }
}
@keyframes txt-old-film-text-scratch {
  0%, 100% { background-position: 22% 0, 71% 0; }
  25% { background-position: 64% 0, 12% 0; }
  50% { background-position: 35% 0, 88% 0; }
  75% { background-position: 80% 0, 47% 0; }
}
@keyframes txt-old-film-text-dust {
  0%, 100% { background-position: 15% 30%, 80% 70%, 55% 15%; opacity: 1; }
  33% { background-position: 70% 80%, 25% 20%, 90% 55%; opacity: 0.6; }
  66% { background-position: 40% 60%, 60% 35%, 10% 85%; opacity: 1; }
}

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

Customizing this snippet

  • Color: set --txt-color on the element or a parent. Currently previewing #3b82f6.
  • Font size: set --txt-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 40px.
  • 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 text animations