feat: replace orb avatar with a livekit-style LED wall driven by agent audio
Rebuild & Deploy / rebuild (push) Successful in 37s

- Swap the CSS orb for a 21x21 LED dot matrix on a glass panel, matching
  the livekit.com LED-array hero visual.
- Pulse driven by the agent's rendered audio: an expanding radial bloom
  brightens and widens with remote-audio level, dimmer when listening on
  the mic, faint static matrix when idle, and a subtle warm breathing
  state while thinking.
- State-tinted palettes: cool azure when speaking, blue when listening,
  amber when thinking, dim slate when idle.
- Drop the unused orb CSS-variable pulse plumbing and the presence-breathe
  / orbit-turn keyframes; the wall renders per-frame from the analyser.
This commit is contained in:
snowspeeder committed 2026-08-29 17:05:57 -04:00
1 parent e2626ba54e
commit 68f4c461c8
3 files changed
+148 -157

No files matched your search

+60 -140
View File
@@ -327,149 +327,68 @@ button {
padding-top: 0.75rem;
}
.orb {
--pulse-scale: 1;
--pulse-opacity: 0.28;
/* ── LED wall avatar ────────────────────────────────────────────────────── */
.led-wall {
position: relative;
width: 260px;
height: 260px;
display: grid;
place-items: center;
isolation: isolate;
}
.orb-core {
position: relative;
z-index: 3;
width: 116px;
height: 116px;
overflow: hidden;
border-radius: 50%;
background:
radial-gradient(circle at 32% 24%, rgba(244, 247, 251, 0.96) 0 3%, transparent 20%),
radial-gradient(circle at 38% 30%, #b9c9ff, #6f8ff7 42%, #3d57ab 72%, #23305b 100%);
box-shadow:
0 0 0 1px rgba(213, 222, 255, 0.24),
0 12px 28px rgba(2, 8, 20, 0.34),
0 0 52px rgba(111, 143, 247, 0.32),
inset -12px -14px 26px rgba(10, 20, 50, 0.34);
transform: scale(var(--pulse-scale));
transition: filter 220ms ease, box-shadow 220ms ease, transform 90ms linear;
}
.orb-shimmer {
position: absolute;
width: 90px;
height: 34px;
left: 9px;
top: 18px;
border-radius: 50%;
border-top: 1px solid rgba(255, 255, 255, 0.52);
transform: rotate(-22deg);
opacity: 0.7;
}
.orb-ring,
.orb-orbit {
position: absolute;
border-radius: 50%;
pointer-events: none;
}
.orb-ring {
z-index: 2;
width: 164px;
height: 164px;
border: 1px solid rgba(139, 167, 255, 0.2);
opacity: var(--pulse-opacity);
transform: scale(var(--pulse-scale));
transition: opacity 180ms ease, transform 90ms linear;
}
.orb-orbit {
z-index: 1;
border: 1px solid rgba(139, 167, 255, 0.15);
width: 320px;
height: 320px;
max-width: 100%;
display: grid;
grid-template-columns: repeat(21, 1fr);
grid-template-rows: repeat(21, 1fr);
gap: 2px;
place-items: center;
padding: 12px;
border-radius: 26px;
isolation: isolate;
transition: filter 220ms ease;
}
.orbit-one {
width: 214px;
height: 140px;
transform: rotate(-18deg);
}
.orbit-one::after {
/* A soft glass panel that holds the matrix up off the page. */
.led-wall::before {
content: "";
position: absolute;
width: 7px;
height: 7px;
top: 12px;
right: 37px;
border-radius: 50%;
background: var(--warm);
box-shadow: 0 0 13px rgba(255, 179, 142, 0.65);
}
.orbit-two {
width: 230px;
height: 230px;
border-color: rgba(139, 167, 255, 0.08);
}
.orb[data-state="idle"] .orb-core {
filter: saturate(0.72) brightness(0.78);
box-shadow:
0 0 0 1px rgba(213, 222, 255, 0.13),
0 12px 28px rgba(2, 8, 20, 0.3),
0 0 28px rgba(111, 143, 247, 0.16);
}
.orb[data-state="connecting"] .orbit-one,
.orb[data-state="thinking"] .orbit-one {
animation: orbit-turn 3.5s linear infinite;
}
.orb[data-state="connecting"] .orb-core {
animation: presence-breathe 1.7s ease-in-out infinite;
}
.orb[data-state="listening"] .orb-core {
box-shadow:
0 0 0 1px rgba(213, 222, 255, 0.4),
0 12px 28px rgba(2, 8, 20, 0.34),
0 0 68px rgba(111, 143, 247, 0.56),
inset -12px -14px 26px rgba(10, 20, 50, 0.28);
}
.orb[data-state="listening"] .orb-ring {
border-color: rgba(139, 167, 255, 0.5);
}
.orb[data-state="thinking"] .orb-core {
inset: 0;
border-radius: 26px;
border: 1px solid rgba(139, 167, 255, 0.14);
background:
radial-gradient(circle at 32% 24%, rgba(255, 255, 255, 0.9) 0 3%, transparent 20%),
radial-gradient(circle at 38% 30%, #f3c9b5, #a783ca 42%, #5d68bd 72%, #29365f 100%);
box-shadow: 0 0 62px rgba(167, 131, 202, 0.48);
radial-gradient(circle at 50% 50%, rgba(111, 143, 247, 0.1), transparent 62%),
rgba(6, 12, 24, 0.4);
z-index: -1;
}
.orb[data-state="speaking"] .orb-core {
box-shadow:
0 0 0 1px rgba(213, 222, 255, 0.45),
0 12px 28px rgba(2, 8, 20, 0.34),
0 0 78px rgba(111, 143, 247, 0.64),
0 0 105px rgba(255, 179, 142, 0.11);
.led-dot {
--sz: 5px;
width: var(--sz);
height: var(--sz);
min-width: 3px;
min-height: 3px;
border: 0;
border-radius: 50%;
background: currentColor;
opacity: 0.16;
transition: opacity 90ms linear, width 90ms linear, height 90ms linear;
}
.orb[data-state="speaking"] .orbit-one::after {
background: #ffd0b8;
/* Palette shifts by state — a cool blue while it listens, warm toward the
center as the agent speaks, amber while thinking, dim when idle. */
.led-wall[data-state="idle"] {
color: #56739e;
}
@keyframes presence-breathe {
0%, 100% { transform: scale(calc(var(--pulse-scale) * 0.98)); }
50% { transform: scale(calc(var(--pulse-scale) * 1.045)); }
.led-wall[data-state="connecting"],
.led-wall[data-state="thinking"] {
color: #d99a6c;
}
@keyframes orbit-turn {
to { transform: rotate(342deg); }
.led-wall[data-state="listening"] {
color: #6f8ff7;
}
.led-wall[data-state="speaking"] {
color: #9dc2ff;
filter: drop-shadow(0 0 14px rgba(111, 143, 247, 0.35));
}
.orb-labels {
@@ -964,10 +883,9 @@ body[data-mode="text"] .orb-stage {
min-height: 220px;
}
body[data-mode="text"] .orb {
width: 180px;
height: 180px;
transform: scale(0.72);
body[data-mode="text"] .led-wall {
width: 260px;
height: 260px;
}
body[data-mode="text"] .controls {
@@ -1548,8 +1466,9 @@ body[data-mode="text"] .mic-meter {
margin: -1rem 0;
}
.orb {
transform: scale(0.78);
.led-wall {
width: 260px;
height: 260px;
}
.connection-strip,
@@ -1628,8 +1547,9 @@ body[data-mode="text"] .mic-meter {
margin: -0.75rem 0;
}
body[data-mode="text"] .orb {
transform: scale(0.48);
body[data-mode="text"] .led-wall {
width: 210px;
height: 210px;
}
.console-intro h2 {
@@ -1640,10 +1560,10 @@ body[data-mode="text"] .mic-meter {
min-height: 300px;
}
.orb {
width: 235px;
height: 235px;
transform: scale(0.94);
.led-wall {
width: 270px;
height: 270px;
padding: 9px;
}
.controls {