Back to the library

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.

conceptattentionmatrixheatmapsweepeasing

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: 10.5px;
    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: 10px;
    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); }
  }

  /* ── reduced motion ────────────────────────────────────────
     No sweep, no flare. The base styles below are the settled
     state: the cells that were found significant stay lit, the
     rest stay at their resting weight. Same information, no travel. */
  @media (prefers-reduced-motion: reduce) {
    /* Selectors match the specificity of the rules that started the
       animations, otherwise `animation: none` loses and the page still moves. */
    .grid i::before,
    .grid i::after,
    .grid i.warm::before,
    .grid i.hit::before,
    .grid i.hit::after,
    .sweep, .band, .tally { animation: none; }
    .sweep { display: none; }
    .grid i.warm::before { opacity: 0.24; transform: scale(1); }
    .grid i.hit::before  { opacity: 1;    transform: scale(1); }
    .grid i.hit::after   { opacity: 1;    transform: scale(1); }
    .tally { opacity: 1; transform: none; }
  }
</style>
</head>
<body>
  <div class="card">
    <div class="head">
      <b>Salience sweep</b>
      <span>layer 7 &middot; 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>