audio-tracker
v2.0.0
Published
A headless JavaScript library that gives you full control over web audio — playback, tracking, and Media Session integration made simple.
Maintainers
Readme
🎵 audio-tracker
A headless JavaScript library that gives you full control over web audio — playback, tracking, and Media Session integration made simple.
🎮 Live Demo | 📖 Documentation | 💾 Installation
📋 Table of Contents
- Features
- Installation
- Quick Start
- API Documentation
- Modules
- Usage Examples
- Browser Support
- Common Issues
- Contributing
- License
✨ Features
- 🎯 TypeScript First - Full type definitions included, no @types needed
- 🎵 Complete Audio Control - Play, pause, seek, volume, speed control
- 🔄 Media Session API - Native lock screen and media key controls
- 📱 Cross-Platform - Works on desktop and mobile browsers
- 🎨 Framework Agnostic - Use with React, Vue, Angular, or vanilla JS
- ⚡ Flexible Input - Accept URL strings or existing HTMLAudioElement
- 🪝 Rich Event System - 18+ callbacks for all audio events
- 🎮 Zero Dependencies - Pure TypeScript, no external dependencies
- 📦 Lightweight - Minimal footprint, tree-shakeable
- 🎧 Headless - No UI, just audio control logic
- 🧩 Modular Architecture - Extend with Media Session and Timestamp modules
📦 Installation
npm
npm install audio-trackeryarn
yarn add audio-trackerpnpm
pnpm add audio-trackerCDN
<script src="https://unpkg.com/[email protected]/dist/index.js"></script>🚀 Quick Start
import AudioTracker, { mediaSessionModule } from "audio-tracker";
// Create tracker with audio URL and options
const tracker = new AudioTracker("/path/to/audio.mp3", {
preload: "metadata",
loop: false,
muted: false,
autoplay: false,
volume: 80,
});
// Optional: Add lock screen controls with one line
tracker.use(mediaSessionModule);
// Initialize with callbacks
tracker.init({
onPlay: () => console.log("▶️ Playing"),
onPause: () => console.log("⏸️ Paused"),
onTimeUpdate: (time) => console.log(`⏱️ ${time}s`),
onDurationChange: (duration) => console.log(`📏 Duration: ${duration}s`),
});
// Control playback
tracker.play();
tracker.pause();
tracker.seekTo(30); // Seek to 30 seconds
tracker.setVolume(80); // 80% volume
tracker.setPlaybackRate(1.5); // 1.5x speed📚 API Documentation
Constructor
new AudioTracker(
audioSource: string | HTMLAudioElement,
options?: AudioTrackerOptions
)Parameters
| Parameter | Type | Description |
| ------------- | ---------------------------- | ---------------------------------------- |
| audioSource | string \| HTMLAudioElement | Audio file URL or existing audio element |
| options | AudioTrackerOptions | Configuration options (optional) |
Options Interface
interface AudioTrackerOptions {
preload?: "none" | "metadata" | "auto";
loop?: boolean;
muted?: boolean;
autoplay?: boolean;
crossOrigin?: "anonymous" | "use-credentials" | null;
volume?: number; // 0-100
}Methods
All methods available on the AudioTracker instance.
| Method | Parameters | Returns | Description |
| ----------------------- | --------------------------------------- | ------------------ | ---------------------------------------- |
| Initialization |
| init | callbacks: AudioCallbacks | AudioTracker | Initialize tracker with event callbacks |
| use | module: AudioModule | AudioTracker | Extend functionality with a module |
| Playback Controls |
| play | none | Promise<void> | Start audio playback |
| pause | none | void | Pause audio playback |
| togglePlay | none | Promise<void> | Toggle between play and pause states |
| seekTo | time: number | void | Seek to specific time in seconds |
| forward | seconds?: number | void | Skip forward (default: 10 seconds) |
| backward | seconds?: number | void | Skip backward (default: 10 seconds) |
| isPlaying | none | boolean | Check if audio is currently playing |
| Volume Controls |
| setVolume | value: number | void | Set volume level (0-100) |
| getVolume | none | number | Get current volume (0-100) |
| toggleMute | none | boolean | Toggle mute state, returns new state |
| setMuted | muted: boolean | void | Set mute state directly |
| isMuted | none | boolean | Check if audio is muted |
| Playback Speed |
| setPlaybackRate | rate: number | void | Set playback speed (0.25 - 4.0) |
| getPlaybackRate | none | number | Get current playback speed |
| Audio Attributes |
| setLoop | loop: boolean | void | Enable or disable looping |
| isLooping | none | boolean | Check if looping is enabled |
| setAutoplay | autoplay: boolean | void | Set autoplay attribute |
| getAutoplay | none | boolean | Get autoplay state |
| setCrossOrigin | crossOrigin: string \| null | void | Set CORS settings |
| getCrossOrigin | none | string \| null | Get CORS setting |
| setPreload | preload: string | void | Set preload strategy |
| getPreload | none | string | Get preload strategy |
| State & Information |
| getDuration | none | number | Get total audio duration in seconds |
| getCurrentTime | none | number | Get current playback position in seconds |
| getTimeRemaining | none | number | Get remaining time in seconds |
| getReadyState | none | number | Get ready state (0-4) |
| getNetworkState | none | number | Get network state (0-3) |
| Utilities |
| formatTime | seconds: number | string | Format seconds to MM:SS format |
| getAudioElement | none | HTMLAudioElement | Get underlying audio element |
| subscribe | eventName: string, callback: Function | void | Subscribe to DOM events |
| unsubscribe | eventName: string, callback: Function | void | Unsubscribe from DOM events |
| destroy | none | void | Clean up and remove all listeners |
Method Examples
// Initialization
tracker.init({ onPlay: () => console.log("Playing") });
tracker.use(mediaSessionModule);
// Playback Controls
await tracker.play();
tracker.pause();
await tracker.togglePlay();
tracker.seekTo(45);
tracker.forward(30);
tracker.backward(15);
const playing = tracker.isPlaying();
// Volume Controls
tracker.setVolume(75);
const volume = tracker.getVolume();
const muted = tracker.toggleMute();
tracker.setMuted(true);
const isMuted = tracker.isMuted();
// Playback Speed
tracker.setPlaybackRate(1.5);
const rate = tracker.getPlaybackRate();
// Audio Attributes
tracker.setLoop(true);
const looping = tracker.isLooping();
tracker.setAutoplay(false);
const autoplay = tracker.getAutoplay();
tracker.setCrossOrigin("anonymous");
const cors = tracker.getCrossOrigin();
tracker.setPreload("metadata");
const preload = tracker.getPreload();
// State & Information
const duration = tracker.getDuration();
const currentTime = tracker.getCurrentTime();
const remaining = tracker.getTimeRemaining();
const readyState = tracker.getReadyState();
const networkState = tracker.getNetworkState();
// Utilities
const formatted = tracker.formatTime(125); // "2:05"
const audioEl = tracker.getAudioElement();
tracker.subscribe("play", () => console.log("Playing"));
tracker.unsubscribe("play", handler);
tracker.destroy();Callbacks
All callbacks are optional. Choose which events to listen to.
interface AudioCallbacks {
// Playback events
onPlay?: () => void;
onPause?: () => void;
onEnded?: () => void;
onPlaying?: () => void;
// Time events
onTimeUpdate?: (currentTime: number) => void;
onDurationChange?: (duration: number) => void;
onSeeking?: (time: number) => void;
// Loading events
onBufferChange?: (bufferedTime: number) => void;
onBufferPercentageChange?: (percentage: number) => void;
onWaiting?: () => void;
onCanPlay?: () => void;
onLoadStart?: () => void;
onStalled?: () => void;
// Control events
onRateChange?: (rate: number) => void;
onVolumeChange?: (volume: number) => void;
onMuteChange?: (muted: boolean) => void;
// Error handling
onError?: (error: MediaError | null) => void;
}Callback Reference
| Callback | Parameters | Description |
| -------------------------- | --------------------------- | ------------------------------------------------- |
| onPlay | none | Fires when audio starts playing |
| onPause | none | Fires when audio pauses |
| onEnded | none | Fires when audio playback ends |
| onPlaying | none | Fires when playback resumes after buffering |
| onTimeUpdate | currentTime: number | Fires continuously during playback (~4 times/sec) |
| onDurationChange | duration: number | Fires when audio duration becomes available |
| onSeeking | time: number | Fires when seeking starts |
| onBufferChange | bufferedTime: number | Fires when buffer progress changes (in seconds) |
| onBufferPercentageChange | percentage: number | Fires when buffer progress changes (0-100%) |
| onWaiting | none | Fires when playback stops due to buffering |
| onCanPlay | none | Fires when enough data is loaded to play |
| onLoadStart | none | Fires when browser starts loading audio |
| onStalled | none | Fires when network stalls |
| onRateChange | rate: number | Fires when playback speed changes |
| onVolumeChange | volume: number | Fires when volume changes (0-100) |
| onMuteChange | muted: boolean | Fires when mute state changes |
| onError | error: MediaError \| null | Fires when an error occurs |
🧩 Modules
AudioTracker supports optional modules to extend functionality.
Media Session Module
Integrates with browser's Media Session API for lock screen controls, media keys, and notification center integration.
import AudioTracker, { mediaSessionModule } from "audio-tracker";
const tracker = new AudioTracker("/audio.mp3");
tracker.use(mediaSessionModule);
// Update metadata dynamically
tracker.updateMediaSessionMetadata?.({
title: "New Song",
artist: "Artist Name",
artwork: [{ src: "/cover.jpg", sizes: "512x512", type: "image/jpeg" }],
});View Media Session API Documentation →
Timestamp Module
Track audio segments, subsegments, and speakers in real-time. Perfect for podcasts, interviews, and chaptered content.
import AudioTracker, { timestampModule } from "audio-tracker";
const tracker = new AudioTracker("/podcast.mp3", {
timestamp: {
segments: [
{
id: "intro",
label: "intro"
start: 0,
end: 30,
order: 1,
speaker: { id: "host", name: "Host Name" },
text: "Introduction",
},
],
gapBehavior: "persist-previous",
},
});
tracker.use(timestampModule);
tracker.init({
onSegmentChange: (segment) => console.log("Segment:", segment),
onSpeakerChange: (speaker) => console.log("Speaker:", speaker),
});
// Seek to segment by ID
tracker.seekToSegmentById?.("intro");View Timestamp API Documentation →
💡 Usage Examples
Basic Usage
import AudioTracker from "audio-tracker";
const tracker = new AudioTracker("/music.mp3");
tracker.init({
onDurationChange: (duration) => {
console.log(`Total duration: ${duration} seconds`);
},
onTimeUpdate: (currentTime) => {
console.log(`Current time: ${currentTime} seconds`);
},
});
tracker.play();React Integration
import React, { useEffect, useRef, useState } from "react";
import AudioTracker from "audio-tracker";
function AudioPlayer({ audioUrl }: { audioUrl: string }) {
const trackerRef = useRef<AudioTracker | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [volume, setVolume] = useState(100);
useEffect(() => {
trackerRef.current = new AudioTracker(audioUrl, {
preload: "metadata",
volume: 100,
});
trackerRef.current.init({
onPlay: () => setIsPlaying(true),
onPause: () => setIsPlaying(false),
onTimeUpdate: (time) => setCurrentTime(time),
onDurationChange: (dur) => setDuration(dur),
onVolumeChange: (vol) => setVolume(vol),
});
return () => trackerRef.current?.destroy();
}, [audioUrl]);
const handlePlayPause = async () => {
await trackerRef.current?.togglePlay();
};
const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
const time = parseFloat(e.target.value);
trackerRef.current?.seekTo(time);
};
return (
<div>
<button onClick={handlePlayPause}>
{isPlaying ? "⏸️ Pause" : "▶️ Play"}
</button>
<div>
{trackerRef.current?.formatTime(currentTime)} /
{trackerRef.current?.formatTime(duration)}
</div>
<input
type="range"
min={0}
max={duration}
value={currentTime}
onChange={handleSeek}
step={0.1}
/>
</div>
);
}
export default AudioPlayer;Vue Integration
<template>
<div class="audio-player">
<button @click="togglePlayPause">
{{ isPlaying ? "⏸️ Pause" : "▶️ Play" }}
</button>
<div>{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</div>
<input
type="range"
:min="0"
:max="duration"
v-model="currentTime"
@input="handleSeek"
/>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
import AudioTracker from "audio-tracker";
const props = defineProps<{ audioUrl: string }>();
let tracker: AudioTracker | null = null;
const isPlaying = ref(false);
const currentTime = ref(0);
const duration = ref(0);
onMounted(() => {
tracker = new AudioTracker(props.audioUrl, { preload: "metadata" });
tracker.init({
onPlay: () => (isPlaying.value = true),
onPause: () => (isPlaying.value = false),
onTimeUpdate: (time) => (currentTime.value = time),
onDurationChange: (dur) => (duration.value = dur),
});
});
onUnmounted(() => tracker?.destroy());
const togglePlayPause = async () => {
await tracker?.togglePlay();
};
const handleSeek = (e: Event) => {
const time = parseFloat((e.target as HTMLInputElement).value);
tracker?.seekTo(time);
};
const formatTime = (seconds: number) => {
return tracker?.formatTime(seconds) || "0:00";
};
</script>Using Existing Audio Element
const audioElement = document.getElementById("myAudio") as HTMLAudioElement;
const tracker = new AudioTracker(audioElement);
tracker.init({
onPlay: () => console.log("Playing from existing element"),
});🌐 Browser Support
Core Audio Features
✅ Full Support - All playback, volume, and speed controls
| Browser | Minimum Version | | --------------- | --------------- | | Chrome | 57+ | | Firefox | 52+ | | Safari | 11+ | | Edge | 79+ | | Opera | 44+ | | iOS Safari | 11+ | | Android Browser | 67+ |
Media Session API
⚠️ Progressive Enhancement - Gracefully degrades on older browsers
| Browser | Media Session Support | | --------------- | --------------------- | | Chrome | ✅ 73+ | | Firefox | ✅ 82+ | | Safari | ⚠️ 15+ (partial) | | Edge | ✅ 79+ | | Opera | ✅ 60+ | | iOS Safari | ⚠️ 15+ (partial) | | Android Browser | ✅ 73+ |
🐛 Common Issues
Issue: Audio doesn't play on mobile
Solution: Mobile browsers require user interaction before playing audio.
// ❌ Won't work on mobile without user interaction
tracker.play();
// ✅ Works - triggered by user click
button.addEventListener("click", () => {
tracker.play();
});Issue: TypeScript errors
Solution: Make sure you're using TypeScript 4.0+
npm install typescript@latest🤝 Contributing
Contributions are welcome! Please follow these steps:
Development Setup
# Clone the repository
git clone [email protected]:tvicky7x/audio-tracker.git
cd audio-tracker
# Install dependencies
npm install
# Build the project
npm run buildGuidelines
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Build and test:
npm run build - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
📝 Repository
- GitHub: https://github.com/tvicky7x/audio-tracker
- Issues: https://github.com/tvicky7x/audio-tracker/issues
- npm: https://www.npmjs.com/package/audio-tracker
- Demo: https://tvicky7x.github.io/audio-tracker/
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2025 T Vicky
💖 Support
If you find this package helpful, please consider:
- ⭐ Starring the repository on GitHub
- 🐛 Reporting bugs and issues
- 💡 Suggesting new features
- 📖 Improving documentation
- 🔀 Contributing code
Made with ❤️ by T Vicky
Repository: github.com/tvicky7x/audio-tracker
