audio-tracker
Version:
A headless JavaScript library that gives you full control over web audio — playback, tracking, and Media Session integration made simple.
651 lines (498 loc) • 22.5 kB
Markdown
# 🎵 audio-tracker
> A headless JavaScript library that gives you full control over web audio — playback, tracking, and Media Session integration made simple.
[](https://www.npmjs.com/package/audio-tracker)
[](https://www.npmjs.com/package/audio-tracker)
[](https://github.com/tvicky7x/audio-tracker/blob/main/LICENSE)
[](https://www.typescriptlang.org/)
**[🎮 Live Demo](https://tvicky7x.github.io/audio-tracker/)** | **[📖 Documentation](https://github.com/tvicky7x/audio-tracker#-api-documentation)** | **[💾 Installation](https://github.com/tvicky7x/audio-tracker#-installation)**
## 📋 Table of Contents
- [Features](#-features)
- [Installation](#-installation)
- [Quick Start](#-quick-start)
- [API Documentation](#-api-documentation)
- [Modules](#-modules)
- [Usage Examples](#-usage-examples)
- [Browser Support](#-browser-support)
- [Common Issues](#-common-issues)
- [Contributing](#-contributing)
- [License](#-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
```bash
npm install audio-tracker
```
### yarn
```bash
yarn add audio-tracker
```
### pnpm
```bash
pnpm add audio-tracker
```
### CDN
```
<script src="https://unpkg.com/audio-tracker@1.2.3/dist/index.js"></script>
```
## 🚀 Quick Start
```typescript
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
```typescript
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
```typescript
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
```typescript
// 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.
```typescript
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.
```typescript
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 →](https://github.com/tvicky7x/audio-tracker/blob/module-system/src/modules/media-session.md)**
### Timestamp Module
Track audio segments, subsegments, and speakers in real-time. Perfect for podcasts, interviews, and chaptered content.
```typescript
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 →](https://github.com/tvicky7x/audio-tracker/blob/module-system/src/modules/timestamp.md)**
## 💡 Usage Examples
### Basic Usage
```typescript
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
```typescript
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
```vue
<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
```typescript
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.
```typescript
// ❌ 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+
```bash
npm install typescript@latest
```
## 🤝 Contributing
Contributions are welcome! Please follow these steps:
### Development Setup
```bash
# Clone the repository
git clone git@github.com:tvicky7x/audio-tracker.git
cd audio-tracker
# Install dependencies
npm install
# Build the project
npm run build
```
### Guidelines
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/amazing-feature`
3. Make your changes
4. Build and test: `npm run build`
5. Commit your changes: `git commit -m 'Add amazing feature'`
6. Push to the branch: `git push origin feature/amazing-feature`
7. Open a Pull Request
## 📝 Repository
- **GitHub:** [https://github.com/tvicky7x/audio-tracker](https://github.com/tvicky7x/audio-tracker)
- **Issues:** [https://github.com/tvicky7x/audio-tracker/issues](https://github.com/tvicky7x/audio-tracker/issues)
- **npm:** [https://www.npmjs.com/package/audio-tracker](https://www.npmjs.com/package/audio-tracker)
- **Demo:** [https://tvicky7x.github.io/audio-tracker/](https://tvicky7x.github.io/audio-tracker/)
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](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](https://github.com/tvicky7x/audio-tracker)