npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uln/impulse

v0.1.1

Published

Real-time audio feature extraction for the browser using WebAssembly. Extract BPM, beats, frequency bands, and spectral features from audio streams.

Downloads

71

Readme

@impulse/web

Real-time audio feature extraction for the browser using WebAssembly.

Extract BPM, beats, frequency bands, spectral features, and detect song transitions from audio streams with sub-millisecond latency.

Features

  • BPM Detection - Autocorrelation-based tempo tracking (configurable range, default 60-200 BPM)
  • Beat Tracking - Phase-accurate beat detection with confidence scoring
  • Onset Detection - Classify transients as kicks, snares, hi-hats, or generic onsets
  • Frequency Bands - 8-band energy analysis (sub-bass to air)
  • Spectral Features - Centroid, rolloff, flux, zero-crossing rate
  • Transition Detection - Detect song changes during DJ sets
  • Zero-Copy - Efficient Float32Array handling via WebAssembly

Installation

npm install @impulse/web
# or
pnpm add @impulse/web
# or
yarn add @impulse/web

Quick Start

import init, { ImpulseProcessor, WasmConfig } from '@impulse/web';

async function main() {
  // Initialize the WASM module (call once at startup)
  await init();

  // Create processor with club-optimized settings
  const config = WasmConfig.forClub();
  const processor = ImpulseProcessor.newWithConfig(48000, config);

  // Connect to audio input
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
  const audioContext = new AudioContext({ sampleRate: 48000 });
  const source = audioContext.createMediaStreamSource(stream);

  // Use AudioWorklet for real-time processing (recommended)
  // Or use ScriptProcessorNode for simpler setup:
  const scriptNode = audioContext.createScriptProcessor(2048, 2, 2);

  scriptNode.onaudioprocess = (event) => {
    const left = event.inputBuffer.getChannelData(0);
    const right = event.inputBuffer.getChannelData(1);

    // Process stereo audio and get features
    const output = processor.processStereo(left, right, performance.now());

    // Use extracted features
    console.log('BPM:', output.beat.bpm);
    console.log('RMS:', output.features.rms);
    console.log('Bass energy:', output.features.bands.values[1]);
  };

  source.connect(scriptNode);
  scriptNode.connect(audioContext.destination);
}

main();

API Reference

Initialization

init(): Promise<void>

Initialize the WASM module. Must be called once before using any other functions.

import init from '@impulse/web';
await init();

Configuration

WasmConfig

Configuration class for the audio processor.

// Create default config
const config = new WasmConfig();

// Create club-optimized config (70-180 BPM, transitions enabled)
const clubConfig = WasmConfig.forClub();

// Create low-latency config (smaller FFT for faster response)
const lowLatencyConfig = WasmConfig.lowLatency();

// Customize settings
config.setFftSize(2048);      // FFT size (power of 2: 1024, 2048, 4096)
config.setHopSize(512);       // Hop size (samples between FFT windows)
config.setBpmRange(70, 180);  // BPM detection range
config.setTransitionDetection(true);  // Enable song transition detection

| Preset | FFT Size | Hop Size | BPM Range | Transitions | |--------|----------|----------|-----------|-------------| | new() (default) | 2048 | 512 | 60-200 | disabled | | forClub() | 2048 | 512 | 70-180 | enabled | | lowLatency() | 1024 | 256 | 60-200 | disabled |

Processor

ImpulseProcessor

Main audio feature extraction processor.

// Create with default config
const processor = new ImpulseProcessor(sampleRate);

// Create with custom config
const processor = ImpulseProcessor.newWithConfig(sampleRate, config);
Methods
processMono(samples: Float32Array, timestampMs: number): ControlOutput

Process mono audio samples.

const output = processor.processMono(monoSamples, performance.now());
processStereo(left: Float32Array, right: Float32Array, timestampMs: number): ControlOutput

Process stereo audio by averaging channels to mono.

const output = processor.processStereo(leftChannel, rightChannel, performance.now());
processInterleaved(interleaved: Float32Array, timestampMs: number): ControlOutput

Process interleaved stereo audio ([L0, R0, L1, R1, ...]).

const output = processor.processInterleaved(interleavedStereo, performance.now());
getBpm(): number

Get current BPM estimate.

getBpmConfidence(): number

Get BPM confidence (0.0-1.0).

isBpmReady(): boolean

Check if BPM tracking has enough data for reliable estimates.

getFftSize(): number

Get the configured FFT size.

getSampleRate(): number

Get the configured sample rate.

reset(): void

Reset all processor state. Call this on track changes, seeks, or discontinuities.

Output Types

ControlOutput

Main output from processing functions.

interface ControlOutput {
  timestamp: number;           // Timestamp in nanoseconds
  sequence: number;            // Monotonic sequence number
  features: AudioFeatures;     // Extracted audio features
  beat: BeatInfo;              // Beat/BPM tracking state
  onsets: OnsetEvent[];        // Detected onsets this frame
  transition?: TransitionInfo; // Song transition state (if enabled)
}

AudioFeatures

Core audio measurements.

interface AudioFeatures {
  rms: number;               // Root mean square energy (0.0-0.7 typical)
  peak: number;              // Peak amplitude (0.0-1.0+)
  bands: BandEnergies;       // 8-band frequency energy
  spectral_centroid: number; // Brightness in Hz
  spectral_rolloff: number;  // 85% energy cutoff in Hz
  spectral_flux: number;     // Frame-to-frame spectral change
  zero_crossing_rate: number; // Noisiness indicator (0.0-1.0)
}

BandEnergies

Frequency band energy distribution.

interface BandEnergies {
  values: number[];  // 8 bands: sub-bass, bass, low-mid, mid, upper-mid, presence, brilliance, air
}

// Band indices:
// 0: Sub-bass (20-60 Hz)
// 1: Bass (60-250 Hz)
// 2: Low-mid (250-500 Hz)
// 3: Mid (500-2000 Hz)
// 4: Upper-mid (2000-4000 Hz)
// 5: Presence (4000-6000 Hz)
// 6: Brilliance (6000-10000 Hz)
// 7: Air (10000-20000 Hz)

BeatInfo

Beat tracking state.

interface BeatInfo {
  bpm: number;         // Current BPM estimate
  confidence: number;  // Confidence in estimate (0.0-1.0)
  phase: number;       // Position within beat (0.0-1.0, 0.0 = on beat)
  last_beat: number;   // Timestamp of last beat (nanoseconds)
  beat_count: number;  // Total beats detected
}

OnsetEvent

Detected transient/onset.

interface OnsetEvent {
  timestamp: number;      // When the onset occurred (nanoseconds)
  strength: number;       // Onset strength (0.0-1.0)
  onset_type: OnsetType;  // Classification
}

type OnsetType = 'Generic' | 'Kick' | 'Snare' | 'HiHat';

TransitionInfo

Song transition detection state.

interface TransitionInfo {
  in_transition: boolean;      // Whether a transition is in progress
  transition_duration: number; // Duration in seconds (0 if not transitioning)
  readiness: number;           // Detector readiness (0.0-1.0)
  event?: TransitionEventInfo; // Event if transition started/ended this frame
}

Utilities

log(message: string): void

Log a message to the browser console.

getTimestamp(): number

Get current high-resolution timestamp in milliseconds (uses performance.now()).

Usage with AudioWorklet

For production applications, use AudioWorklet instead of ScriptProcessorNode:

// audio-processor.worklet.ts
class AudioCaptureProcessor extends AudioWorkletProcessor {
  process(inputs: Float32Array[][], outputs: Float32Array[][]): boolean {
    const input = inputs[0];
    if (input.length >= 2) {
      this.port.postMessage({
        left: input[0].slice(),
        right: input[1].slice(),
        timestamp: currentTime * 1000  // Convert to ms
      });
    }
    return true;
  }
}
registerProcessor('audio-capture', AudioCaptureProcessor);

// main.ts
await audioContext.audioWorklet.addModule('audio-processor.worklet.js');
const workletNode = new AudioWorkletNode(audioContext, 'audio-capture');

workletNode.port.onmessage = (event) => {
  const { left, right, timestamp } = event.data;
  const output = processor.processStereo(left, right, timestamp);
  // Update visualization...
};

Usage with Bundlers

Vite

// vite.config.ts
import wasm from 'vite-plugin-wasm';
import topLevelAwait from 'vite-plugin-top-level-await';

export default {
  plugins: [wasm(), topLevelAwait()],
  optimizeDeps: {
    exclude: ['@impulse/web']
  }
};

Webpack 5

// webpack.config.js
module.exports = {
  experiments: {
    asyncWebAssembly: true
  }
};

Performance

  • DSP per-hop: ~15 microseconds (695x realtime)
  • Throughput: 525x realtime (release build)
  • FFT (2048): ~10 microseconds

Benchmarked on Apple M1. Performance scales with FFT size.

Browser Compatibility

Requires:

  • WebAssembly support
  • Web Audio API (getUserMedia for audio input)

Tested in:

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

Building from Source

Prerequisites:

  • Rust toolchain
  • wasm-pack (cargo install wasm-pack)
# Clone the repository
git clone https://github.com/jakeisnt/beatgrid.git
cd beatgrid

# Build the WASM package
cd packages/impulse-wasm
npm run build

# Or build for development (faster, with debug info)
npm run build:dev

License

MIT