Audio Player

  • Skia
  • Reanimated
  • Audio API

A player whose visualiser is driven by real FFT output rather than a looping animation. The interesting part is everything between the analyser and the bars: which bins each bar covers, and how its height is allowed to move.

Bars are spaced by ear, not by index

Mapping bars to bins linearly wastes most of them on treble nobody can pick out. Here the band edges follow pow(i / count, 1.55), so the low end gets far more bars than the top. The first two bins are skipped as rumble, everything above 72% is dropped, and the lowest eight bars are widened to at least three bins so they do not flicker on one noisy value.

const minBin = totalBins > 4 ? 2 : 0;const maxBin = Math.max(minBin + 1, Math.min(totalBins, Math.floor(totalBins * 0.72)));const span = maxBin - minBin;const curve = 1.55;const lo = Math.min(totalBins, Math.floor(minBin + Math.pow(i / count, curve) * span));// …const minWidth = index < lowBandCount ? 3 : 1;

Smoothing that does not depend on frame rate

A fixed blend per frame moves twice as fast at 120 fps as at 60. approach instead converts a rate and a delta into 1 - exp(-rate * dt), so a bar takes the same wall-clock time to reach a level on any device. The delta is capped at 100 ms, so a stalled frame eases in rather than snapping everything to the new value.

const d = dt > 0.1 ? 0.1 : dt < 0 ? 0 : dt;const k = 1 - Math.exp(-rate * d);return current + (target - current) * k;

Peaks that fall at a fixed speed

The cap above each bar is not smoothed. It jumps straight to any level that exceeds it and otherwise falls by a fixed amount per second, so a peak reads as a measurement being held rather than another animated bar. Because the fall is in units per second, it also survives a change of frame rate unchanged.

if (level >= peak)  return level;const d = dt > 0.1 ? 0.1 : dt < 0 ? 0 : dt;const next = peak - fall * d;return next < level ? level : next;

Tuning

  • 64
<AudioPlayer params={{ mode: "bars", palette: "aurora", barCount: 64 }} fireSignal={0} />