Back to the library

Pulse beacon, double tap

Variant: Double tap

Two rings on the same cycle, offset by 220ms. The offset is the whole idea: one ring reads as a clock ticking, two read as a heartbeat — something alive on the other end of a connection. Good for presence and live-link indicators where "connected" is the message.

statuslooppresencedot

The source

A complete HTML document. Paste it into an empty .html file and it plays — no stylesheet, script, font, or image to fetch.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Pulse beacon — double tap</title>
    <style>
      :root {
        --ivory: #faf9f5;
        --slate: #141413;
        --olive: #788c5d;
        --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
      }

      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
      }

      body {
        min-height: 100vh;
        display: grid;
        place-items: center;
        background: var(--ivory);
        font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
        color: var(--slate);
      }

      .status {
        display: flex;
        align-items: center;
        gap: 12px;
        font-size: 14px;
        letter-spacing: -0.01em;
      }

      .beacon {
        position: relative;
        width: 12px;
        height: 12px;
        flex: none;
      }

      .beacon span {
        position: absolute;
        inset: 0;
        border-radius: 50%;
      }

      .beacon .core {
        background: var(--olive);
      }

      /* Two rings on one cycle, offset by 220ms — a heartbeat rather than a
         metronome. The offset is the whole idea: a single ring reads as a
         clock, two read as something alive and still connected. */
      .beacon .ring {
        border: 1.5px solid var(--olive);
        opacity: 0;
        animation: beacon-double 2.6s var(--ease-out) infinite;
      }

      .beacon .ring.trailing {
        animation-delay: 220ms;
      }

      @keyframes beacon-double {
        0% {
          transform: scale(1);
          opacity: 0.6;
        }
        30%,
        100% {
          transform: scale(2.4);
          opacity: 0;
        }
      }

      @media (prefers-reduced-motion: reduce) {
        .beacon .ring {
          animation: beacon-double-fade 2.6s ease infinite;
          transform: scale(2);
        }

        @keyframes beacon-double-fade {
          0%,
          100% {
            opacity: 0;
          }
          15% {
            opacity: 0.45;
          }
        }
      }
    </style>
  </head>
  <body>
    <p class="status">
      <span class="beacon">
        <span class="ring"></span>
        <span class="ring trailing"></span>
        <span class="core"></span>
      </span>
      Agent connected
    </p>
  </body>
</html>

The variant family

Same idea, one axis moved. Compare them side by side before you commit.