VoiceBeam
An audio-reactive glow along the bottom edge of the element it wraps.
Usage
Basic
Wrap exactly one element that carries its own radius; feed it a microphone stream, or drive it with level.
Loading demo...
Microphone Input
useMicrophone() requests getUserMedia with echo cancellation, noise suppression, and auto gain off; call start() from a click.
<script setup lang="ts">
import { useMicrophone } from '@talex-touch/tuffex/pro'
const mic = useMicrophone()
const live = computed(() => mic.state.value === 'live')
</script>
<template>
<TxVoiceBeam :stream="mic.stream.value" :processing="transcribing">
<TxCard :radius="16" :padding="24" shadow="none">Ask anything…</TxCard>
</TxVoiceBeam>
<TxButton :aria-pressed="live" @click="live ? mic.stop() : mic.start()">
{{ live ? 'Stop' : 'Listen' }}
</TxButton>
</template>
Host Presets and Palettes
type picks the host preset, and any geometry prop you pass wins over it; colorVariant picks the palette.
<template>
<TxVoiceBeam type="pill" color-variant="ocean" :scale="0.9">
<div class="pill">Recording…</div>
</TxVoiceBeam>
<TxVoiceBeam type="mobile" color-variant="candy" :level="() => 0.8">
<div class="screen">Listening</div>
</TxVoiceBeam>
</template>
Best Practices
- Never let the glow be the only sign the mic is live: keep a text status and the button's
aria-pressed, and announce changes withrole="status". - Pass
levela getter (:level="() => meter.value"), not a reactive number; the getter is sampled once per frame with no re-render. - Put content that must stay crisp at
position: relative; z-index: 5; overlays inside the wrapped element get clipped, so portal them out. - Call
mic.stop()when the voice UI closes; the composable stops tracks only on unmount. - Keep instances few: each runs blurred layers and a canvas, so it doesn't suit dense lists.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'default' | 'pill' | 'mobile' | 'default' | Host preset (chat input, recording pill, phone bottom) that seeds the geometry props. |
stream | MediaStream | null | null | Live audio to react to; wins over level. |
level | number | () => number | 0 | Manual level (0-1) used when there is no stream. |
sensitivity | number | 3.1 | Input gain on the analysed audio. |
threshold | number | 0.015 | Noise gate (0-1); levels below it read as silence. |
attack | number | 0.325 | Seconds the glow takes to rise. |
release | number | 0.86 | Seconds the glow takes to settle. |
idle | number | 0.23 | Resting presence (0-1), so the beam never looks dead. |
breatheDuration | number | 5.2 | Idle breathing period in seconds. |
reach | number | 1.2 | How tall the glow grows at full level. |
spread | number | 1.05 | How far the glow widens at full level. |
bands | boolean | true | Lets low, mid, and high bands move the lobes independently. |
flow | number | 48 | Sideways travel of the spectrum in px/s at full level. |
processing | boolean | false | Gathers the glow into a travelling beam and holds it lit. |
processingDuration | number | 1.1 | Seconds for one pass of the processing beam. |
processingLevel | number | 0.55 | How lit the glow stays while processing. |
processingEase | number | 0.6 | Seconds of the morph in either direction. |
processingTravel | number | 1.55 | How far the processing beam travels to each side. |
processingCurve | number | 2.1 | How the sweep eases into each turn. |
cornerFollow | number | 0.45 | How much the glow rides the corner arcs while processing. |
colorVariant | 'colorful' | 'mono' | 'ocean' | 'sunset' | … | 'colorful' | Palette for the lobes. |
colors | string[] | none | Up to seven lobe colours, centre first. |
bandColors | { core?, above?, mid?, below? } | theme defaults | Colours of the band's ridge and fringes. |
theme | 'dark' | 'light' | 'auto' | 'dark' | Background adaptation; auto follows the system. |
staticColors | boolean | false | Turns off the slow hue drift. |
hueRange | number | 24 / 40 | Hue drift range in degrees (dark / light default). |
hueDuration | number | 12 / 8.5 | Hue drift period in seconds (dark / light default). |
active | boolean | true | Off fades the beam out and stops the audio analysis. |
paused | boolean | false | Freezes glow, band, and analysis on their last frame. |
borderRadius | number | auto-detected | Corner radius in px. |
brightness / saturation | number | theme defaults | Glow multipliers. |
glowSize | number | 1 | Bloom blur radius multiplier. |
strokeOpacity / innerOpacity / bloomOpacity | number | 1 | Opacity multipliers of the stroke, inner light, and bloom layers. |
scale | number | 1 | Multiplies every pixel dimension at once. |
bend, bandStrength, bandWidth, bandPosition, bandCurve, bandSpread, bandSkew, bandOffset, bandTail, bandTailPosition, bandTailCurve, bandTailOverflow, bandAberration | number | tuned | Shape of the glow's contour and the band along it. |
distortion, distortionDetail | number | 0.62, 2.3 | Strength and noise grain of the warp under the band line. |
glowWidth, glowHeight, lobeSpacing, rangeWidth, rangeHeight, softness, coreSize, coreLight, coreLightWidth, coreLightHeight, strokeScale, innerScale, innerHeight, bloomScale, bloomHeight | number | tuned | Lobe, visible-range, core, and halo geometry. |
strength | number | 1 | Overall effect opacity (0-1); the children are untouched. |
css | string | none | CSS appended after the generated stylesheet; {id} is replaced per instance. |
Slots
| Slot | Props | Description |
|---|---|---|
default | - | The wrapped element; the beam layers render behind and above it. |
Events
| Event | Payload | Description |
|---|---|---|
level | (level: number) | Fires every frame with the smoothed level the beam shows. |
activate | - | Fires when the fade-in completes. |
deactivate | - | Fires when the fade-out completes. |
CSS Variables
| Variable | Source | Description |
|---|---|---|
--voice-strength | strength | Beam-layer opacity (0-1). |
--voice-stroke-opacity / --voice-inner-opacity / --voice-bloom-opacity | host CSS | Per-layer opacity multipliers, multiplied with strokeOpacity / innerOpacity / bloomOpacity; the component never sets them. |
Overview
- It clips to the child's
border-top-left-radius(16 px when none is found), so the beam hugs the element edge. - One shared
requestAnimationFrameloop, capped at about 60 fps, drives every instance; an instance scrolled offscreen (256 px margin) unregisters and releases its analyser. - One
AudioContextis shared per page, with one source node per stream (reference-counted) and one analyser per instance. Audio is analysed, never played. - With a
stream, the analyser reads the RMS level plus three voice bands (80-300, 300-2000, 2000-6000 Hz). - Under reduced motion, the idle breathing, colour flow, hue drift, distortion, and processing sweep stop; the reaction to sound stays, since it is a meter.
Technologies
styles.ts,presets.ts,voice-driver.ts,audio.ts, andcolor.tsare verbatim ports of Jakubantalik/Libraries · voice-glow (MIT © Jakub Antalik);useMicrophoneis the Vue port of its React hook.- Source:
packages/tuffex/packages/components/src/voice-beam/.
查看源码
packages/tuffex/packages/components/src/voice-beam/index.ts