Custom Audio Player Component
A stylish and modern custom audio player component. It features album art, track information, a seekable progress bar, play/pause controls, and a volume slider, all designed to be fully responsive
LTR
RTL
<div class="max-w-md w-full">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-6 text-center rtl:text-right">Custom Audio Player</h1>
<div
x-data="{
isPlaying: false,
isMuted: false,
volume: 0.7,
currentTime: 0,
duration: 0,
progress: 0,
currentTrack: {
title: 'Summer Vibes',
artist: 'Ocean Waves',
src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'
},
init() {
this.duration = this.$refs.audio.duration || 0;
},
togglePlay() {
if (this.$refs.audio.paused) {
this.$refs.audio.play();
this.isPlaying = true;
} else {
this.$refs.audio.pause();
this.isPlaying = false;
}
},
updateProgress() {
this.currentTime = this.$refs.audio.currentTime;
this.duration = this.$refs.audio.duration || 0;
this.progress = this.duration > 0 ? (this.currentTime / this.duration) * 100 : 0;
},
seek(event) {
const rect = this.$refs.progressBar.getBoundingClientRect();
const percent = (event.clientX - rect.left) / rect.width;
this.$refs.audio.currentTime = percent * this.duration;
},
toggleMute() {
this.isMuted = !this.isMuted;
this.$refs.audio.muted = this.isMuted;
if (this.isMuted) {
this.$refs.audio.volume = 0;
} else {
this.$refs.audio.volume = this.volume;
}
},
updateVolume() {
this.$refs.audio.volume = this.volume;
this.isMuted = this.volume === 0;
},
formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
},
onAudioEnd() {
this.isPlaying = false;
this.$refs.audio.currentTime = 0; // Reset to start
}
}"
x-init="init"
@keydown.window.space.prevent="togglePlay"
class="bg-white dark:bg-gradient-to-br dark:from-slate-800 dark:to-slate-950 rounded-xl shadow-2xl p-6 overflow-hidden"
>
<!-- Hidden audio element -->
<audio
x-ref="audio"
@timeupdate="updateProgress"
@loadedmetadata="init"
@ended="onAudioEnd"
preload="metadata"
class="hidden"
>
<source src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" type="audio/mp3">
Your browser does not support the audio element.
</audio>
<!-- Album Art & Title -->
<div class="flex items-center space-x-4 rtl:space-x-reverse mb-6">
<div class="w-16 h-16 bg-blue-500 rounded-lg overflow-hidden shadow-lg">
<img src="https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
alt="Album Art" class="w-full h-full object-cover">
</div>
<div class="flex-1">
<h2 class="text-gray-900 dark:text-white font-semibold text-lg" x-text="currentTrack.title">Song Title</h2>
<p class="text-gray-500 dark:text-gray-400" x-text="currentTrack.artist">Artist Name</p>
</div>
</div>
<!-- Progress Bar -->
<div class="w-full mb-4">
<div
x-ref="progressBar"
@click="seek($event)"
class="relative w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden cursor-pointer group"
>
<div
class="absolute top-0 left-0 h-full bg-blue-500 rounded-full"
:style="`width: ${progress}%`"
></div>
<div
class="absolute top-1/2 w-4 h-4 bg-blue-500 rounded-full transform -translate-x-1/2 -translate-y-1/2 shadow-lg opacity-0 group-hover:opacity-100 transition-opacity"
:style="`left: ${progress}%`"
></div>
</div>
</div>
<!-- Time Display -->
<div class="flex justify-between text-gray-500 dark:text-gray-400 text-sm mb-6 rtl:flex-row-reverse">
<span x-text="formatTime(currentTime)">0:00</span>
<span x-text="formatTime(duration)">0:00</span>
</div>
<!-- Controls -->
<div class="flex items-center justify-between">
<!-- Play/Pause Button -->
<button
@click="togglePlay"
class="text-white bg-blue-600 hover:bg-blue-500 transition-colors rounded-full w-16 h-16 flex items-center justify-center shadow-lg"
>
<!-- Play Icon (Solid) -->
<svg x-show="!isPlaying" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M4.5 5.653c0-1.426 1.529-2.33 2.779-1.643l11.54 6.648c1.295.742 1.295 2.545 0 3.286L7.279 20.99c-1.25.717-2.779-.217-2.779-1.643V5.653z" clip-rule="evenodd" />
</svg>
<!-- Pause Icon (Solid) -->
<svg x-show="isPlaying" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6 ml-1" style="display: none;">
<path fill-rule="evenodd" d="M6.75 5.25a.75.75 0 00-.75.75v12a.75.75 0 00.75.75h.75a.75.75 0 00.75-.75V6a.75.75 0 00-.75-.75H6.75zm5.25 0a.75.75 0 00-.75.75v12a.75.75 0 00.75.75h.75a.75.75 0 00.75-.75V6a.75.75 0 00-.75-.75h-.75z" clip-rule="evenodd" />
</svg>
</button>
<!-- Volume Control -->
<div class="flex items-center space-x-2 rtl:space-x-reverse">
<button
@click="toggleMute"
class="text-gray-500 dark:text-gray-400 hover:text-blue-400 transition-colors"
:class="{ 'text-blue-400': isMuted }"
>
<!-- Volume On Icon -->
<svg x-show="!isMuted && volume > 0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z" />
</svg>
<!-- Volume Mute Icon -->
<svg x-show="isMuted || volume == 0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" style="display: none;">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 9.75L19.5 12m0 0l2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z" />
</svg>
</button>
<div class="relative w-[100px] h-[6px] bg-gray-200 dark:bg-gray-700 rounded-full">
<div
class="absolute top-0 left-0 h-full bg-blue-500 rounded-full w-full origin-left"
:style="`transform: scaleX(${volume})`"
></div>
<div
class="absolute top-1/2 w-3 h-3 bg-blue-500 rounded-full transform -translate-x-1/2 -translate-y-1/2 shadow-lg z-10"
:style="`left: ${volume * 100}%`"
></div>
<input
type="range"
min="0"
max="1"
step="0.01"
x-model="volume"
@input="updateVolume"
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer"
>
</div>
</div>
</div>
</div>
<div class="mt-6 text-center text-gray-500 dark:text-gray-400">
<p>Click the play button to start audio. Use spacebar to toggle play/pause.</p>
<p class="mt-2">Click on the progress bar to seek to a specific time.</p>
<p class="mt-2">Audio: SoundHelix Song 1 (SoundHelix)</p>
</div>
</div>