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

@zakkster/lite-audio-pool

v1.0.2

Published

Zero-GC Web Audio sound sprite pool. Voice stealing with 20ms anti-pop fade, pitch shifting, stereo pan.

Readme

@zakkster/lite-audio-pool

npm version npm bundle size npm downloads npm total downloads TypeScript Zero Dependencies License: MIT

🎧 What is lite-audio-pool?

@zakkster/lite-audio-pool is a zero-allocation, pre-wired, high-performance Web Audio sound system designed for real-time games.

It gives you:

  • 🔊 32+ concurrent voices
  • ⚡ O(1) channel reuse
  • 🔄 Voice stealing with 20ms anti-pop fade
  • 🎚️ Volume, pan, and pitch per sound
  • 🧩 Sprite-based audio (single buffer, many sounds)
  • 🧼 Zero garbage collection during gameplay
  • 🪶 < 1 KB minified

It's the opposite of a big audio framework — it's a tiny, raw, predictable tool that gives you full control without overhead.

Part of the @zakkster/lite-* ecosystem — micro-libraries built for deterministic, cache-friendly game development.

🚀 Install

npm i @zakkster/lite-audio-pool

🕹️ Quick Start

import { AudioPool } from '@zakkster/lite-audio-pool';

// Your Web Audio context
const ctx = new AudioContext();

// Your sprite atlas (one AudioBuffer, many slices)
const spriteMap = {
  laser:   { start: 0.00, duration: 0.15 },
  hit:     { start: 0.20, duration: 0.10 },
  explode: { start: 0.35, duration: 0.40 }
};

// Create a pool with 32 channels
const pool = new AudioPool(ctx, audioBuffer, spriteMap, 32);

// Play a sound (returns channel ID)
const ch = pool.play('laser', 1.0, 0.0, 1.0);
//                     id    vol   pan  pitch

// Stop a specific channel (20ms anti-pop fade)
pool.stop(ch);

// Stop everything (scene transitions)
pool.stopAll();

🧠 Why This Exists

Most JS audio libraries:

  • allocate new nodes per sound
  • create garbage on every play
  • cause audio pops when stealing voices
  • hide Web Audio behind abstractions
  • weigh 10–40 KB

lite-audio-pool does the opposite:

  • pre-allocates everything at construction
  • reuses channels in O(1)
  • applies a 20ms gain ramp to prevent pops
  • exposes raw Web Audio behavior
  • weighs under 1 KB

It's built for games, not apps.

📊 Comparison

| Library | Size | Allocations | Voice Stealing | Pitch | Pan | Use Case | |---------|------|-------------|----------------|-------|-----|----------| | howler.js | ~35 KB | High | No | Yes | Yes | General audio | | pizzicato | ~12 KB | Medium | No | Yes | Yes | Effects chains | | lite-audio-pool | < 1 KB | Zero | Yes (anti-pop) | Yes | Yes | Games, SFX, sprites |

⚙️ API

new AudioPool(ctx, audioBuffer, spriteMap, capacity?)

Creates a pool of pre-wired audio channels.

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | ctx | AudioContext | — | Your Web Audio context | | audioBuffer | AudioBuffer | — | The decoded sprite file | | spriteMap | Record<string, { start, duration }> | — | Named slices into the buffer | | capacity | number | 32 | Max concurrent voices (clamped to 256) |

play(spriteId, volume?, pan?, pitch?)

Plays a sound sprite immediately. Returns the channel index used, or -1 if the sprite ID is invalid.

| Parameter | Type | Default | Range | Description | |-----------|------|---------|-------|-------------| | spriteId | string | — | — | Key from your sprite map | | volume | number | 1.0 | 0–∞ | Gain multiplier | | pan | number | 0.0 | -1 to 1 | Stereo position (clamped) | | pitch | number | 1.0 | 0–∞ | Playback rate (2 = octave up) |

If all channels are busy, the oldest channel is stolen with a 20ms anti-pop fade-out before the new sound starts.

stop(channelId)

Stops a specific channel with a 20ms fade. Safe to call with invalid IDs.

stopAll()

Stops all active channels. Useful for scene transitions or pause screens.

Sprite Map Format

{
  laser:   { start: 0.00, duration: 0.15 },
  hit:     { start: 0.20, duration: 0.10 },
  explode: { start: 0.35, duration: 0.40 }
}

Each entry defines a time slice within the single AudioBuffer. The start is in seconds from the beginning of the buffer, duration is the length in seconds.

🧪 Benchmark

32 concurrent voices, rapid fire:

howler.js:
  - Allocates new AudioBufferSourceNode per play
  - GC spikes at 60–120ms intervals

lite-audio-pool:
  - Reuses pre-wired nodes
  - Zero allocations during gameplay
  - No GC spikes
  - Smooth 60–240 FPS gameplay

🔧 Internal Architecture

                ┌─────────────────────────────────┐
                │         AudioPool               │
                │                                 │
  play('laser') │  ┌─ Channel 0 ──────────────┐  │
  ─────────────►│  │ StereoPanner → GainNode ──┤──┼──► ctx.destination
                │  │ BufferSource ─►           │  │
                │  └───────────────────────────┘  │
                │  ┌─ Channel 1 ──────────────┐  │
                │  │ StereoPanner → GainNode ──┤──┤
                │  │ BufferSource ─►           │  │
                │  └───────────────────────────┘  │
                │  ...                            │
                │  ┌─ Channel N ──────────────┐  │
                │  │ StereoPanner → GainNode ──┤──┤
                │  │ BufferSource ─►           │  │
                │  └───────────────────────────┘  │
                │                                 │
                │  expireTimes: Float32Array(N)    │
                │  sources:     Array(N)          │
                └─────────────────────────────────┘

GainNode and StereoPanner are pre-wired at construction.
Only BufferSource is created per play (required by Web Audio spec).
Voice stealing: oldest channel → 20ms gain ramp to 0.0001 → stop → new source.

📦 TypeScript

Full TypeScript declarations included in AudioPool.d.ts.

📚 LLM-Friendly Documentation

See llms.txt for AI-optimized metadata and usage examples.

License

MIT