Music is a universal language that connects us all. Building a custom music player app is a fantastic way to practice front-end web development and create something both functional and fun. In this tutorial, we'll walk you through building a Music Player App with essential features:
- Play/Pause Controls
- Next/Previous Track navigation
- Progress Bar with seeking
- Volume Control slider
- Shuffle & Repeat toggles
- Interactive Queue display
- Responsive Design for all screen sizes
Let’s dive in!
Step 1: Set Up Your HTML Structure
We'll start with the basic markup that houses the player controls, progress bar, volume slider, and track list.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Music Player</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div class="music-player">
<div class="player-header">
<h1>Music Player</h1>
<div class="player-controls-top">
<button id="shuffle-btn" class="control-btn" title="Shuffle">
<i class="fas fa-random"></i>
</button>
<button id="repeat-btn" class="control-btn" title="Repeat">
<i class="fas fa-redo"></i>
</button>
</div>
</div>
<div class="album-art">
<img id="album-art" src="https://via.placeholder.com/300" alt="Album Art">
</div>
<div class="song-info">
<h2 id="song-title">Song Title</h2>
<p id="artist-name">Artist Name</p>
</div>
<div class="progress-container">
<span id="current-time">0:00</span>
<input type="range" id="progress-bar" value="0">
<span id="duration">0:00</span>
</div>
<div class="player-controls">
<button id="prev-btn" class="control-btn" title="Previous">
<i class="fas fa-step-backward"></i>
</button>
<button id="play-btn" class="control-btn play-pause" title="Play">
<i class="fas fa-play"></i>
</button>
<button id="next-btn" class="control-btn" title="Next">
<i class="fas fa-step-forward"></i>
</button>
</div>
<div class="volume-control">
<i class="fas fa-volume-down"></i>
<input type="range" id="volume-slider" min="0" max="1" step="0.01" value="0.7">
<i class="fas fa-volume-up"></i>
</div>
<div class="queue-container">
<h3>Up Next</h3>
<ul id="song-queue">
<!-- Songs will be added here dynamically -->
</ul>
</div>
</div>
<audio id="audio-player"></audio>
<script src="script.js"></script>
</body>
</html>
Step 2: Style the Player with CSS
Let's make the player visually appealing and responsive.
:root {
--primary-color: #4a76a8;
--secondary-color: #f5f5f5;
--text-color: #333;
--highlight-color: #6ba1d8;
--shadow-color: rgba(0, 0, 0, 0.2);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--secondary-color);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.music-player {
background-color: white;
border-radius: 15px;
box-shadow: 0 10px 30px var(--shadow-color);
width: 100%;
max-width: 400px;
padding: 25px;
text-align: center;
}
.player-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.player-header h1 {
font-size: 1.5rem;
color: var(--primary-color);
}
.control-btn {
background: none;
border: none;
color: var(--primary-color);
font-size: 1.2rem;
cursor: pointer;
padding: 8px;
border-radius: 50%;
transition: all 0.3s ease;
}
.control-btn:hover {
background-color: var(--secondary-color);
transform: scale(1.1);
}
.album-art {
margin: 0 auto 20px;
width: 200px;
height: 200px;
border-radius: 50%;
overflow: hidden;
box-shadow: 0 5px 15px var(--shadow-color);
animation: rotateAlbumArt 20s linear infinite;
animation-play-state: paused;
}
.album-art img {
width: 100%;
height: 100%;
object-fit: cover;
}
.music-player.playing .album-art {
animation-play-state: running;
}
.song-info {
margin-bottom: 20px;
}
.song-info h2 {
font-size: 1.5rem;
margin-bottom: 5px;
}
.song-info p {
color: #666;
font-size: 1rem;
}
.progress-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.progress-container span {
font-size: 0.8rem;
color: #666;
width: 40px;
}
#progress-bar {
flex-grow: 1;
margin: 0 10px;
height: 6px;
-webkit-appearance: none;
appearance: none;
background-color: #ddd;
border-radius: 3px;
cursor: pointer;
}
#progress-bar::-webkit-slider-thumb {
-webkit-appearance: none;
width: 15px;
height: 15px;
background-color: var(--primary-color);
border-radius: 50%;
cursor: pointer;
}
.player-controls {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}
.play-pause {
background-color: var(--primary-color);
color: white;
width: 50px;
height: 50px;
border-radius: 50%;
margin: 0 20px;
font-size: 1.5rem;
display: flex;
justify-content: center;
align-items: center;
}
.play-pause:hover {
background-color: var(--highlight-color);
transform: scale(1.1);
}
.volume-control {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
}
.volume-control i {
color: var(--primary-color);
margin: 0 10px;
}
#volume-slider {
width: 100px;
height: 5px;
-webkit-appearance: none;
appearance: none;
background-color: #ddd;
border-radius: 3px;
cursor: pointer;
}
#volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
background-color: var(--primary-color);
border-radius: 50%;
cursor: pointer;
}
.queue-container {
text-align: left;
margin-top: 20px;
max-height: 200px;
overflow-y: auto;
}
.queue-container h3 {
color: var(--primary-color);
margin-bottom: 10px;
font-size: 1rem;
}
#song-queue {
list-style-type: none;
}
#song-queue li {
padding: 8px 10px;
border-bottom: 1px solid #eee;
cursor: pointer;
transition: background-color 0.2s;
}
#song-queue li:hover {
background-color: var(--secondary-color);
}
#song-queue li.playing {
color: var(--primary-color);
font-weight: bold;
}
.active {
color: var(--highlight-color);
}
@keyframes rotateAlbumArt {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Responsive Design */
@media (max-width: 480px) {
.music-player {
padding: 15px;
}
.album-art {
width: 150px;
height: 150px;
}
.song-info h2 {
font-size: 1.2rem;
}
.play-pause {
width: 45px;
height: 45px;
margin: 0 15px;
}
}
Step 3: Add JavaScript Functionality
Now the fun part — powering up the player with JavaScript.
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const audioPlayer = document.getElementById('audio-player');
const playBtn = document.getElementById('play-btn');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const progressBar = document.getElementById('progress-bar');
const currentTimeEl = document.getElementById('current-time');
const durationEl = document.getElementById('duration');
const volumeSlider = document.getElementById('volume-slider');
const shuffleBtn = document.getElementById('shuffle-btn');
const repeatBtn = document.getElementById('repeat-btn');
const songTitle = document.getElementById('song-title');
const artistName = document.getElementById('artist-name');
const albumArt = document.getElementById('album-art');
const songQueue = document.getElementById('song-queue');
const musicPlayer = document.querySelector('.music-player');
// Music library
const songs = [
{
title: "Blinding Lights",
artist: "The Weeknd",
src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
cover: "https://i.scdn.co/image/ab67616d0000b2734718e2b124f79258be7bc452"
},
{
title: "Save Your Tears",
artist: "The Weeknd",
src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3",
cover: "https://i.scdn.co/image/ab67616d0000b2738863bc11d2aa12b54f5aeb36"
},
{
title: "Starboy",
artist: "The Weeknd ft. Daft Punk",
src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3",
cover: "https://i.scdn.co/image/ab67616d0000b2734718e2b124f79258be7bc452"
},
{
title: "Take My Breath",
artist: "The Weeknd",
src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-4.mp3",
cover: "https://i.scdn.co/image/ab67616d0000b2734718e2b124f79258be7bc452"
},
{
title: "Die For You",
artist: "The Weeknd",
src: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-5.mp3",
cover: "https://i.scdn.co/image/ab67616d0000b2734718e2b124f79258be7bc452"
}
];
// Player state
let currentSongIndex = 0;
let isPlaying = false;
let isShuffled = false;
let isRepeated = false;
let originalQueue = [...songs];
let shuffledQueue = [];
// Initialize player
function initPlayer() {
loadSong(currentSongIndex);
updateQueue();
}
// Load song
function loadSong(index) {
const song = songs[index];
songTitle.textContent = song.title;
artistName.textContent = song.artist;
albumArt.src = song.cover;
audioPlayer.src = song.src;
// Update active song in queue
const queueItems = songQueue.querySelectorAll('li');
queueItems.forEach(item => item.classList.remove('playing'));
queueItems[index].classList.add('playing');
}
// Play song
function playSong() {
isPlaying = true;
musicPlayer.classList.add('playing');
playBtn.innerHTML = '<i class="fas fa-pause"></i>';
audioPlayer.play();
}
// Pause song
function pauseSong() {
isPlaying = false;
musicPlayer.classList.remove('playing');
playBtn.innerHTML = '<i class="fas fa-play"></i>';
audioPlayer.pause();
}
// Next song
function nextSong() {
if (isRepeated) {
audioPlayer.currentTime = 0;
playSong();
return;
}
currentSongIndex++;
if (currentSongIndex >= songs.length) {
currentSongIndex = 0;
}
loadSong(currentSongIndex);
if (isPlaying) {
playSong();
}
}
// Previous song
function prevSong() {
if (audioPlayer.currentTime > 3) {
audioPlayer.currentTime = 0;
return;
}
currentSongIndex--;
if (currentSongIndex < 0) {
currentSongIndex = songs.length - 1;
}
loadSong(currentSongIndex);
if (isPlaying) {
playSong();
}
}
// Update progress bar
function updateProgressBar() {
const { currentTime, duration } = audioPlayer;
const progressPercent = (currentTime / duration) * 100;
progressBar.value = progressPercent;
// Update time displays
currentTimeEl.textContent = formatTime(currentTime);
if (duration) {
durationEl.textContent = formatTime(duration);
}
}
// Set progress bar
function setProgressBar(e) {
const width = this.clientWidth;
const clickX = e.offsetX;
const duration = audioPlayer.duration;
audioPlayer.currentTime = (clickX / width) * duration;
}
// Format time
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
}
// Set volume
function setVolume() {
audioPlayer.volume = this.value;
}
// Toggle shuffle
function toggleShuffle() {
isShuffled = !isShuffled;
shuffleBtn.classList.toggle('active');
if (isShuffled) {
// Create a shuffled queue
shuffledQueue = [...songs];
for (let i = shuffledQueue.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffledQueue[i], shuffledQueue[j]] = [shuffledQueue[j], shuffledQueue[i]];
}
songs.splice(0, songs.length, ...shuffledQueue);
} else {
// Restore original queue
songs.splice(0, songs.length, ...originalQueue);
}
// Update current song index
const currentSong = originalQueue[currentSongIndex];
currentSongIndex = songs.findIndex(song => song.title === currentSong.title);
updateQueue();
}
// Toggle repeat
function toggleRepeat() {
isRepeated = !isRepeated;
repeatBtn.classList.toggle('active');
}
// Update song queue
function updateQueue() {
songQueue.innerHTML = '';
songs.forEach((song, index) => {
const li = document.createElement('li');
li.textContent = `${song.title} - ${song.artist}`;
li.addEventListener('click', () => {
currentSongIndex = index;
loadSong(currentSongIndex);
if (isPlaying) {
playSong();
}
});
if (index === currentSongIndex) {
li.classList.add('playing');
}
songQueue.appendChild(li);
});
}
// Event listeners
playBtn.addEventListener('click', () => {
isPlaying ? pauseSong() : playSong();
});
prevBtn.addEventListener('click', prevSong);
nextBtn.addEventListener('click', nextSong);
audioPlayer.addEventListener('timeupdate', updateProgressBar);
audioPlayer.addEventListener('ended', nextSong);
audioPlayer.addEventListener('loadedmetadata', updateProgressBar);
progressBar.addEventListener('click', setProgressBar);
volumeSlider.addEventListener('input', setVolume);
shuffleBtn.addEventListener('click', toggleShuffle);
repeatBtn.addEventListener('click', toggleRepeat);
// Initialize player
initPlayer();
});
How It Works
- Play/Pause Controls: The central play/pause button toggles audio playback and updates the button icon.
- Next/Previous Tracks: Buttons let you navigate forward or backward through the playlist.
- Progress Bar: A clickable progress bar displays how far the track has played and lets users seek to any part by clicking.
- Volume Slider: Adjust the volume smoothly with a slider.
- Shuffle & Repeat: Toggle shuffle for random playback or repeat to loop the current track.
- Interactive Queue: A list shows upcoming tracks; clicking any track jumps to it immediately.
- Responsive Design: The CSS ensures the player looks good and works well on desktops, tablets, and phones.
Try It Yourself!
Feel free to copy the code snippets above, tweak the styles, add your own tracks, and create your unique music experience.
Happy coding and happy listening! 🎵