Back to the library

Token stream, bursty

Variant: Bursty

The same model on the same 175ms beat, but delivery runs through a four-slot buffer: tokens queue, the buffer flushes, four words land at once. Production never pauses — the queue filling underneath is the proof — so the stall you feel is transport, not the model. Use it to explain chunked transfer, batched SSE flushes, or why a proxy makes a fast model feel jerky.

diagramllminfrastructure

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>Token stream, bursty</title>
    <style>
      :root {
        --ivory: #faf9f5;
        --slate: #141413;
        --clay: #d97757;
        --oat: #e3dacc;
        --text: rgba(20, 20, 19, 0.34);
        /* Same 4.2s loop and same 175ms token period as the steady variant —
           production is identical. Four tokens per chunk, so the buffer cycles
           every 700ms and fits the loop exactly six times. */
        --loop: 4.2s;
        --tok: 175ms;
        --chunk: 700ms;
      }

      * {
        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: 7px;
        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: 7px;
        animation: sent var(--loop) 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: 26px;
      }

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

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

      /* --- buffer ------------------------------------------------------ */

      .bufgate {
        position: absolute;
        inset: 0;
        opacity: 0;
        animation: gate var(--loop) steps(1, end) infinite;
      }

      /* Four slots at 17px rather than 11px: this is the element that explains
         the whole variant, so it has to survive being shrunk to a card. */
      .slots,
      .fill {
        position: absolute;
        top: 3px;
        left: 50%;
        width: 72px;
        height: 6px;
        margin-left: -36px;
        background: repeating-linear-gradient(90deg, currentColor 0 17px, transparent 17px 18px);
      }

      .slots {
        color: var(--oat);
      }

      /* Four slots fill on the same 175ms beat the steady variant emits on —
         the queue is what clumps, not the model. */
      .fill {
        color: var(--clay);
        animation: buffer var(--chunk) infinite;
      }

      .flush {
        position: absolute;
        top: 12px;
        left: 50%;
        width: 7px;
        height: 7px;
        margin-left: -3.5px;
        border-radius: 50%;
        background: var(--clay);
        opacity: 0;
        animation: deliver var(--chunk) infinite;
      }

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

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

      .model .ring {
        border-radius: 9px;
        animation: live var(--loop) infinite;
      }

      .beat {
        position: absolute;
        inset: 0;
        display: grid;
        place-items: center;
        opacity: 0;
        animation: gate var(--loop) 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(--loop) 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;
      }

      .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(--loop) infinite;
      }
      .l1 .hot {
        animation: hot-a var(--loop) infinite;
      }
      .l2 .ink {
        animation: ink-b var(--loop) infinite;
      }
      .l2 .hot {
        animation: hot-b var(--loop) 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;
        }
      }

      /* Discrete steps, not a slide: a queue holds whole tokens. It empties the
         instant the chunk leaves, then starts refilling — production never stops. */
      @keyframes buffer {
        0% {
          clip-path: inset(0 75% 0 0);
          animation-timing-function: steps(1, end);
        }
        25% {
          clip-path: inset(0 50% 0 0);
          animation-timing-function: steps(1, end);
        }
        50% {
          clip-path: inset(0 25% 0 0);
          animation-timing-function: steps(1, end);
        }
        75% {
          clip-path: inset(0 0 0 0);
          animation-timing-function: steps(1, end);
        }
        76%,
        100% {
          clip-path: inset(0 100% 0 0);
        }
      }

      @keyframes deliver {
        0%,
        75% {
          opacity: 0;
          transform: translateY(0);
        }
        76% {
          opacity: 1;
          transform: translateY(0);
          animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
        }
        90% {
          opacity: 1;
          transform: translateY(12px);
        }
        90.5%,
        100% {
          opacity: 0;
          transform: translateY(12px);
        }
      }

      /* Two steps across the first line instead of eight: four tokens land
         together, then nothing for 700ms. Same total, worse to read. */
      @keyframes ink-a {
        0%,
        31.667% {
          clip-path: inset(0 288px 0 0);
          animation-timing-function: steps(2, end);
        }
        65%,
        100% {
          clip-path: inset(0 96px 0 0);
        }
      }

      @keyframes hot-a {
        0% {
          clip-path: inset(0 192px 0 0);
          opacity: 0;
          animation-timing-function: steps(1, end);
        }
        31.667% {
          clip-path: inset(0 192px 0 0);
          opacity: 1;
          animation-timing-function: steps(2, end);
        }
        65%,
        100% {
          clip-path: inset(0 0 0 192px);
          opacity: 1;
        }
      }

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

      @keyframes hot-b {
        0% {
          clip-path: inset(0 96px 0 0);
          opacity: 0;
          animation-timing-function: steps(1, end);
        }
        65% {
          clip-path: inset(0 96px 0 0);
          opacity: 1;
          animation-timing-function: steps(1, end);
        }
        81.667%,
        100% {
          clip-path: inset(0 0 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,
        .flush {
          animation: none;
          opacity: 0;
        }

        .model {
          animation: none;
        }

        .core {
          animation: none;
          opacity: 0.75;
        }

        /* The buffer is what makes this variant different from the steady one,
           so it stays — held full, which is the state a bursty consumer sits in
           between flushes. Nothing about it travels. */
        .bufgate {
          animation: none;
          opacity: 1;
        }

        .fill {
          animation: none;
          clip-path: inset(0 0 0 0);
        }
      }
    </style>
  </head>
  <body>
    <div
      class="sys"
      role="img"
      aria-label="A prompt enters a model; tokens queue in a four-slot buffer and are delivered to the completion four at a time"
    >
      <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="bufgate">
          <i class="slots"></i>
          <i class="fill"></i>
          <i class="flush"></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.