Back to the library

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.

statusloaderdialpanelambient

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>