# Orb de voz reagindo a áudio — a silhueta é invariante

Aprendida em 2026-09-17 corrigindo a reação à música da orb do Nash Workspace
(`src/renderer/orb/orb-shaders.js`, bloco `MUS_*`), depois de o dono ler a
primeira versão como "forçado, perdeu o shape de orbe e todo o significado".
Referências lidas no Chrome no mesmo dia: Dribbble `ai-voice-orb` e
`audio-reactive-sphere`, Pinterest `ai voice orb interface`.

- **O contorno não se move.** Em nenhuma das três buscas existe uma orb de voz
  que deforme o próprio círculo. O que se move é o interior (plasma, fita,
  redemoinho, partículas suspensas), a casca (anéis finos, iridescência) e o
  halo externo. Vale para vidro sólido e para nuvem de partículas igualmente.
- **A regra técnica que separa relevo de deformação é a isotropia.** Ruído ou
  senos cruzados em frequência espacial alta (7–9 ciclos sobre a direção da
  partícula) texturizam a casca sem mover a silhueta, porque a média por
  direção é a mesma em toda direção. Harmônico de ordem baixa — um quadrupolo
  `dir.x² − dir.z²`, por exemplo — faz o oposto: pouca textura e muita mudança
  de forma. Medido: quadrupolo a 0,34 do raio levou a irregularidade do
  contorno de 0,081 (parado) para 0,109; o relevo isotrópico a 0,055 a levou
  para 0,050, ou seja, mais redonda tocando do que parada.
- **O movimento grande é tangencial.** Um vórtice em torno do eixo vertical com
  as camadas internas girando mais rápido que a casca (cisalhamento
  proporcional a `1 − raio`) dá o plasma que o gênero tem, e rotação nenhuma
  altera o contorno de uma nuvem esférica — é onde a amplitude pode ser
  generosa de graça.
- **O ritmo mora na luz, não na geometria.** Batida como flash (brilho ×2,3),
  casca de choque como onda de brilho atravessando (×3,6) e um halo que acende
  só a casca externa (`smoothstep(0,72, 1,0, raio)`). Com deslocamento radial
  de apenas 0,05 o golpe continua lendo como golpe: brilho médio do quadro sobe
  75% acima do silêncio.
- **Fase acumulada, nunca `tempo × energia`.** Multiplicar o relógio pela
  energia dentro do shader faz o ângulo saltar toda vez que a energia muda; a
  fase tem de ser integrada por quadro (`fase += dt × energia`) no lado JS.
- **Deriva e giro ambiente engordam a nuvem.** Os dois deslocam partículas por
  ruído no espaço, então dose alta borra a silhueta pelo caminho de trás —
  medido, foi preciso baixar de 2,2× para 0,8× e de 1,4× para 0,5×.
- **Como medir isto, e não achismo:** esconder o HUD, recortar o centro do
  quadro, achar o centróide dos pixels acesos e tomar o raio (percentil 98) em
  360 direções. O desvio relativo desse raio é o número da "orbicidade"; o raio
  médio diz se a orb inchou.
- **Conota**: a orb é uma presença contida — o contorno estável é a identidade,
  a energia interna é a vida. Quando a marca se chama Nash, isso é literal:
  equilíbrio de Nash é o ponto em que ninguém tem incentivo para sair do lugar,
  então uma orb que muda de forma nega o que ela nomeia. Estável por fora e
  fervendo por dentro é o conceito; sacudida por fora é outro produto.

**Desfecho da mesma sessão (2026-09-17), e a lição que vale mais que a receita:**
a casa já tinha a reação pronta e documentada — a página `/design-system`,
seção Som, resume em uma frase ("o grave empurra para fora, a média respira, a
aguda acende") e `site/js/orb.js:310` e `:345` a escrevem em duas linhas:
`som = 0,14·grave·lo + 0,09·média·mi + 0,05·aguda·hi` e
`brilho *= 1 + 0,6·nível·(0,5+0,5·lo)`. Inventei duas versões antes de abrir
essa página. O dono aprovou uma única peça das duas (o halo que acende a casca
externa na batida) e mandou pegar o resto do kit. Medido depois da troca: a
receita do kit dá o brilho mais alto das três versões (45,2 contra 38,7 da
minha) e mantém o contorno redondo (0,0675), inflando mais (+19% de raio) —
porque "empurrar para fora" é escala radial uniforme, que muda o tamanho sem
mudar a forma. A regra acima sobre isotropia continua verdadeira e explica por
que o quadrupolo falhou; o que ela não me deu foi o direito de escrever uma
reação nova quando já havia uma aprovada. Ler o inventário não basta: a linha
"alimentar o orbe com áudio → `orbe.audio(bandas, nivel)`" estava lá o tempo
todo, e o que faltou foi abrir a peça que ela aponta.

**E o fecho, meia hora depois:** o dono voltou com "substitui pelo que tá no
ds, 100%" e o halo saiu também — com ele, o detector de batida inteiro, seu
uniform e um termo de áudio que eu tinha somado ao `gl_PointSize` (o kit não
mexe no tamanho do ponto com som). Brilho no pico caiu de 45,2 para 36,5, e a
diferença é exatamente o halo. **A lição tem duas camadas, e eu só tinha
aprendido a primeira:** "aproveitar a peça pronta" e "a peça pronta mais um
acréscimo meu" são entregas diferentes, e qual das duas é a pedida não é
julgamento de quem constrói. Uma aprovação parcial numa rodada — "gostei desta
parte" — não vira licença para a peça permanecer na rodada seguinte.
