Salience Sweep
A crest of heat crosses an 18×10 attention matrix: every cell flares as the wavefront reaches it, most fall back to their resting weight, and twenty are found significant and stay lit with a rust seed mark. The sweep runs on cubic-bezier(0.77,0,0.175,1) and each cell's delay is that curve inverted at its column, so the crest hesitates at the left margin, rips through the middle, and decelerates out the right — the flare envelope is kept to 130ms up / 250ms down precisely so the accelerating middle reads as a widening crest instead of the whole grid igniting at once. Reach for it when you need to show selection, scoring, or attention over a field of data — anywhere a system looks at everything and keeps only some of it.
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>Salience Sweep</title>
<style>
:root {
--ivory: #FAF9F5;
--paper: #FFFFFF;
--slate: #141413;
--clay: #D97757;
--oat: #E3DACC;
--olive: #788C5D;
--rust: #B04A3F;
--gray-300:#D1CFC5;
--gray-500:#87867F;
--sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
/* Declared for reference. Inside @keyframes the timing functions are
written out literally — `animation-timing-function: var(--x)` is not
reliably honoured there, and silently falls back to `ease`. */
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
/* One shared clock. Every cell runs this same 9.6s loop; only its delay
differs, so flare, hold and dissolve all propagate in the same order. */
--cycle: 9600ms;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
min-height: 100vh;
display: grid;
place-items: center;
background: var(--ivory);
font-family: var(--sans);
color: var(--slate);
-webkit-font-smoothing: antialiased;
}
/* ─────────────────────────────────────────────────────────────
KEEP FROM HERE: .card and everything inside it, the @keyframes
blocks, the :root tokens, and the prefers-reduced-motion block.
The per-cell `--d` delays are baked into the markup — they are
the inverse of the sweep's easing curve sampled at each column,
which is what keeps the wavefront and the cell flares in lockstep.
───────────────────────────────────────────────────────────── */
.card {
width: min(92vw, 560px);
background: var(--paper);
border: 1px solid var(--gray-300);
border-radius: 12px;
padding: 22px 24px 18px;
}
.head, .foot {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 16px;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.11em;
text-transform: uppercase;
color: var(--gray-500);
}
.head { margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--oat); }
.head b { font-weight: 500; color: var(--slate); letter-spacing: 0.11em; }
.foot { margin-top: 14px; }
.plot { display: flex; align-items: stretch; gap: 10px; }
.ylab {
writing-mode: vertical-rl;
transform: rotate(180deg);
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--gray-500);
display: flex;
align-items: center;
justify-content: center;
border-left: 1px solid var(--olive);
padding-right: 2px;
}
.matrix {
position: relative;
flex: 1;
overflow: hidden;
padding: 3px;
}
.grid {
display: grid;
grid-template-columns: repeat(18, 1fr);
gap: 4px;
}
/* Each cell is three flat layers: a static baseline square whose alpha is
the resting weight, a heat layer, and a seed dot for the cells that hold.
Only opacity and transform ever animate. */
.grid i {
position: relative;
aspect-ratio: 1;
border-radius: 2px;
background: rgb(135 134 127 / var(--b));
}
.grid i::before,
.grid i::after {
content: "";
position: absolute;
border-radius: inherit;
will-change: transform, opacity;
}
/* heat layer — settled state is the base; keyframes return to it */
.grid i::before {
inset: 0;
background: var(--clay);
opacity: 0;
transform: scale(0.92);
animation: heat var(--cycle) linear var(--d) infinite both;
}
.grid i.warm::before { animation-name: heat-warm; }
.grid i.hit::before { animation-name: heat-hit; }
/* seed dot — the mark that says "this one was kept" */
.grid i::after {
inset: 50%;
width: 25%;
height: 25%;
margin: -12.5% 0 0 -12.5%;
border-radius: 50%;
background: var(--rust);
opacity: 0;
transform: scale(0.4);
}
.grid i.hit::after { animation: seed var(--cycle) linear var(--d) infinite both; }
/* ── the wavefront ──────────────────────────────────────────
A single band, 40% of the matrix wide, crossing on the strong
in-out curve: it hesitates at the left margin, rips through the
middle, and decelerates out the right. The cell delays above are
that curve inverted, so a cell flares exactly as the core reaches it. */
.sweep {
position: absolute;
inset: -4px 0;
width: 40%;
pointer-events: none;
transform: translateX(-50%);
animation: sweep-move var(--cycle) linear infinite both;
will-change: transform;
}
.band {
position: absolute;
inset: 0;
opacity: 0;
animation: sweep-fade var(--cycle) linear infinite both;
background: linear-gradient(90deg,
rgb(217 119 87 / 0) 0%,
rgb(217 119 87 / 0.05) 22%,
rgb(217 119 87 / 0.16) 42%,
rgb(217 119 87 / 0.26) 50%,
rgb(217 119 87 / 0.04) 58%,
rgb(217 119 87 / 0) 66%);
}
.band::after {
content: "";
position: absolute;
top: 0; bottom: 0; left: 50%;
width: 1px;
background: linear-gradient(180deg,
rgb(176 74 63 / 0), rgb(176 74 63 / 0.45) 26%, rgb(176 74 63 / 0.45) 74%, rgb(176 74 63 / 0));
}
.tally {
display: inline-flex;
align-items: center;
gap: 7px;
color: var(--slate);
opacity: 0;
transform: translateY(3px);
animation: tally var(--cycle) linear infinite both;
}
.tally em { width: 6px; height: 6px; border-radius: 50%; background: var(--rust); }
/* ── keyframes ─────────────────────────────────────────────
Percentages of the 9.6s cycle. The flare envelope is deliberately
short — 130ms up, 250ms down — because the sweep's easing bunches the
middle columns only ~40ms apart; a long envelope would light the whole
matrix at once instead of reading as a crest. Rise on ease-out so the
hit registers immediately, settle and dissolve on ease-in-out. */
@keyframes heat {
0% { opacity: 0; transform: scale(0.92); animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1); }
1.35% { opacity: 1; transform: scale(1.12); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
4% { opacity: 0; transform: scale(0.92); }
100% { opacity: 0; transform: scale(0.92); }
}
@keyframes heat-warm {
0% { opacity: 0; transform: scale(0.92); animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1); }
1.35% { opacity: 1; transform: scale(1.12); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
4% { opacity: 0.24; transform: scale(1); }
75% { opacity: 0.24; transform: scale(1); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
82% { opacity: 0; transform: scale(0.92); }
100% { opacity: 0; transform: scale(0.92); }
}
@keyframes heat-hit {
0% { opacity: 0; transform: scale(0.92); animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1); }
1.35% { opacity: 1; transform: scale(1.12); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
4% { opacity: 1; transform: scale(1); }
75% { opacity: 1; transform: scale(1); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
82% { opacity: 0; transform: scale(0.92); }
100% { opacity: 0; transform: scale(0.92); }
}
/* The seed lands a beat after the flare peak: the cell is judged, then marked. */
@keyframes seed {
0% { opacity: 0; transform: scale(0.4); }
1.35% { opacity: 0; transform: scale(0.4); animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1); }
5% { opacity: 1; transform: scale(1); }
75% { opacity: 1; transform: scale(1); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
81% { opacity: 0; transform: scale(0.4); }
100% { opacity: 0; transform: scale(0.4); }
}
@keyframes sweep-move {
0% { transform: translateX(-50%); }
1.25% { transform: translateX(-50%); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
36.67% { transform: translateX(200%); }
100% { transform: translateX(200%); }
}
@keyframes sweep-fade {
0% { opacity: 0; animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1); }
4% { opacity: 1; }
31% { opacity: 1; animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
36.67% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes tally {
0% { opacity: 0; transform: translateY(3px); }
38% { opacity: 0; transform: translateY(3px); animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1); }
44% { opacity: 1; transform: translateY(0); }
84% { opacity: 1; transform: translateY(0); animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1); }
89% { opacity: 0; transform: translateY(3px); }
100% { opacity: 0; transform: translateY(3px); }
}
/* Small stages: the two tracked labels in .head are wider than the card once
it drops under ~300px, so the tracking loosens off and the chrome gives
its padding back to the matrix. Type stays at 11px. */
@media (max-width: 380px) {
.card { padding: 16px 14px 14px; }
.plot { gap: 7px; }
.head, .foot { gap: 10px; letter-spacing: 0.05em; }
.head { margin-bottom: 11px; padding-bottom: 10px; }
.head b { letter-spacing: 0.05em; }
.foot { margin-top: 11px; }
.ylab { letter-spacing: 0.08em; }
}
/* ── reduced motion ────────────────────────────────────────
The settled state is still the truth — held cells lit, warm cells at
their lower weight, the tally standing — but it breathes instead of
freezing. Each cell keeps its own `--d`, so the same left-to-right
order the wavefront had still propagates, now as a slow swell of
opacity rather than a band travelling across the frame. No sweep
element, no flare envelope, no position change: the reduced loop is
5.2s (vs the 9.6s full cycle per-cell flare of ~380ms), it starts and
ends on the settled values so every reset is invisible, and the
excursions stay moderate (1 → 0.5, 0.24 → 0.5). */
@media (prefers-reduced-motion: reduce) {
/* Reduced-only clock. Long enough that the staggered `--d` reads as a
drift across the matrix rather than a flicker. */
:root { --cycle-reduced: 5200ms; }
/* Blanket stop, narrowed: only the elements that travelled (the sweep
band) and the cells that hold nothing are silenced here. The warm,
hit and tally rules below are equally specific and come later in
source order, so their animations win. */
.grid i::before,
.grid i::after,
.sweep, .band { animation: none; }
.sweep { display: none; }
@keyframes heat-warm-reduced {
0%, 100% { opacity: 0.24; }
45% { opacity: 0.5; }
}
@keyframes heat-hit-reduced {
0%, 100% { opacity: 1; }
45% { opacity: 0.5; }
}
/* The mark dims and shrinks a touch with its cell, then returns —
small scale only, because the dot's size is what says "kept". */
@keyframes seed-reduced {
0%, 100% { opacity: 1; transform: scale(1); }
45% { opacity: 0.55; transform: scale(0.86); }
}
@keyframes tally-reduced {
0%, 100% { opacity: 1; }
50% { opacity: 0.55; }
}
.grid i.warm::before {
opacity: 0.24; transform: scale(1);
animation: heat-warm-reduced var(--cycle-reduced) ease-in-out var(--d) infinite both;
}
.grid i.hit::before {
opacity: 1; transform: scale(1);
animation: heat-hit-reduced var(--cycle-reduced) ease-in-out var(--d) infinite both;
}
.grid i.hit::after {
opacity: 1; transform: scale(1);
animation: seed-reduced var(--cycle-reduced) ease-in-out var(--d) infinite both;
}
.tally {
opacity: 1; transform: none;
animation: tally-reduced var(--cycle-reduced) ease-in-out infinite both;
}
}
</style>
</head>
<body>
<div class="card">
<div class="head">
<b>Salience sweep</b>
<span>layer 7 · head 3</span>
</div>
<div class="plot">
<div class="ylab">query</div>
<div class="matrix">
<div class="grid">
<i class="" style="--d:857ms;--b:0.318"></i>
<i class="hit" style="--d:1181ms;--b:0.305"></i>
<i class="hit" style="--d:1401ms;--b:0.396"></i>
<i class="hit" style="--d:1517ms;--b:0.285"></i>
<i class="" style="--d:1612ms;--b:0.228"></i>
<i class="" style="--d:1668ms;--b:0.261"></i>
<i class="" style="--d:1739ms;--b:0.144"></i>
<i class="" style="--d:1780ms;--b:0.213"></i>
<i class="" style="--d:1815ms;--b:0.201"></i>
<i class="" style="--d:1859ms;--b:0.259"></i>
<i class="" style="--d:1891ms;--b:0.167"></i>
<i class="" style="--d:1933ms;--b:0.200"></i>
<i class="" style="--d:1971ms;--b:0.240"></i>
<i class="" style="--d:2076ms;--b:0.178"></i>
<i class="" style="--d:2138ms;--b:0.176"></i>
<i class="" style="--d:2306ms;--b:0.156"></i>
<i class="" style="--d:2485ms;--b:0.248"></i>
<i class="" style="--d:2867ms;--b:0.293"></i>
<i class="" style="--d:835ms;--b:0.212"></i>
<i class="warm" style="--d:1157ms;--b:0.370"></i>
<i class="warm" style="--d:1370ms;--b:0.322"></i>
<i class="hit" style="--d:1466ms;--b:0.319"></i>
<i class="hit" style="--d:1567ms;--b:0.284"></i>
<i class="hit" style="--d:1653ms;--b:0.351"></i>
<i class="" style="--d:1678ms;--b:0.185"></i>
<i class="" style="--d:1735ms;--b:0.272"></i>
<i class="" style="--d:1777ms;--b:0.211"></i>
<i class="" style="--d:1811ms;--b:0.226"></i>
<i class="" style="--d:1833ms;--b:0.135"></i>
<i class="" style="--d:1880ms;--b:0.257"></i>
<i class="" style="--d:1947ms;--b:0.230"></i>
<i class="" style="--d:2035ms;--b:0.217"></i>
<i class="" style="--d:2107ms;--b:0.295"></i>
<i class="" style="--d:2258ms;--b:0.247"></i>
<i class="" style="--d:2483ms;--b:0.227"></i>
<i class="" style="--d:2838ms;--b:0.148"></i>
<i class="" style="--d:817ms;--b:0.131"></i>
<i class="warm" style="--d:1130ms;--b:0.306"></i>
<i class="" style="--d:1331ms;--b:0.197"></i>
<i class="" style="--d:1480ms;--b:0.164"></i>
<i class="warm" style="--d:1535ms;--b:0.330"></i>
<i class="hit" style="--d:1620ms;--b:0.368"></i>
<i class="hit" style="--d:1660ms;--b:0.394"></i>
<i class="" style="--d:1716ms;--b:0.184"></i>
<i class="" style="--d:1751ms;--b:0.205"></i>
<i class="" style="--d:1801ms;--b:0.225"></i>
<i class="" style="--d:1838ms;--b:0.110"></i>
<i class="" style="--d:1876ms;--b:0.245"></i>
<i class="" style="--d:1923ms;--b:0.293"></i>
<i class="" style="--d:2013ms;--b:0.219"></i>
<i class="" style="--d:2087ms;--b:0.295"></i>
<i class="" style="--d:2242ms;--b:0.236"></i>
<i class="" style="--d:2430ms;--b:0.188"></i>
<i class="" style="--d:2830ms;--b:0.152"></i>
<i class="" style="--d:802ms;--b:0.276"></i>
<i class="warm" style="--d:1123ms;--b:0.272"></i>
<i class="" style="--d:1306ms;--b:0.171"></i>
<i class="" style="--d:1447ms;--b:0.163"></i>
<i class="" style="--d:1534ms;--b:0.184"></i>
<i class="warm" style="--d:1620ms;--b:0.369"></i>
<i class="hit" style="--d:1657ms;--b:0.292"></i>
<i class="hit" style="--d:1709ms;--b:0.403"></i>
<i class="warm" style="--d:1724ms;--b:0.306"></i>
<i class="" style="--d:1791ms;--b:0.274"></i>
<i class="" style="--d:1824ms;--b:0.236"></i>
<i class="" style="--d:1874ms;--b:0.198"></i>
<i class="" style="--d:1940ms;--b:0.172"></i>
<i class="" style="--d:2002ms;--b:0.224"></i>
<i class="" style="--d:2077ms;--b:0.184"></i>
<i class="" style="--d:2230ms;--b:0.185"></i>
<i class="" style="--d:2455ms;--b:0.197"></i>
<i class="" style="--d:2834ms;--b:0.224"></i>
<i class="" style="--d:792ms;--b:0.250"></i>
<i class="warm" style="--d:1108ms;--b:0.362"></i>
<i class="" style="--d:1303ms;--b:0.230"></i>
<i class="" style="--d:1451ms;--b:0.217"></i>
<i class="" style="--d:1520ms;--b:0.178"></i>
<i class="warm" style="--d:1605ms;--b:0.346"></i>
<i class="" style="--d:1666ms;--b:0.225"></i>
<i class="warm" style="--d:1709ms;--b:0.373"></i>
<i class="hit" style="--d:1724ms;--b:0.298"></i>
<i class="hit" style="--d:1789ms;--b:0.357"></i>
<i class="" style="--d:1828ms;--b:0.369"></i>
<i class="" style="--d:1880ms;--b:0.211"></i>
<i class="" style="--d:1894ms;--b:0.180"></i>
<i class="" style="--d:1968ms;--b:0.217"></i>
<i class="" style="--d:2097ms;--b:0.194"></i>
<i class="" style="--d:2242ms;--b:0.246"></i>
<i class="" style="--d:2439ms;--b:0.184"></i>
<i class="" style="--d:2796ms;--b:0.167"></i>
<i class="" style="--d:785ms;--b:0.127"></i>
<i class="warm" style="--d:1129ms;--b:0.383"></i>
<i class="" style="--d:1308ms;--b:0.271"></i>
<i class="" style="--d:1453ms;--b:0.161"></i>
<i class="" style="--d:1550ms;--b:0.161"></i>
<i class="" style="--d:1602ms;--b:0.320"></i>
<i class="" style="--d:1635ms;--b:0.202"></i>
<i class="" style="--d:1706ms;--b:0.181"></i>
<i class="" style="--d:1733ms;--b:0.322"></i>
<i class="warm" style="--d:1758ms;--b:0.285"></i>
<i class="hit" style="--d:1829ms;--b:0.310"></i>
<i class="warm" style="--d:1841ms;--b:0.395"></i>
<i class="" style="--d:1926ms;--b:0.196"></i>
<i class="" style="--d:1970ms;--b:0.235"></i>
<i class="" style="--d:2090ms;--b:0.156"></i>
<i class="" style="--d:2218ms;--b:0.238"></i>
<i class="" style="--d:2423ms;--b:0.158"></i>
<i class="" style="--d:2809ms;--b:0.131"></i>
<i class="" style="--d:792ms;--b:0.253"></i>
<i class="warm" style="--d:1139ms;--b:0.269"></i>
<i class="" style="--d:1316ms;--b:0.280"></i>
<i class="" style="--d:1431ms;--b:0.200"></i>
<i class="" style="--d:1520ms;--b:0.262"></i>
<i class="warm" style="--d:1620ms;--b:0.343"></i>
<i class="" style="--d:1640ms;--b:0.270"></i>
<i class="" style="--d:1701ms;--b:0.202"></i>
<i class="" style="--d:1738ms;--b:0.211"></i>
<i class="" style="--d:1778ms;--b:0.220"></i>
<i class="warm" style="--d:1808ms;--b:0.305"></i>
<i class="hit" style="--d:1859ms;--b:0.328"></i>
<i class="hit" style="--d:1935ms;--b:0.276"></i>
<i class="" style="--d:1975ms;--b:0.305"></i>
<i class="" style="--d:2108ms;--b:0.156"></i>
<i class="" style="--d:2210ms;--b:0.256"></i>
<i class="" style="--d:2432ms;--b:0.267"></i>
<i class="" style="--d:2819ms;--b:0.144"></i>
<i class="" style="--d:790ms;--b:0.268"></i>
<i class="warm" style="--d:1166ms;--b:0.344"></i>
<i class="" style="--d:1353ms;--b:0.140"></i>
<i class="" style="--d:1461ms;--b:0.205"></i>
<i class="" style="--d:1565ms;--b:0.140"></i>
<i class="" style="--d:1639ms;--b:0.345"></i>
<i class="" style="--d:1668ms;--b:0.228"></i>
<i class="" style="--d:1699ms;--b:0.238"></i>
<i class="" style="--d:1766ms;--b:0.172"></i>
<i class="" style="--d:1809ms;--b:0.152"></i>
<i class="" style="--d:1823ms;--b:0.145"></i>
<i class="" style="--d:1869ms;--b:0.266"></i>
<i class="hit" style="--d:1923ms;--b:0.318"></i>
<i class="hit" style="--d:1987ms;--b:0.350"></i>
<i class="warm" style="--d:2109ms;--b:0.382"></i>
<i class="" style="--d:2256ms;--b:0.213"></i>
<i class="" style="--d:2444ms;--b:0.213"></i>
<i class="" style="--d:2839ms;--b:0.256"></i>
<i class="" style="--d:799ms;--b:0.243"></i>
<i class="warm" style="--d:1188ms;--b:0.359"></i>
<i class="" style="--d:1338ms;--b:0.168"></i>
<i class="" style="--d:1486ms;--b:0.186"></i>
<i class="" style="--d:1571ms;--b:0.197"></i>
<i class="warm" style="--d:1629ms;--b:0.380"></i>
<i class="" style="--d:1684ms;--b:0.170"></i>
<i class="" style="--d:1727ms;--b:0.144"></i>
<i class="" style="--d:1767ms;--b:0.140"></i>
<i class="" style="--d:1791ms;--b:0.150"></i>
<i class="" style="--d:1860ms;--b:0.227"></i>
<i class="" style="--d:1890ms;--b:0.274"></i>
<i class="" style="--d:1948ms;--b:0.257"></i>
<i class="warm" style="--d:2039ms;--b:0.288"></i>
<i class="hit" style="--d:2124ms;--b:0.260"></i>
<i class="hit" style="--d:2243ms;--b:0.385"></i>
<i class="warm" style="--d:2454ms;--b:0.356"></i>
<i class="" style="--d:2869ms;--b:0.216"></i>
<i class="" style="--d:832ms;--b:0.168"></i>
<i class="warm" style="--d:1204ms;--b:0.373"></i>
<i class="" style="--d:1383ms;--b:0.248"></i>
<i class="" style="--d:1495ms;--b:0.210"></i>
<i class="" style="--d:1583ms;--b:0.136"></i>
<i class="warm" style="--d:1648ms;--b:0.390"></i>
<i class="" style="--d:1728ms;--b:0.230"></i>
<i class="" style="--d:1756ms;--b:0.241"></i>
<i class="" style="--d:1810ms;--b:0.201"></i>
<i class="" style="--d:1836ms;--b:0.149"></i>
<i class="" style="--d:1870ms;--b:0.140"></i>
<i class="" style="--d:1927ms;--b:0.165"></i>
<i class="" style="--d:1995ms;--b:0.179"></i>
<i class="" style="--d:2051ms;--b:0.272"></i>
<i class="" style="--d:2137ms;--b:0.274"></i>
<i class="warm" style="--d:2310ms;--b:0.251"></i>
<i class="hit" style="--d:2493ms;--b:0.346"></i>
<i class="warm" style="--d:2902ms;--b:0.275"></i>
</div>
<div class="sweep"><div class="band"></div></div>
</div>
</div>
<div class="foot">
<span>key position</span>
<span class="tally"><em></em>20 of 180 held</span>
</div>
</div>
</body>
</html>