Console warm-up ready
Variant: Single beat
A connection-status panel that shows a device coming online: a dial needle swings up to its resting mark while the three capsule lamps beside it latch on and a hairline sweep crosses the readout. Reach for it as an ambient status indicator for hardware pairing, a service booting, or any "waiting for the far end" state where a spinner would read as generic. This primary tuning is one tight beat — needle, lamps and sweep all resolve inside the first 40% of the loop, then the panel simply holds lit, so a glance at any random moment most often finds the settled state rather than the motion.
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>Console warm-up ready — single beat</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
(#d97757 / #b85c3e / #788c5d / #b04a3f). */
--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));
/* Clock — every duration in the document runs off --t. */
--cycle: 3.2s; /* the authored master loop */
--t: calc(var(--cycle) / var(--speed));
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
/* File-specific: lamp latch delays, one clock. */
--_lamp-1: calc(0.12s / var(--speed));
--_lamp-2: calc(0.18s / var(--speed));
--_lamp-3: calc(0.24s / var(--speed));
--_chrome: #cfd2d4;
}
* { box-sizing: border-box; }
html, body { margin: 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);
}
.console {
width: min(360px, 90vw);
padding: clamp(14px, 4.4vw, 20px);
background: var(--paper);
border: 1px solid var(--oat);
border-radius: calc(14px * var(--round));
box-shadow: 0 1px 0 var(--oat) inset;
display: grid;
gap: clamp(12px, 3.6vw, 16px);
}
.head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.label {
font-size: clamp(11px, 3vw, 12px);
letter-spacing: 0.16em;
text-transform: uppercase;
font-weight: 600;
color: var(--slate);
}
.lamps { display: flex; gap: 6px; }
.lamp {
width: 20px;
height: 7px;
border-radius: 999px; /* capsule — stays literal */
background: var(--oat);
opacity: 0.55;
transform-origin: left center;
animation: lamp-latch var(--t) var(--ease-out) infinite;
}
.lamp:nth-child(1) { animation-delay: var(--_lamp-1); }
.lamp:nth-child(2) { animation-delay: var(--_lamp-2); }
.lamp:nth-child(3) { animation-delay: var(--_lamp-3); }
@keyframes lamp-latch {
0% { opacity: 0.35; background: var(--oat); transform: scaleX(0.92); }
7%, 82% { opacity: 1; background: var(--clay); transform: scaleX(1); }
100% { opacity: 0.35; background: var(--oat); transform: scaleX(0.92); }
}
.face {
position: relative;
border-radius: calc(10px * var(--round));
background: linear-gradient(180deg, var(--ivory), var(--paper));
border: 1px solid var(--oat);
padding: clamp(10px, 3vw, 14px);
overflow: hidden;
}
svg { display: block; width: 100%; height: auto; }
.arc { stroke: var(--oat); }
.arc-live {
stroke: var(--clay);
stroke-dasharray: 150;
stroke-dashoffset: 150;
animation: arc-fill var(--t) var(--ease-out) infinite;
}
@keyframes arc-fill {
0% { stroke-dashoffset: 150; }
34% { stroke-dashoffset: 42; }
86% { stroke-dashoffset: 42; }
100% { stroke-dashoffset: 150; }
}
/* Needle swings from rest to mark, then holds: state indication, not decoration. */
.needle {
transform-origin: 110px 78px;
animation: needle-swing var(--t) var(--ease-in-out) infinite;
}
@keyframes needle-swing {
0% { transform: rotate(-58deg); }
34% { transform: rotate(34deg); }
86% { transform: rotate(34deg); }
100% { transform: rotate(-58deg); }
}
.pip { fill: var(--oat); }
.hub { fill: var(--slate); }
/* Chrome sweep: a single linear pass across the glass, no loop of its own. */
.sweep {
position: absolute;
inset: 0;
background: linear-gradient(105deg, transparent 38%, var(--_chrome) 50%, transparent 62%);
opacity: 0.5;
transform: translateX(-120%);
animation: sweep var(--t) linear infinite;
pointer-events: none;
}
@keyframes sweep {
0%, 12% { transform: translateX(-120%); }
40% { transform: translateX(120%); }
100% { transform: translateX(120%); }
}
.foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
font-size: clamp(10.5px, 2.9vw, 11.5px);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--clay-d);
}
.foot .right { color: var(--olive); }
.bar {
height: 4px;
border-radius: 999px;
background: var(--oat);
overflow: hidden;
}
.bar i {
display: block;
height: 100%;
border-radius: 999px;
background: var(--rust);
transform-origin: left center;
transform: scaleX(0.06);
animation: bar-fill var(--t) var(--ease-out) infinite;
}
@keyframes bar-fill {
0% { transform: scaleX(0.06); }
34% { transform: scaleX(1); }
86% { transform: scaleX(1); }
100% { transform: scaleX(0.06); }
}
@media (max-width: 380px) {
.console { padding: 13px; gap: 11px; }
.face { padding: 9px; }
.lamp { width: 17px; }
}
@media (prefers-reduced-motion: reduce) {
/* Keep one slow lit/unlit cycle so the panel still reads as live. */
.needle, .sweep, .bar i, .arc-live { animation: none; }
.needle { transform: rotate(34deg); }
.bar i { transform: scaleX(1); }
.arc-live { stroke-dashoffset: 42; }
.sweep { display: none; }
.lamp {
transform: none;
animation: lamp-dim calc(var(--t) * 2.4) ease-in-out infinite;
}
.lamp:nth-child(1) { animation-delay: var(--_lamp-1); }
.lamp:nth-child(2) { animation-delay: var(--_lamp-2); }
.lamp:nth-child(3) { animation-delay: var(--_lamp-3); }
@keyframes lamp-dim {
0%, 100% { opacity: 0.6; background: var(--clay); }
50% { opacity: 1; background: var(--clay); }
}
}
</style>
</head>
<body>
<div class="console" role="img" aria-label="Device warming up to ready state">
<div class="head">
<span class="label">Link status</span>
<div class="lamps">
<span class="lamp"></span>
<span class="lamp"></span>
<span class="lamp"></span>
</div>
</div>
<div class="face">
<svg viewBox="0 0 220 96" aria-hidden="true">
<path class="arc" d="M 42 78 A 68 68 0 0 1 178 78" fill="none" stroke-width="6" stroke-linecap="round" />
<path class="arc-live" d="M 42 78 A 68 68 0 0 1 178 78" fill="none" stroke-width="6" stroke-linecap="round" />
<g class="pip">
<rect x="54" y="44" width="2.5" height="8" rx="1.2" transform="rotate(-52 55 48)" />
<rect x="108.5" y="22" width="2.5" height="9" rx="1.2" />
<rect x="163" y="44" width="2.5" height="8" rx="1.2" transform="rotate(52 164 48)" />
</g>
<g class="needle">
<rect x="108.6" y="30" width="2.8" height="50" rx="1.4" fill="currentColor" />
</g>
<circle class="hub" cx="110" cy="78" r="5" />
</svg>
<span class="sweep"></span>
</div>
<div class="bar"><i></i></div>
<div class="foot">
<span>Warming</span>
<span class="right">Ready</span>
</div>
</div>
</body>
</html>
The variant family
Same idea, one axis moved. Compare them side by side before you commit.