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" />
<title>Token stream, steady</title>
<style>
:root {
--ivory: #faf9f5;
--slate: #141413;
--clay: #d97757;
--oat: #e3dacc;
--text: rgba(20, 20, 19, 0.34);
/* 4.2s loop and a 175ms token period: the period divides the loop
exactly 24 times, so the metronome never drifts against the restart. */
--loop: 4.2s;
--tok: 175ms;
}
* {
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: 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(--loop) 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(--loop) 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: 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;
}
/* 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(--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;
}
}
@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.