AudioQ
The purpose of this package is to help manage the playback of audio files.
๐ต Demo
๐ Docs
Key Features
- Multi-channel queue management - Independent audio queues for concurrent playback
- Pause/Resume functionality - Full playback control for individual channels or all channels
- Volume control with ducking - Dynamic volume management, global volume control, and automatic background audio reduction
- Loop support - Seamless audio looping for background music and ambient sounds
- Priority queueing - Add urgent audio to the front of any queue
- Real-time progress tracking - Comprehensive playback monitoring and metadata
- **Eโฆ
AudioQ
The purpose of this package is to help manage the playback of audio files.
๐ต Demo
๐ Docs
Key Features
- Multi-channel queue management - Independent audio queues for concurrent playback
- Pause/Resume functionality - Full playback control for individual channels or all channels
- Volume control with ducking - Dynamic volume management, global volume control, and automatic background audio reduction
- Loop support - Seamless audio looping for background music and ambient sounds
- Priority queueing - Add urgent audio to the front of any queue
- Real-time progress tracking - Comprehensive playback monitoring and metadata
- Event-driven architecture - Extensive callback system for UI integration
- TypeScript support - Full type definitions and IntelliSense support
- Zero dependencies - Lightweight and self-contained
- Backward compatible - All existing APIs continue to work
This package offers TypeScript support, has zero dependencies, and is released under the MIT license.
To preview this package and see how it works with visualized code examples, check out the demo that can be found here: AudioQ Demo. (A link to the demo repo can be found here: AudioQ Demo Repo.)
NPM package can be found here.
GitHub Repo can be found here.
Documentation can be found here
๐ Browser Compatibility
This package is designed for browser environments and uses the Web Audio API (HTMLAudioElement). It is not intended for Node.js server-side use.
Supported Browsers:
- Chrome 51+ (June 2016)
- Firefox 54+ (June 2017)
- Safari 10+ (September 2016)
- Edge 15+ (April 2017)
- Mobile browsers with HTML5 audio support
Development Requirements:
- Node.js 14+ (for building and testing only)
- TypeScript 4.5+ (included in devDependencies)
Not Supported:
- Node.js server environments (no HTMLAudioElement)
- Internet Explorer (lacks ES6 support)
- Web Workers (no DOM access)
Architecture & Code Organization ๐๏ธ
Modular architecture for maintainability and extensibility:
src/
โโโ index.ts # Main entry point with organized exports
โโโ types.ts # TypeScript interfaces and type definitions
โโโ core.ts # Core queue management functions
โโโ pause.ts # Pause and resume functionality
โโโ volume.ts # Volume control and ducking management
โโโ info.ts # Audio information and progress tracking
โโโ events.ts # Event handling and emission logic
โโโ utils.ts # Helper functions and utilities
How To Install This Package:
Install this package by running either of these commands (typescript packages are included automatically):
- For npm run
npm install audioq - For yarn run
yarn add audioq
Basic Queue Management Functions:
Queue Audio
// Add an audio file to the queue and start playing it automatically.
queueAudio(audioUrl, channelNumber?, options?);
await queueAudio('hello.mp3'); // Add to default channel 0
await queueAudio('laser.mp3', 1, { loop: true, volume: 0.8 }); // Add to channel 1 with options
Queue Audio with Priority
// Add a file to the front of the queue (plays after current audio finishes).
queueAudioPriority(audioUrl, channelNumber?, options?);
await queueAudioPriority('urgent.mp3'); // Add to front of default channel 0
await queueAudioPriority('announcement.mp3', 1, { volume: 1.0 }); // Add to front of channel 1
Stop Current Audio
// Stop the current audio and automatically start playing the next one in queue.
stopCurrentAudioInChannel(channelNumber?);
await stopCurrentAudioInChannel(); // Stop current audio in default channel (0)
await stopCurrentAudioInChannel(2); // Stop current audio in channel 2
Stop All Audio in Channel
// Stop all audio in a channel and remove all enqueued files.
stopAllAudioInChannel(channelNumber?);
await stopAllAudioInChannel(); // Stop and clear all audio in default channel (0)
await stopAllAudioInChannel(1); // Stop and clear all audio in channel 1
Stop All Audio
// Stop all audio in all channels and remove all enqueued files.
await stopAllAudio();
๐ Advanced Queue Manipulation:
Remove Queued Item
// Remove a specific item from the queue by its position (cannot remove currently playing item at index 0).
removeQueuedItem(queuedSlotNumber, channelNumber?);
const result = await removeQueuedItem(2); // Remove item at index 2 from default channel (0)
const result = await removeQueuedItem(1, 1); // Remove item at index 1 from channel 1
Reorder Queue Items
// Move a queue item from one position to another (cannot move currently playing item at index 0).
reorderQueue(currentQueuedSlotNumber, newQueuedSlotNumber, channelNumber?);
const result = await reorderQueue(3, 1); // Move item from index 3 to index 1 in default channel (0)
const result = await reorderQueue(2, 4, 1); // Move item from index 2 to index 4 in channel 1
Clear Queue After Current
// Remove all items from the queue except the currently playing audio.
clearQueueAfterCurrent(channelNumber?);
const result = await clearQueueAfterCurrent(); // Clear queue after current in default channel (0)
const result = await clearQueueAfterCurrent(2); // Clear queue after current in channel 2
Swap Queue Items
// Swap the positions of two items in the queue (cannot involve currently playing item at index 0).
swapQueueItems(firstQueuedSlotNumber, secondQueuedSlotNumber, channelNumber?);
const result = await swapQueueItems(1, 3); // Swap items at index 1 and 3 in default channel (0)
const result = await swapQueueItems(2, 4, 1); // Swap items at index 2 and 4 in channel 1
Get Queue Item Info
// Get information about a specific item in the queue.
getQueueItemInfo(queuedSlotNumber, channelNumber?);
const itemInfo = getQueueItemInfo(1); // Get info for item at index 1 in default channel (0)
const info = getQueueItemInfo(2, 1); // Get info for item at index 2 in channel 1
Get Queue Length
// Get the total number of items in a channel's queue.
getQueueLength(channelNumber?);
const length = getQueueLength(); // Get queue length for default channel (0)
const count = getQueueLength(2); // Get queue length for channel 2
// All queue manipulation functions return a QueueManipulationResult:
interface QueueManipulationResult {
success: boolean; // Whether the operation was successful
error?: string; // Error message if operation failed
updatedQueue?: QueueSnapshot; // The queue snapshot after the operation (if successful)
}
๐๏ธ Volume Control Functions:
Set Channel Volume
// Set the volume for a specific channel (0-1 range).
setChannelVolume(channelNumber, volume);
await setChannelVolume(0, 0.5); // Set channel 0 to 50% volume
await setChannelVolume(1, 0.8); // Set channel 1 to 80% volume
Get Channel Volume
// Get the current volume level for a specific channel.
getChannelVolume(channelNumber?);
const volume = getChannelVolume(); // Get default channel (0) volume
console.log(`Channel volume: ${(getChannelVolume(2) * 100).toFixed(0)}%`); // Get channel 2 volume
Set All Channels Volume
// Set the same volume level for all channels.
setAllChannelsVolume(volume);
await setAllChannelsVolume(0.6); // Set all channels to 60% volume
await setAllChannelsVolume(0.0); // Mute all channels
Global Volume Control
// Set a global volume multiplier that affects all channels while preserving their relative levels.
setGlobalVolume(volume);
await setGlobalVolume(0.5); // Set global volume to 50%
// Get the current global volume level.
getGlobalVolume();
const globalVol = getGlobalVolume(); // Returns current global volume (0-1)
console.log(`Global volume: ${(getGlobalVolume() * 100).toFixed(0)}%`);
How Global Volume Works: The global volume acts as a global volume multiplier - it scales all channel volumes proportionally while preserving their individual settings. This is perfect for implementing a global volume slider in your UI.
// Example: Setting up different audio types with a global volume control
await setChannelVolume(0, 0.3); // SFX channel at 30%
await setChannelVolume(1, 0.7); // Music channel at 70%
await setChannelVolume(2, 0.5); // Voice channel at 50%
// User adjusts global volume slider to 50%
await setGlobalVolume(0.5);
// Actual playback volumes become:
// - SFX: 30% ร 50% = 15%
// - Music: 70% ร 50% = 35%
// - Voice: 50% ร 50% = 25%
// Channel volumes remain at their original settings (0.3, 0.7, 0.5)
// So when global volume is increased, ratios are preserved
await setGlobalVolume(1.0); // Back to full volume
// - SFX: 30% ร 100% = 30%
// - Music: 70% ร 100% = 70%
// - Voice: 50% ร 100% = 50%
Note: setAllChannelsVolume() sets all channels to the same volume, while setGlobalVolume() multiplies all channels by the same amount, preserving their relative differences.
Volume Ducking (Background Audio Reduction)
// Automatically reduce other channels' volume when priority audio plays.
setVolumeDucking(config);
setVolumeDucking({ priorityChannel: 1, duckingVolume: 0.2 }); // Simple ducking
setVolumeDucking({
priorityChannel: 1,
priorityVolume: 1.0,
duckingVolume: 0.2,
transitionDuration: 500
}); // Full configuration
// Remove volume ducking configuration from all channels.
clearVolumeDucking();
โฏ๏ธ Pause/Resume Functions:
Pause Channel
// Pause audio playback in a specific channel.
pauseChannel(channelNumber?);
await pauseChannel(); // Pause audio in default channel (0)
await pauseChannel(1); // Pause audio in channel 1
Resume Channel
// Resume audio playback in a specific channel.
resumeChannel(channelNumber?);
await resumeChannel(); // Resume audio in default channel (0)
await resumeChannel(1); // Resume audio in channel 1
Toggle Pause
// Toggle between pause and resume states.
togglePauseChannel(channelNumber?);
await togglePauseChannel(); // Toggle default channel (0)
await togglePauseChannel(1); // Toggle channel 1
Pause/Resume All Channels
// Pause all channels simultaneously.
pauseAllChannels();
await pauseAllChannels();
// Resume all channels that were paused.
resumeAllChannels();
await resumeAllChannels();
Global Toggle Pause/Resume
// Smart toggle that pauses all channels if any are playing, or resumes all if all are paused.
togglePauseAllChannels();
await togglePauseAllChannels();
Check Pause State
// Check if a specific channel is paused.
isChannelPaused(channelNumber?);
const isPaused = isChannelPaused(); // Check if default channel (0) is paused
const channelPaused = isChannelPaused(2); // Check if channel 2 is paused
// Get pause state for all channels.
getAllChannelsPauseState();
const allPauseStates = getAllChannelsPauseState();
Audio Information and Progress Tracking:
AudioInfo Interface
The package provides detailed information about audio playback through the enhanced AudioInfo interface:
interface AudioInfo {
currentTime: number; // Current position in milliseconds
duration: number; // Total duration in milliseconds
fileName: string; // Extracted filename from URL
isLooping: boolean; // Whether audio is set to loop
isPaused: boolean; // Whether audio is currently paused
isPlaying: boolean; // Whether audio is currently playing
progress: number; // Progress as percentage (0-1)
src: string; // Audio file source URL
volume: number; // Current volume level (0-1)
}
Get Current Audio Info
// Get information about the currently playing audio in a specific channel.
getCurrentAudioInfo(channelNumber?);
const audioInfo = getCurrentAudioInfo(); // Get current audio info for default channel (0)
const info = getCurrentAudioInfo(1); // Get info for channel 1 - returns AudioInfo | null
Get All Channels Info
// Get audio information for all channels.
getAllChannelsInfo();
const allChannelsInfo = getAllChannelsInfo();
Queue State Management
// Get a complete snapshot of the queue state for a specific channel.
getQueueSnapshot(channelNumber?);
const queueSnapshot = getQueueSnapshot(); // Get snapshot for default channel (0)
const snapshot = getQueueSnapshot(2); // Get snapshot for channel 2 - returns QueueSnapshot | null
interface QueueSnapshot {
channelNumber: number; // Channel this snapshot represents
currentIndex: number; // Index of currently playing item
isPaused: boolean; // Whether current audio is paused
items: Array<{ // Array of queue items with metadata
duration: number;
fileName: string;
isCurrentlyPlaying: boolean;
isLooping: boolean;
src: string;
volume: number;
}>;
totalItems: number; // Total items in queue
volume: number; // Current channel volume (0-1)
}
Real-time Progress Tracking
// Subscribe to real-time progress updates for a specific channel.
onAudioProgress(channelNumber, callback);
onAudioProgress(0, (info) => console.log(info.progress)); // Simple progress logging
onAudioProgress(1, (info) => updateProgressBar(info.currentTime, info.duration)); // Complex UI update
// Remove all progress listeners for a specific channel.
offAudioProgress(channelNumber?);
offAudioProgress(); // Remove all progress listeners in default channel (0)
offAudioProgress(1); // Remove all progress listeners in channel 1
Enhanced Event System
// Subscribe to queue change events for visual updates.
onQueueChange(channelNumber, callback);
onQueueChange(0, (snapshot) => updateQueueDisplay(snapshot)); // Update UI on queue changes
// Unsubscribe from queue change events (removes ALL queue change callbacks for the channel)
offQueueChange(channelNumber?);
offQueueChange(); // Stop receiving all queue change notifications for default channel (0)
offQueueChange(1); // Stop receiving all queue change notifications for channel 1
// Subscribe to audio start events.
onAudioStart(channelNumber, callback);
onAudioStart(0, (info) => console.log(`Started: ${info.fileName}`)); // Log audio starts
// Unsubscribe from audio start events (removes ALL start callbacks for the channel)
offAudioStart(channelNumber?);
offAudioStart(); // Stop receiving all start notifications for default channel (0)
offAudioStart(1); // Stop receiving all start notifications for channel 1
// Subscribe to audio completion events.
onAudioComplete(channelNumber, callback);
onAudioComplete(0, (info) => logPlayHistory(info)); // Track completed audio
// Unsubscribe from audio completion events (removes ALL complete callbacks for the channel)
offAudioComplete(channelNumber?);
offAudioComplete(); // Stop receiving all completion notifications for default channel (0)
offAudioComplete(1); // Stop receiving all completion notifications for channel 1
// Subscribe to audio pause events.
onAudioPause(channelNumber, callback);
onAudioPause(0, (info) => showPauseIcon(info)); // Show pause state in UI
// Unsubscribe from audio pause events (removes ALL pause callbacks for the channel)
offAudioPause(channelNumber?);
offAudioPause(); // Stop receiving all pause notifications for default channel (0)
offAudioPause(1); // Stop receiving all pause notifications for channel 1
// Subscribe to audio resume events.
onAudioResume(channelNumber, callback);
onAudioResume(0, (info) => showPlayIcon(info)); // Show play state in UI
// Unsubscribe from audio resume events (removes ALL resume callbacks for the channel)
offAudioResume(channelNumber?);
offAudioResume(); // Stop receiving all resume notifications for default channel (0)
offAudioResume(1); // Stop receiving all resume notifications for channel 1
TypeScript Support
If you cannot import audio files into your app, you may need a custom.d.ts file in the root directory. An example of one is shown here:
custom.d.ts
declare module '*.mp3' {
const src: string;
export default src;
}
Development & Contributing ๐ ๏ธ
The package uses a modular TypeScript architecture that makes it easy to contribute and extend:
Testing:
The package includes a comprehensive test suite with 74+ tests covering all functionality:
# Run tests once
npm test
# Run tests in watch mode during development
npm run test:watch
# Run tests with coverage report
npm run test:coverage