AI CSS Animations

Doppler Effect Loader

A source glides back and forth sending out rings that bunch up ahead of it and spread out behind, the Doppler effect in miniature. Each ring is born where the source was at that moment.

Also known as: doppler effect animation, moving ripple source, sound wave doppler.

HTML

<div class="ldr-doppler-effect" role="status" aria-label="Loading">
  <span class="ldr-doppler-effect-wave" style="--x: -0.4; --t: 0s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: -0.3; --t: 0.2s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: -0.2; --t: 0.4s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: -0.1; --t: 0.6s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0; --t: 0.8s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0.1; --t: 1s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0.2; --t: 1.2s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0.3; --t: 1.4s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0.4; --t: 1.6s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0.3; --t: 1.8s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0.2; --t: 2s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0.1; --t: 2.2s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: 0; --t: 2.4s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: -0.1; --t: 2.6s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: -0.2; --t: 2.8s"></span>
  <span class="ldr-doppler-effect-wave" style="--x: -0.3; --t: 3s"></span>
  <span class="ldr-doppler-effect-source"></span>
</div>

CSS

.ldr-doppler-effect {
  --s: var(--ldr-size, 48px);
  --c: var(--ldr-color, #3b82f6);
  position: relative;
  width: calc(var(--s) * 2);
  height: calc(var(--s) * 1.2);
}
.ldr-doppler-effect-wave {
  position: absolute;
  top: 50%;
  left: calc(50% + var(--s) * var(--x));
  width: calc(var(--s) * 1.2);
  height: calc(var(--s) * 1.2);
  margin: calc(var(--s) * -0.6);
  box-sizing: border-box;
  border: calc(var(--s) / 22) solid var(--c);
  border-radius: 50%;
  opacity: 0;
  animation: ldr-doppler-effect-wave 3.2s linear infinite;
  animation-delay: var(--t);
}
/* The source moves at 0.5 --s per second, the waves at about 0.67. */
.ldr-doppler-effect-source {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--s) / 5);
  height: calc(var(--s) / 5);
  margin: calc(var(--s) / -10);
  border-radius: 50%;
  background: var(--c);
  animation: ldr-doppler-effect-glide 1.6s linear infinite alternate;
}
@keyframes ldr-doppler-effect-glide {
  from { transform: translateX(calc(var(--s) * -0.4)); }
  to   { transform: translateX(calc(var(--s) * 0.4)); }
}
@keyframes ldr-doppler-effect-wave {
  0%        { transform: scale(0); opacity: 1; }
  28%, 100% { transform: scale(1); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .ldr-doppler-effect, .ldr-doppler-effect *, .ldr-doppler-effect::before, .ldr-doppler-effect::after, .ldr-doppler-effect *::before, .ldr-doppler-effect *::after {
    animation-duration: 3s;
  }
}

Customizing this loader

  • Color: set --ldr-color on the element or a parent. Currently previewing #3b82f6.
  • Size: set --ldr-size. Every dimension in the snippet is derived from it. Currently previewing 64px.
  • Speed: change the duration on the animation shorthand. Delays on the children are relative, so they keep their rhythm.
  • Accessibility: the root carries role="status" and an aria-label so screen readers announce the wait. The reduced-motion rule at the end slows the animation for users who asked for less motion.

Similar loaders