20 CSS Loaders 18 / 20

CSS Infinity Loop Loader

Three infinity and loop CSS loaders — a lemniscate dot path, an SVG animateMotion tracer, and a Möbius counter-spin — creating hypnotic continuous-flow loading indicators.

Pure CSS MIT licensed
Live Demo Open in tab

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<div class="ld-18">
  <div class="ld-18__stage">
    <div class="ld-18__cell">
      <div class="ld-18__lemniscate">
        <div class="ld-18__lem-track">
          <div class="ld-18__lem-dot"></div>
          <div class="ld-18__lem-dot"></div>
          <div class="ld-18__lem-dot"></div>
        </div>
.ld-18,.ld-18 *,.ld-18 *::before,.ld-18 *::after{box-sizing:border-box;margin:0;padding:0}
.ld-18{
  --bg:#05050a;--c1:#00e5ff;--c2:#ff1744;--c3:#76ff03;--c4:#ffea00;
  background:var(--bg);display:flex;align-items:center;justify-content:center;min-height:100vh;font-family:'Segoe UI',sans-serif;
}
.ld-18__stage{display:flex;gap:80px;flex-wrap:wrap;justify-content:center;padding:40px;align-items:center}
.ld-18__cell{display:flex;flex-direction:column;align-items:center;gap:24px}
.ld-18__label{color:rgba(255,255,255,.3);font-size:11px;letter-spacing:1.5px;text-transform:uppercase}

/* Figure-8 / Infinity */
.ld-18__infinity{width:100px;height:50px;position:relative}
.ld-18__infinity svg{width:100px;height:50px}
.ld-18__inf-path{fill:none;stroke:rgba(0,229,255,.15);stroke-width:3}
.ld-18__inf-dot{animation:ld-18-inf-move 2s linear infinite}
@keyframes ld-18-inf-move{0%{offset-distance:0%}100%{offset-distance:100%}}

/* Lemniscate dot tracker */
.ld-18__lemniscate{width:110px;height:55px;position:relative}
.ld-18__lem-track{position:absolute;inset:0}
.ld-18__lem-dot{width:10px;height:10px;border-radius:50%;background:var(--c1);box-shadow:0 0 10px var(--c1);position:absolute;animation:ld-18-lem 2.4s ease-in-out infinite}
.ld-18__lem-dot:nth-child(1){animation-delay:0s}
.ld-18__lem-dot:nth-child(2){animation-delay:.8s;background:var(--c2);box-shadow:0 0 10px var(--c2)}
.ld-18__lem-dot:nth-child(3){animation-delay:1.6s;background:var(--c3);box-shadow:0 0 10px var(--c3)}
@keyframes ld-18-lem{
  0%{top:22px;left:50px}
  12.5%{top:2px;left:80px}
  25%{top:22px;left:100px}
  37.5%{top:42px;left:80px}
  50%{top:22px;left:50px}
  62.5%{top:2px;left:20px}
  75%{top:22px;left:0px}
  87.5%{top:42px;left:20px}
  100%{top:22px;left:50px}
}

/* Möbius strip illusion */
.ld-18__mobius{width:90px;height:45px;position:relative}
.ld-18__mobius::before,.ld-18__mobius::after{content:'';position:absolute;border-radius:50px;border:4px solid transparent}
.ld-18__mobius::before{width:40px;height:40px;top:0;left:0;border-top-color:var(--c4);border-left-color:var(--c4);animation:ld-18-mob1 2s ease-in-out infinite}
.ld-18__mobius::after{width:40px;height:40px;top:0;right:0;border-bottom-color:var(--c2);border-right-color:var(--c2);animation:ld-18-mob2 2s ease-in-out infinite}
@keyframes ld-18-mob1{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
@keyframes ld-18-mob2{0%{transform:rotate(0)}100%{transform:rotate(-360deg)}}

@media(prefers-reduced-motion:reduce){
  .ld-18__inf-dot,.ld-18__lem-dot,.ld-18__mobius::before,.ld-18__mobius::after{animation:none}
}

How this works

The lemniscate path animation uses eight keyframe percentage stops that map each dot's position along a figure-eight lemniscate curve defined by absolute top/left pixel coordinates. All three dots run the same keyframe but with staggered delays of 0s, 0.8s, 1.6s (evenly spaced at duration/3), so they always maintain equal spacing on the path. The positions were derived from the parametric equations x = a·cos(t)/(1+sin²(t)) and y = a·sin(t)cos(t)/(1+sin²(t)) sampled at eight even intervals.

The SVG tracer uses <animateMotion> with the same path string as the visible polyline, so the glowing dot follows the drawn track exactly at all sizes. Using path= directly on animateMotion is more reliable than <mpath> references for inline SVGs. The Möbius spin places two arc-shaped pseudo-elements that spin in opposite directions — ::before clockwise, ::after counter-clockwise — both coloured differently to create a continuously interweaving pattern.

Customize

  • Add more lemniscate dots by duplicating .ld-18__lem-dot elements and adjusting the delay to duration / dotCount per dot for even spacing.
  • Change the Möbius spin speed ratio between the two arcs — currently both run at the same speed; try 2s / 3s for an off-beat weave.
  • Edit the SVG path string to create different loop shapes — a trefoil knot or figure-9 can be traced by updating the animateMotion path attribute.
  • Resize the lemniscate by scaling all coordinate values proportionally — multiply every top/left value by the same factor and update the container dimensions.
  • Add a colour transition to each lemniscate dot using a background keyframe step so the dot changes hue as it traverses the path.

Watch out for

  • The lemniscate positions are hardcoded pixel values that assume a 110×55px container — changing container size without updating all eight keyframe coordinates breaks the path shape.
  • SVG <animateMotion> is SMIL-based; while browsers maintain support, it is not part of CSS Animations — if a CSS-only constraint is required, replace with JS-driven offset-path motion.
  • The Möbius spin creates a visual loop illusion but the two arcs actually occupy the same width/height space — if the parent element has overflow:hidden, the counter-rotating arc will clip during its rotation cycle.

Browser support

ChromeSafariFirefoxEdge
49+ 9+ 44+ 49+

SVG animateMotion (SMIL) is supported in all modern browsers; CSS offset-path is the modern alternative for pure-CSS motion paths.

Search CodeFronts

Loading…