WebberUI

Audio Visualizer

Web Audio API 音訊頻譜視覺化:以 canvas 2D 繪製長條、鏡像與圓形三種款式,可分析真實音源或以合成資料驅動示意動畫。

載入預覽⋯

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

48
1
<AudioVisualizer />

安裝

npx shadcn@latest add https://webberui.com/r/audio-visualizer.json

或在 components.json 設定 registries 後,改用 @webberui/audio-visualizer 安裝。

安裝依賴後,打開 audio-visualizer.json,把 files[0].content 的內容複製到你的 components/ui/audio-visualizer.tsx

npm install motion lucide-react clsx tailwind-merge

使用

不提供任何音源時,元件以程序化合成資料驅動,適合作為裝飾性的示意動畫:

import { AudioVisualizer } from "@/components/ui/audio-visualizer";

<div className="h-40 overflow-hidden rounded-xl">
  <AudioVisualizer variant="bars" />
</div>

分析真實媒體元素

<audio> / <video> 的 ref 傳給 media,元件會透過 Web Audio API 讀取即時頻譜。媒體聲音會經由音訊圖回放,維持可聽見:

"use client";

import * as React from "react";
import { AudioVisualizer } from "@/components/ui/audio-visualizer";

export function Player() {
  const audioRef = React.useRef<HTMLAudioElement>(null);
  const [playing, setPlaying] = React.useState(false);

  return (
    <div>
      <audio ref={audioRef} src="/track.mp3" />
      <AudioVisualizer
        variant="mirror"
        media={audioRef}
        active={playing}
        color="#22d3ee"
        className="h-40"
      />
      <button
        onClick={() => {
          const el = audioRef.current;
          if (!el) return;
          if (playing) el.pause();
          else void el.play();
          setPlaying(!playing);
        }}
      >
        播放 / 暫停
      </button>
    </div>
  );
}

分析麥克風

getUserMedia 取得的 MediaStream 傳給 stream(麥克風不會回放,避免回授):

const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
<AudioVisualizer variant="radial" stream={stream} sensitivity={1.4} />

Props

Prop型別預設值說明
variant"bars" | "mirror" | "radial""bars"視覺化款式:底部長條、中線鏡像、圓形徑向
mediaRefObject<HTMLMediaElement | null>要分析的 <audio>/<video> ref;提供後分析真實頻譜
streamMediaStream | nullnull直接提供音訊串流(例如麥克風);優先於 media
activebooleantrue是否運行動畫;false 時凍結為靜止低振幅狀態
barsnumber48頻段數量(radial 為射線數),內部夾在 4–256
colorstring隨主題的 neutral 色主色;未指定時跟隨 currentColor(隨深淺主題切換)
fftSizenumber2048FFT 視窗大小,需為 2 的次方(32–32768)
smoothingnumber0.8頻譜平滑係數 0–1,越大越平順、反應越慢
sensitivitynumber1靈敏度倍率,放大整體振幅
labelstring"音訊視覺化"套用於容器 aria-label 的無障礙標籤
classNamestring附加到最外層容器的 class

細節

  • 雙模式資料來源。 提供 mediastream 時建立 AnalyserNode,每幀以 getByteFrequencyData 讀取真實頻譜,並依「近似對數」分組為 bars 條、強調低頻;未提供音源時退回程序化合成資料(多層正弦振盪 + 節拍脈衝),示範環境無需外部音檔即可展示
  • canvas 2D 單層繪製。 三種款式都畫在同一張 <canvas>bars 由底部升起、mirror 以中線上下鏡像、radial 沿圓周向外輻射;每幀 clearRect 後重繪,圓角以原生 roundRect(不支援時退化為 arcTo 手繪)
  • MediaElementSource 只能建立一次。 同一個媒體元素在整個生命週期僅能建立一次來源節點,重複建立會拋錯;元件以 WeakMap 快取「context / source / analyser」音訊圖,重掛載或重渲染時重用,元素被回收後自動釋放
  • AudioContext 生命週期。 由麥克風 stream 建立的 context 會在卸載時 close();媒體元素的音訊圖因被快取重用故不關閉。context 若處於 suspended(瀏覽器需使用者手勢),在 active 時嘗試 resume()
  • devicePixelRatio(上限 2)設定實際畫布尺寸再用 setTransform 縮放,Retina 螢幕維持銳利;ResizeObserver 監看容器變化即時重繪
  • 未指定 color 時讀取 canvas 的 currentColor(由 text-neutral-* class 解析),並以 MutationObserver 監看 <html> 的 class 變化,深淺主題切換時即時重讀顏色

可及性

  • 容器帶 role="img" 與可自訂的 aria-label(預設「音訊視覺化」),內部 <canvas> 標記 aria-hidden,不朗讀逐格畫面
  • 使用者系統開啟「減少動態效果」時,requestAnimationFrame 迴圈完全停用,只渲染一張靜止的低振幅剪影,DOM 結構不變(毋須兩段式掛載)
  • 視覺化為使用者已聽見之聲音的裝飾性映射;請確保實際的播放控制(播放/暫停按鈕等)本身具備完整的鍵盤與標籤支援

On this page