A small cross-browser library that turns any audio
source — mic, an <audio> element, a picked input
device, a browser tab, a dropped file — into one clean, smoothed
AudioFrame your visualizer reads. The same callback,
every browser. No build, no dependencies.
Every music visualizer needs the same boring thing first: a clean,
AGC'd, smoothed frequency + waveform + beat frame — cross-browser,
without re-solving AudioContext resume quirks, Safari's
webkitAudioContext, or each capture API's edge cases.
sj-audio is that layer. Five source adapters share one
analysis pipeline and emit one stable AudioFrame, so
your viz code never cares where the audio came from — and a unified
engine falls back through the chain until one source works.
No bundler required. Pull the UMD build off the CDN for a plain
<script>, or import the ESM build directly.
<script src="https://cdn.jsdelivr.net/gh/jayvee6/sj-audio@latest/dist/sj-audio.umd.js"></script>
<script>
const engine = SJAudio.createAudioEngine({
mediaElement: document.querySelector('audio'),
});
await engine.start();
engine.onFrame((f) => drawBars(f.magnitudesSmooth));
</script>
import { createAudioEngine } from
'https://cdn.jsdelivr.net/gh/jayvee6/sj-audio@latest/dist/sj-audio.esm.js';
@latest tracks the newest release tag. Pin a specific
version (e.g. @v0.2.0) in production so a future release
can't shift behaviour under you.
Pick a source directly, or hand the engine a fallback chain and let
it choose the first that works. Listeners survive a runtime
switchTo().
import { createAudioEngine } from 'sj-audio';
const engine = createAudioEngine({
fallbackChain: ['mediaElement', 'microphone', 'file'],
mediaElement: document.querySelector('audio'),
});
await engine.start(); // tries in order; first success wins
console.log(engine.activeKind); // 'mediaElement' | 'microphone' | …
engine.onFrame((f) => {
drawBars(f.magnitudesSmooth); // 32 EMA-smoothed mel bands
if (f.isBeatNow) flash(); // onset this frame
});
Analyze any <audio> / <video>. Universal; keeps play / pause / seek.
getUserMedia — works everywhere. iOS Safari is quirky; the library handles the resume dance for you.
Served-site, zero install: enumerate the visitor's inputs, let them pick — or autodetect the one that's actually playing. Safari + Firefox too.
getDisplayMedia — Chromium desktop only. The library feature-detects and degrades gracefully elsewhere.
Drag-drop or picked File / Blob, analyze-only — one pass through the analyzer.
createAudioEngine wires a fallback chain across all of the above, with switchTo() at runtime and persistent listeners.
Same shape from every adapter and the engine. Typed-array buffers are stable references — mutated in place, so a visualizer can cache them once.
waveform — 256 samples, time-domain [-1..1].magnitudes / magnitudesSmooth — 32 mel-scale bands, AGC'd (+ EMA, attack 10 ms / release 120 ms).bass / mid / treble — pre-summed band energy, [0..1].beatPulse / bpm / isBeatNow — exp-decay onset, rolling-median tempo, per-frame onset flag.valence / energy / danceability — mood params, overridable via setMood.No silent failures — the library tells you what a browser can't do so your UI can adapt.
detectCapabilities() is synchronous and side-effect-free — show / hide the right buttons before you start.Seven self-contained HTML files — one per source, plus a UMD drop-in and the device picker.
Full TypeScript signatures, the AudioFrame spec, per-source recipes and caveats.