Back to the library

Token stream, steady

Variant: Steady

A prompt drops into a model and the completion writes itself back one token at a time, 175ms apart, with the token that just landed held in clay so the leading edge reads as a caret. The evenness is the only claim this diagram makes, and it is the baseline the other two are measured against. Reach for it when documenting a streaming endpoint that behaves.

diagramllm

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" />
    <meta name="animation-knobs" content="speed: 0.7-1.3" />
    <title>Token stream, steady</title>
    <style>
      :root {
        /* Neutrals — fixed, never remixed. */
        --ivory: #faf9f5;
        --paper: #ffffff;
        --oat: #e3dacc;
        --slate: #141413;

        /* Knobs — the remix API. A knob exists iff declared here and used below. */
        --hue: 38.8;
        --lift: 0;
        --chroma: 1;
        --round: 1;
        --speed: 1;

        /* Accents — derived from the knobs with the family's offsets baked in,
           so one hue roll moves them together. Defaults reproduce the original hex. */
        --clay: oklch(calc(0.6724 + var(--lift)) calc(0.1308 * var(--chroma)) var(--hue));
        --clay-d: oklch(calc(0.5797 + var(--lift)) calc(0.127 * var(--chroma)) var(--hue));
        --olive: oklch(
          calc(0.6118 + var(--lift)) calc(0.0713 * var(--chroma)) calc(var(--hue) + 88.3)
        );
        --rust: oklch(
          calc(0.5408 + var(--lift)) calc(0.1357 * var(--chroma)) calc(var(--hue) - 10.3)
        );

        --_text: color-mix(in srgb, var(--slate) 34%, transparent);

        /* Clock — 4.2s loop and a 175ms token period: the period divides the
           loop exactly 24 times, so the metronome never drifts against the
           restart. Both divide by the same --speed, which preserves the ratio
           under any pace roll. */
        --cycle: 4.2s;
        --t: calc(var(--cycle) / var(--speed));
        --_tok: calc(175ms / var(--speed));
      }

      * {
        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);
      }

      .sys {
        width: 192px;
        display: grid;
        justify-items: start;
      }

      /* --- prompt ------------------------------------------------------ */

      .prompt {
        position: relative;
        width: 192px;
        height: 30px;
        display: grid;
        align-content: center;
        gap: 5px;
        padding: 0 10px;
        border-radius: calc(7px * var(--round));
        background: var(--oat);
      }

      .prompt b {
        display: block;
        height: 4px;
        border-radius: 2px;
        background: var(--_text);
      }

      .prompt b:nth-of-type(1) {
        width: 128px;
      }

      .prompt b:nth-of-type(2) {
        width: 86px;
      }

      .prompt .ring {
        border-radius: calc(7px * var(--round));
        animation: sent var(--t) infinite;
      }

      .ring {
        position: absolute;
        inset: -1.5px;
        border: 1.5px solid var(--clay);
        opacity: 0;
        pointer-events: none;
      }

      /* --- channels ---------------------------------------------------- */

      .chan {
        position: relative;
        width: 192px;
      }

      .chan.a {
        height: 16px;
      }

      .chan.b {
        height: 22px;
      }

      .rail {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 50%;
        width: 1.5px;
        margin-left: -0.75px;
        background: var(--oat);
      }

      .pkt,
      .flow {
        position: absolute;
        top: 0;
        left: 50%;
        width: 7px;
        height: 7px;
        margin-left: -3.5px;
        border-radius: 50%;
        background: var(--clay);
        opacity: 0;
      }

      .pkt {
        animation: send var(--t) infinite;
      }

      .flow {
        width: 5px;
        height: 5px;
        margin-left: -2.5px;
      }

      /* One dot leaves every 175ms — the same period as one token. Linear,
         because a packet in transit does not accelerate. */
      .flowgate {
        position: absolute;
        inset: 0;
        opacity: 0;
        animation: gate var(--t) steps(1, end) infinite;
      }

      .flow:nth-child(1) {
        animation: drift calc(var(--_tok) * 2) linear infinite;
      }

      .flow:nth-child(2) {
        animation: drift calc(var(--_tok) * 2) linear calc(var(--_tok) * -1) infinite;
      }

      /* --- model ------------------------------------------------------- */

      .model {
        position: relative;
        width: 192px;
        height: 36px;
        border: 1.5px solid var(--oat);
        border-radius: calc(9px * var(--round));
        animation: receive var(--t) infinite;
      }

      .model .ring {
        border-radius: calc(9px * var(--round));
        animation: live var(--t) infinite;
      }

      .beat {
        position: absolute;
        inset: 0;
        display: grid;
        place-items: center;
        opacity: 0;
        animation: gate var(--t) steps(1, end) infinite;
      }

      .core {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background: var(--clay);
        animation: tick var(--_tok) infinite;
      }

      /* --- completion -------------------------------------------------- */

      .out {
        width: 192px;
        display: grid;
        gap: 9px;
        overflow: hidden;
        animation: hold var(--t) infinite;
      }

      .line {
        position: relative;
        height: 7px;
      }

      .l1 {
        width: 288px;
      }

      .l2 {
        width: 192px;
      }

      .row {
        position: absolute;
        inset: 0;
        display: flex;
      }

      .row span {
        flex: 0 0 24px;
      }

      .row span::before {
        content: '';
        display: block;
        height: 7px;
        border-radius: 3.5px;
        background: currentColor;
      }

      /* Settled tokens are text; the one that just landed is clay. The clay
         window is one cell wide and steps forward, so it reads as a caret. */
      .ink {
        color: var(--_text);
      }

      .hot {
        color: var(--clay);
      }

      .l1 span:nth-child(1)::before {
        width: 18px;
      }
      .l1 span:nth-child(2)::before {
        width: 12px;
      }
      .l1 span:nth-child(3)::before {
        width: 20px;
      }
      .l1 span:nth-child(4)::before {
        width: 14px;
      }
      .l1 span:nth-child(5)::before {
        width: 16px;
      }
      .l1 span:nth-child(6)::before {
        width: 12px;
      }
      .l1 span:nth-child(7)::before {
        width: 19px;
      }
      .l1 span:nth-child(8)::before {
        width: 15px;
      }
      .l1 span:nth-child(n + 9)::before {
        display: none;
      }

      .l2 span:nth-child(1)::before {
        width: 13px;
      }
      .l2 span:nth-child(2)::before {
        width: 20px;
      }
      .l2 span:nth-child(3)::before {
        width: 16px;
      }
      .l2 span:nth-child(4)::before {
        width: 11px;
      }
      .l2 span:nth-child(n + 5)::before {
        display: none;
      }

      .l1 .ink {
        animation: ink-a var(--t) infinite;
      }
      .l1 .hot {
        animation: hot-a var(--t) infinite;
      }
      .l2 .ink {
        animation: ink-b var(--t) infinite;
      }
      .l2 .hot {
        animation: hot-b var(--t) infinite;
      }

      /* --- timeline ---------------------------------------------------- */

      @keyframes sent {
        0%,
        1% {
          opacity: 0;
          animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
        }
        4% {
          opacity: 1;
        }
        11%,
        100% {
          opacity: 0;
        }
      }

      @keyframes send {
        0%,
        4% {
          opacity: 0;
          transform: translateY(-4px);
        }
        5% {
          opacity: 1;
          transform: translateY(-4px);
          animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
        }
        11.5% {
          opacity: 1;
          transform: translateY(14px);
        }
        12%,
        100% {
          opacity: 0;
          transform: translateY(14px);
        }
      }

      @keyframes receive {
        0%,
        11.4% {
          transform: scale(1);
        }
        11.5% {
          transform: scale(0.99);
          animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
        }
        15%,
        100% {
          transform: scale(1);
        }
      }

      @keyframes live {
        0%,
        11.5% {
          opacity: 0;
          animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
        }
        15%,
        66.667% {
          opacity: 1;
        }
        71%,
        100% {
          opacity: 0;
        }
      }

      @keyframes gate {
        0% {
          opacity: 0;
        }
        16.667%,
        66.6% {
          opacity: 1;
        }
        66.667%,
        100% {
          opacity: 0;
        }
      }

      @keyframes tick {
        0% {
          transform: scale(1.3);
          opacity: 0.95;
          animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
        }
        55%,
        100% {
          transform: scale(1);
          opacity: 0.5;
        }
      }

      @keyframes drift {
        0% {
          opacity: 0;
          transform: translateY(0);
        }
        16% {
          opacity: 1;
        }
        76% {
          opacity: 1;
        }
        100% {
          opacity: 0;
          transform: translateY(20px);
        }
      }

      /* Eight equal steps over 33.33% of the loop — one token every 175ms,
         no clustering. The evenness is the entire point of this variant. */
      @keyframes ink-a {
        0%,
        16.667% {
          clip-path: inset(0 288px 0 0);
          animation-timing-function: steps(8, end);
        }
        50%,
        100% {
          clip-path: inset(0 96px 0 0);
        }
      }

      @keyframes hot-a {
        0% {
          clip-path: inset(0 264px 0 0);
          opacity: 0;
          animation-timing-function: steps(1, end);
        }
        16.667% {
          clip-path: inset(0 264px 0 0);
          opacity: 1;
          animation-timing-function: steps(8, end);
        }
        50%,
        100% {
          clip-path: inset(0 72px 0 192px);
          opacity: 1;
        }
      }

      @keyframes ink-b {
        0%,
        50% {
          clip-path: inset(0 192px 0 0);
          animation-timing-function: steps(4, end);
        }
        66.667%,
        100% {
          clip-path: inset(0 96px 0 0);
        }
      }

      @keyframes hot-b {
        0% {
          clip-path: inset(0 168px 0 0);
          opacity: 0;
          animation-timing-function: steps(1, end);
        }
        50% {
          clip-path: inset(0 168px 0 0);
          opacity: 1;
          animation-timing-function: steps(4, end);
        }
        66.667%,
        100% {
          clip-path: inset(0 72px 0 96px);
          opacity: 1;
        }
      }

      @keyframes hold {
        0%,
        88% {
          opacity: 1;
          animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
        }
        94%,
        100% {
          opacity: 0;
        }
      }

      @media (prefers-reduced-motion: reduce) {
        .pkt,
        .flow,
        .flowgate {
          animation: none;
          opacity: 0;
        }

        .model {
          animation: none;
        }

        .core {
          animation: none;
          opacity: 0.75;
        }
      }
    </style>
  </head>
  <body>
    <div
      class="sys"
      role="img"
      aria-label="A prompt enters a model and the completion streams back one token at a time, at an even cadence"
    >
      <div class="prompt">
        <b></b><b></b>
        <i class="ring"></i>
      </div>

      <div class="chan a">
        <i class="rail"></i>
        <i class="pkt"></i>
      </div>

      <div class="model">
        <i class="ring"></i>
        <div class="beat"><i class="core"></i></div>
      </div>

      <div class="chan b">
        <i class="rail"></i>
        <div class="flowgate"><i class="flow"></i><i class="flow"></i></div>
      </div>

      <div class="out">
        <div class="line l1">
          <div class="row ink">
            <span></span><span></span><span></span><span></span><span></span><span></span
            ><span></span><span></span><span></span><span></span><span></span><span></span>
          </div>
          <div class="row hot">
            <span></span><span></span><span></span><span></span><span></span><span></span
            ><span></span><span></span><span></span><span></span><span></span><span></span>
          </div>
        </div>
        <div class="line l2">
          <div class="row ink">
            <span></span><span></span><span></span><span></span><span></span><span></span
            ><span></span><span></span>
          </div>
          <div class="row hot">
            <span></span><span></span><span></span><span></span><span></span><span></span
            ><span></span><span></span>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

The variant family

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