REFERENCE
- Gallery page: https://magpui.com/en/i/voice-ring-that-turns-toward-sound-905f84
- First frame (image): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/voice-ring-that-turns-toward-sound-905f84/shot.jpg
- Clip (mp4): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/voice-ring-that-turns-toward-sound-905f84/clip.mp4
- Original post: https://x.com/Jakubantalik/status/2117304731290771737 by @Jakubantalik
Before building, download the first frame and look at it. If ffmpeg is available, download the clip and extract frames (ffmpeg -i clip.mp4 -vf fps=2 frame-%02d.png), then study the motion frame by frame. Match what you see; the text below describes it, the frames are the truth.
BUILD
A voice-mode component for an AI assistant: a soft, glowing, slightly wobbly ring that lives above a chat input and reacts to the user's voice. The ring breathes with the audio level, its blob leans and drifts toward the side the sound comes from (left, front, right), and its gradient shifts with the detected mood (calm blue/violet, happy lime/green, excited pink/orange). Under it, the latest transcribed sentence fades in. The same ring also exists in two small forms: an inline 'Thinking...' chip and a cursor-sized orb that floats over streamed text while the agent works. It should feel alive, warm and precise, like a premium native app, never like an audio visualizer.
DESIGN SYSTEM
- Surface: near-black page #0B0B0C, card #161617 with radius 28px, no border, very soft shadow 0 30px 80px rgba(0,0,0,0.5). Card about 340x320px, ring centered in the top 60%.
- Ring: about 120px diameter, 6-8px stroke, drawn as a closed noisy loop (not a perfect circle), conic gradient through #FF3DA5, #7B5CFF, #2EC5FF, #8CFF5A, #FFC24A with an outer glow (two blurred copies at 12px and 28px, 60% and 30% opacity, additive blending).
- Text: clean grotesk sans, transcript 13px/18px #E8E8EA centered, max 2 lines; labels 11px #8A8A8F; debug HUD (optional) in 10px monospace uppercase #6C6C72 with letter-spacing 0.08em.
- Composer: pill input 44px tall, radius 22px, fill #232325, placeholder 'Ask anything' #77777C; round icon buttons 36px (#2A2A2C) for mic and close.
- Thinking chip: pill 40px tall, fill #1A1A1B, 20px mini ring on the left, label 'Thinking...' 15px with a left-to-right shimmer.
MOTION
- Audio level: map RMS (0-1) to ring scale 1 -> 1.12 and stroke 6 -> 9px with a spring (stiffness 220, damping 18); the noise amplitude of the loop rises from 2px to 8px while speaking. Idle breathing: scale 0.98 <-> 1.02, 3200ms ease-in-out loop.
- Direction: a direction value from -1 (left) to 1 (right) offsets the ring center up to 14px and skews the noise so the loop bulges toward that side; spring transition 400ms cubic-bezier(0.22,1,0.36,1). A tiny dotted guide line with a pulsing dot can show the source side.
- Emotion: on a new label, rotate the gradient stops to that palette over 900ms cubic-bezier(0.4,0,0.2,1); the conic gradient itself rotates slowly (1 turn / 8s, linear).
- Transcript: each new sentence fades in with translateY 6px -> 0, opacity 0 -> 1, 260ms cubic-bezier(0.16,1,0.3,1); the old one fades out 160ms.
- Shrink to agent: on submit the ring scales to 20px and moves into the Thinking chip (shared layout animation, 500ms cubic-bezier(0.32,0.72,0,1)); while streaming, a 28px orb version follows the text caret with 120ms lag.
SECTIONS
Parts: card, ring canvas, transcript line, composer (input, mic, close), thinking chip, caret orb.
States: idle (breathing), listening (reacts to level + direction), emotion change, processing (Thinking chip), streaming (caret orb), muted (ring desaturated to #4A4A4F, no glow), error (ring flashes #FF5A5A once).
IMPLEMENTATION
- Draw the ring in a single <canvas> (2D with a conic gradient and shadowBlur, or a small WebGL fragment shader for smoother glow); generate the loop from 64 points with simplex noise offset by time, level and direction.
- Audio: Web Audio AnalyserNode for level. Direction and emotion come from props (direction: -1..1, emotion: string) so the component works with any model (on-device direction estimation, speech emotion recognition) or with mocked values; ship a demo that fakes them.
- React + Motion for the layout morph between ring, chip and caret orb (layoutId).
- prefers-reduced-motion: no wobble or drift, keep a static ring whose opacity follows the level, instant palette swaps.
- Accessibility: mic button with aria-pressed, live region for the transcript, visible focus ring 2px #FFFFFF at 40%.
- Performance: one canvas per ring, devicePixelRatio capped at 2, pause the loop when hidden.
ADAPT TO THE PROJECT
Reuse the project's existing design tokens, fonts and input components; map the neutral surfaces to the closest existing tokens and keep the iridescent palette as the only accent. Never copy logos, brand names or product copy from the original; use placeholder text.
SUCCESS CHECK
- Speaking louder visibly grows and thickens the ring, silence returns it to a calm breathing loop.
- Setting direction to -1 and then 1 makes the ring lean left and then right with a smooth spring, and changing emotion recolors it in under a second.
- Submitting morphs the ring into the Thinking chip without a jump, and reduced motion removes all wobble.