am-audio-player-widget
v1.1.9
Published
A standalone and embeddable audio player widget, built for **Audio Modeling** with **React + TypeScript + Vite**. The widget can be integrated into any website via UMD bundle and supports multiple audio sources including Audius platform.
Readme
🎵 AM Audio Player Widget
A standalone and embeddable audio player widget, built for Audio Modeling with React + TypeScript + Vite. The widget can be integrated into any website via UMD bundle and supports multiple audio sources including Audius platform.
🚀 Key Features
- 🎯 Standalone Widget: Zero external dependencies, React bundled internally
- 🌐 UMD Universal: Compatible with any website or CMS (WordPress, etc.)
- 🎨 CSS Isolation: Shadow DOM + Tailwind CSS v4 with PostCSS transformations
- 🔊 Multi-Source: Audius API support with extensible architecture
- 📱 Responsive: Spotify-like design for desktop and mobile
- ⚡ Performance: Optimized bundle with Vite + Rollup
- 🎮 Programmatic API: Control playback via JavaScript API
- 🧩 Multiple Modes: Bar, compact, and full player layouts
- 🎵 Enhanced UX: Volume persistence, smooth seeking, accessibility support
- 🎛 Smart Progress Bars: Multiple styles including minimal always-visible indicator
🎨 Player Modes & Progress Bar Styles
Player Modes
| Mode | Description | Use Case |
|----------|-------------|----------|
| bar | Horizontal minimal bar with mobile/desktop layouts | Headers, navbars, sticky players |
| compact | Reduced version | Sidebars, footers |
| full | Complete player with all controls | Dedicated pages |
Progress Bar Styles
| Style | Description | Features |
|-------|-------------|----------|
| default | Standard progress slider with hover effects | Time display, click-to-seek, hover thumb |
| waveform | Visual waveform representation | Audio visualization, interactive seeking |
| minimal | Thin bottom bar indicator | Always-visible progress, space-efficient |
🛠 Tech Stack
- Frontend: React 19 + TypeScript
- Build Tool: Vite 7 + Rollup
- Styling: Tailwind CSS v4 with CSS isolation via Shadow DOM
- CSS Processing: Custom PostCSS plugin for
@propertytransformation - Audio: react-use-audio-player with custom hooks
- Data Fetching: TanStack Query v5
- UI Components: Radix UI + shadcn/ui
- Icons: Lucide React
- Package Manager: pnpm
📦 Installation*
*CDN (Recommended for WordPress/CMS)
<!DOCTYPE html>
<html>
<head>
<title>My Website</title>
<style>
/* Modern animations for player visibility */
.am-player-sticky {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
transform: translateY(100%);
opacity: 0;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.am-player-sticky.show {
transform: translateY(0);
opacity: 1;
}
.am-mini-trigger {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1001;
width: 56px;
height: 56px;
background: linear-gradient(135deg, #8b5cf6, #ec4899);
border-radius: 50%;
border: none;
cursor: pointer;
display: none;
align-items: center;
justify-content: center;
box-shadow: 0 4px 20px rgba(139, 92, 246, 0.3);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.am-mini-trigger:hover {
transform: scale(1.1);
box-shadow: 0 6px 25px rgba(139, 92, 246, 0.4);
}
</style>
</head>
<body>
<!-- Player container -->
<div class="am-player-sticky" role="region" aria-label="Music Player">
<div id="am-footer-audio-player"></div>
</div>
<!-- Mini trigger button -->
<button class="am-mini-trigger" aria-label="Open music player" title="Open Player">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M8 5v14l11-7z" fill="white"/>
</svg>
</button>
<!-- Load widget -->
<script src="https://unpkg.com/[email protected]/dist/am-audio-player-widget.js"></script>
<script>
(function() {
'use strict';
let widget = null;
let isPlayerVisible = false;
const elements = {
player: document.querySelector('.am-player-sticky'),
trigger: document.querySelector('.am-mini-trigger'),
container: document.querySelector('#am-footer-audio-player')
};
// Get configuration from data attributes, URL params, or globals
function getConfig() {
const container = elements.container;
const urlParams = new URLSearchParams(window.location.search);
const trackId = container?.dataset?.trackId ||
urlParams.get('trackId') ||
window.trackId;
const playlistId = container?.dataset?.playlistId ||
urlParams.get('playlistId') ||
window.playlistId;
return {
trackId,
playlistId,
mode: 'bar',
progressBarStyle: 'minimal', // 🎯 NEW: minimal style by default
showCloseButton: true,
showVolume: true,
showProgressBar: true,
onClose: hidePlayer
};
}
function showPlayer() {
if (elements.player && !isPlayerVisible) {
elements.player.style.display = 'block';
requestAnimationFrame(() => {
elements.player.classList.add('show');
});
if (elements.trigger) elements.trigger.style.display = 'none';
isPlayerVisible = true;
}
}
function hidePlayer() {
if (elements.player && isPlayerVisible) {
// Pause music when closing
if (widget && widget.togglePlay && widget.isPlaying) {
widget.togglePlay();
}
elements.player.classList.remove('show');
setTimeout(() => {
elements.player.style.display = 'none';
}, 400);
if (elements.trigger) elements.trigger.style.display = 'flex';
isPlayerVisible = false;
}
}
function initWidget() {
const config = getConfig();
// Only initialize if we have trackId or playlistId
if (!config.trackId && !config.playlistId) {
console.warn('[AM Widget] No trackId or playlistId provided. Widget not initialized.');
return;
}
try {
widget = AMAudioPlayerWidget.init('#am-footer-audio-player', config);
showPlayer();
// Setup mini trigger
if (elements.trigger) {
elements.trigger.addEventListener('click', showPlayer);
}
} catch (error) {
console.error('[AM Widget] Initialization failed:', error);
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initWidget);
} else {
initWidget();
}
})();
</script>
</body>
</html>NPM/Package Manager
npm install @andreadf/am-audio-player-widget
# or
pnpm add @andreadf/am-audio-player-widgetimport AMAudioPlayerWidget from '@andreadf/am-audio-player-widget';
const widget = AMAudioPlayerWidget.init('#player', {
trackId: 'XB2lGlO',
mode: 'bar',
progressBarStyle: 'minimal',
showVolume: true,
showProgressBar: true
});⚙️ Widget Configuration
interface WidgetConfig {
// Source configuration
trackId?: string // Audius track ID
playlistId?: string // Audius playlist ID
tracks?: AMTrack[] // Custom tracks array
// Player configuration
mode?: "bar" | "compact" | "full"
progressBarStyle?: "default" | "waveform" | "minimal" // 🎯 NEW: minimal option
// UI Configuration
showVolume?: boolean // Show/hide volume control (default: true)
showProgressBar?: boolean // Show/hide progress bar (default: true)
showCloseButton?: boolean // Show/hide close button (default: false)
onClose?: () => void // Close button callback
// Asset URLs
backgroundUrl?: string // Custom background image
noAlbumArtUrl?: string // Placeholder cover image
// Styling
className?: string // Additional CSS classes
}🎛 API Methods
const player = AMAudioPlayerWidget.init('#player', config);
// Playback control
player.togglePlay(); // Toggle play/pause
console.log(player.isPlaying); // Get playback state
// Configuration management
player.update({
mode: 'compact',
trackId: 'newTrackId',
progressBarStyle: 'minimal', // 🎯 NEW: Switch progress bar style
showVolume: false
});
// Get current configuration
const currentConfig = player.getConfig();
// Cleanup
player.destroy();
// Version info
console.log(AMAudioPlayerWidget.version); // "1.0.0"🎨 Features
Shadow DOM CSS Isolation
- Complete CSS encapsulation using Shadow DOM
- Custom PostCSS plugin converts Tailwind v4
@propertydeclarations - No style conflicts with host website
Spotify-like Design
- Mobile Layout: Single row with album art, track info, and compact controls
- Desktop Layout: Three-column grid with centered controls and progress bar
- Mini Progress Bar: Always visible thin progress indicator at bottom (NEW!)
- Hover Effects: Scale transforms, color changes, and smooth transitions
Enhanced UX
- Volume Persistence: Settings saved in localStorage across page refreshes
- Smart Seeking: Progress bar updates smoothly during playback and when paused
- Anti-Flash Logic: Prevents visual glitches when seeking while paused
- Accessibility: Full ARIA labels and keyboard navigation support
- Responsive: Adaptive layouts for mobile/desktop with media queries
Progress Bar Styles
- Default: Interactive slider with time display and hover effects
- Waveform: Visual audio representation with seeking capabilities
- Minimal: Thin bottom indicator bar that's always visible (NEW!)
Modern Animations
- CSS transforms with cubic-bezier easing
- Backdrop blur effects and glassmorphism
🏗 Development
Available Commands
# Development with demo page
pnpm dev # Start dev server on http://localhost:5173
# Build for production
pnpm build # Create UMD bundle in dist/
pnpm build:test # Build test mode
pnpm build:prod # Build production mode
pnpm build:debug # Build debug mode
# Quality checks
pnpm lint # ESLint check
pnpm preview # Preview local buildProject Structure
am-player/
├── src/
│ ├── components/
│ │ ├── controls/ # Playback controls (play/pause, volume)
│ │ ├── core/ # Main player logic
│ │ ├── layouts/ # Layout components (bar, compact, full)
│ │ ├── progress/ # Progress bars (default, waveform, minimal)
│ │ ├── ui/ # UI primitives (slider, button, etc.)
│ │ └── types/ # TypeScript definitions
│ ├── hooks/ # Custom React hooks (useAudioTime, etc.)
│ ├── api/ # Data fetching (Audius API)
│ ├── styles/ # Global CSS and Tailwind config
│ ├── Widget.tsx # Main widget entry point
│ └── main.tsx # Development demo page
├── postcss.config.ts # PostCSS with Shadow DOM transformation
├── vite.config.ts # Vite build configuration
└── README.md🔧 Technical Details
CSS in Shadow DOM
The widget uses a custom PostCSS plugin to transform Tailwind CSS v4 for Shadow DOM compatibility:
// Converts @property declarations to :root, :host selectors
@property --tw-gradient-from { initial-value: transparent; }
// Becomes:
:root, :host { --tw-gradient-from: transparent; }Performance Optimizations
- Smart Audio Hooks:
useAudioTimeoptimized for continuous playback monitoring - Conditional Updates: Progress bars only update when necessary
- Memoized Components: React.memo for heavy UI components
- localStorage Caching: Volume and settings persistence
- Efficient Bundling: Tree-shaking and code splitting with Vite
Audio Position Management
// Consolidated useAudioTime hook with smart seeking
const useAudioTime = (getPosition: () => number, active: boolean) => {
// Handles both playing and paused states
// Detects significant position changes (seeks)
// Optimizes requestAnimationFrame usage
};Progress Bar Architecture
- Default: Full-featured slider with time display
- Waveform: Visual audio representation
- Minimal: Thin Radix UI slider with subtle styling (NEW!)
📄 License
MIT License - see LICENSE file for details.
🤝 Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
Built with ❤️ by Audio Modeling Team
