HTML5音频分析器不直接识别节奏,而是提供低延迟时域数据;应聚焦20–250Hz能量变化,用getByteTimeDomainData()获取波形、计算短时RMS、差分增强敲击点,再通过滑动窗口统计BPM并平滑过渡。

HTML5 音频分析器本身不直接执行节奏识别,而是为算法提供稳定、低延迟的原始信号特征——真正起作用的是你用这些数据构建的节奏检测逻辑。关键不是“分析器做了什么”,而是“你怎么用它喂出可靠的时间域线索”。
聚焦时域能量变化,而非频谱分布
节奏本质是能量在时间轴上的周期性冲击,底鼓、踩镲等节奏源集中在低频段(20–250 Hz),对应 AnalyserNode 的前几个频点。与其做全频 FFT,不如用 getByteTimeDomainData() 获取原始波形振幅序列,再计算短时能量(如每 50ms 窗口内 RMS 值)。这样响应更快、抗噪更强,也更贴近人耳对节拍的感知方式。
- 设置 analyser.fftSize = 256 → frequencyBinCount = 128,但时域分析无需依赖此值
- 调用
analyser.getByteTimeDomainData(array)每帧获取 128 个 0–255 的归一化采样点 - 计算窗口 RMS:
Math.sqrt(array.reduce((sum, v) => sum + Math.pow(v - 128, 2), 0) / array.length) - 对 RMS 序列做一阶差分,放大能量上升沿(即“敲击点”),比直接找峰值更鲁棒
用滑动缓冲区替代单帧判断
单次能量突增可能是噪声或误触。节奏识别必须建立时间上下文:
- 主线程每 100ms 向 Worker 发送一帧 RMS 值(附带时间戳)
- Worker 维护最近 2 秒的 RMS 时间序列(约 20 个点)
- 在该序列中检测局部极大值,并计算相邻峰值间隔(单位 ms)
- 将间隔映射为 BPM:
bpm = Math.round(60000 / interval),再放入长度为 5 的缓冲区 - 输出中位数 BPM,剔除明显离群值(如与当前中位数偏差 > ±20 bpm)
主线程同步动画需锚定真实时间
拿到 BPM 后,不能简单设 CSS 动画 duration,否则音画会漂移:
- 记录首次有效 BPM 的时间
baseTime = performance.now() - 每帧用
beatPhase = ((now - baseTime) * bpm / 60000) % 1计算小节内相位 - UI 动画(如圆圈缩放、背景色脉动)直接绑定
beatPhase,例如:const scale = 1 + 0.25 * Math.abs(Math.sin(2 * Math.PI * beatPhase)); element.style.transform = `scale(${scale})`; - 若新 BPM 与旧值差异 > ±3 且置信度高,用 400ms 缓动过渡,避免跳变
实际落地要绕开三个典型坑
移动端静音限制、数值抖动、噪声干扰,不处理就会让节奏识别“看起来很准,用起来总错”:
- AudioContext 必须由用户手势(click/touchstart)触发 resume,否则无数据
- 对原始 RMS 做指数平滑:
smoothedRMS = smoothedRMS * 0.7 + currentRMS * 0.3 - 加能量门限:仅当
smoothedRMS > 30才参与节奏检测,过滤环境底噪
不复杂但容易忽略



















