create a music player banner

Create Your Own Music Player with HTML, CSS, and JavaScript

Ready to rock out with a custom music player you built yourself? In this tutorial, we'll guide you through the steps to create a simple yet functional music player using basic HTML, CSS and JavaScript. Let's get started!

Overview

This tutorial delves into the practical application of HTML, CSS, and JavaScript to construct a fully functional music player. Designed for both novice and intermediate web developers, the journey will equip you with valuable skills in:

  • DOM manipulation: Learn to dynamically control the presentation and behavior of audio elements within your web page.
  • JavaScript event handling: Implement intuitive playback controls, including play/pause, stop, next/previous track, and volume adjustment.
  • Basic audio APIs: Integrate JavaScript features to manage audio files, track duration, and playback progress.
  • CSS styling: Tailor the player's visual aesthetics to match your preferences, incorporating fonts, colors, and layout customization.

Beyond the technical skills, this project serves as a springboard for further exploration, enabling you to:

  • Implement advanced features: Enhance functionality with playlist management, search capabilities, custom audio effects, and integration with external music libraries.
  • Personalize the user experience: Craft unique themes, layouts, and interactive elements to reflect your individual style and preferences.
  • Gain deeper understanding of web technologies: Solidify your grasp of HTML, CSS, and JavaScript while applying them in a practical and engaging context.

Before we start there are a few things to note:

  • We will need to ask the user for permission to read their files and access the music folder. For the sake of simplicity, this example allows them to add music to their playlist manually by selecting various folders.

  • browse music
  • The selected files will be displayed in a playlist. The user can choose any song by clicking it.

  • playlist
  • Once the song has been clicked it will open a now playing screen and this will display some album art, song title and artist as well as other meta data.

  • now playing screen

Note that for the purpose of this tutorial we will only focus on one folder. If you want to add music from multiple folders, you will need to manually browse each folder and repeat the process.

Here's a preview of how our music player will look.

Now that we have an overview, let's start building our music player

1. Set Up the HTML Structure:

Our player will have 3 key sections.

  1. Option to browse music folders on their device
  2. A playlist to display a list of discovered songs
  3. A "Now playing" screen to display the song that's playing, some meta data and controls e.g. play/pause, next etc.

Create an HTML file and add the following code.

HTML
                        
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Asaqeni Audio Player</title>
    <link rel="stylesheet" href="styles.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.1.1/css/all.css">
  </head>
<body>
  <section class="section-browse">
    <img src="/images/cool-pointing-dude.png" alt="logo image"><br>
    <button id="select-files">Browse Music</button>
  </section>

  <section class="section-playlist">
    <div class="playlist-container">
      <button id="change-directory">Change Directory</button>
      <h2>Playlist</h2>
      <ul id="playlist">
        <!-- Available songs will be added here -->
      </ul>
    </div>
  </section>

  <section class="section-player" id="now-playing">
    <div class="player-header">
      <h2>Music Player</h2>
    </div>
    <img src="/images/b-boy-music-player-icon.jpg" alt="Album Art">

    <div class="track-info">
      <span class="track-title">Track Title</span><br>
      <span class="artist">Artist Name</span><br>
      <div class="time-stats-container">
        <span class="time-played">0:00</span>
        <span class="length">0:00</span>
      </div>
      <div class="progress-container">
        <div class="progress-bar">
          <div class="progress"></div>
        </div>
      </div>
      <input type="range" min="0" max="1" step="0.01" id="volume-slider" />
    </div>

    <div class="controls">
      <button class="playlist-icon"><i class="fas fa-list-alt"></i></button>
      <button class="prev"><i class="fas fa-step-backward"></i></button>
      <button class="play-pause" id="play-pause"><i class="fas fa-play"></i></button>
      <button class="next"><i class="fas fa-step-forward"></i></button>
      <button class="volume"><i class="fas fa-volume-up"></i></button>
    </div>

  </section>

  <script src="script.js"></script>

  </body>
</html>

We will be using Fontawesome to display icons in our app and thus we have added <link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.1.1/css/all.css">

We have also given our elements meaningful IDs and classes to style and manipulate them with CSS and JavaScript.

2. Style the Player with CSS:

Now that we have our layout, let's add some CSS to give it some cool styling. Create a CSS file and name it style.css. Inside the style.css file, add the following code.

style.css
                        
body {
  font-family: Arial, sans-serif;
  background-color: #f5f5f5;
  margin: 0;
  padding: auto;
}

section {
  background-color: white;
  border-radius: 8px;
  box-shadow: 5px 5px 10px #bdbdbd;
  margin: 30px auto;
  max-width: 500px;
  min-height: 70vh;
}

.section-browse {
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.section-browse img {
  width: 150px;
  height: 150px;
}

.section-browse button, .section-playlist button {
  background-color: #333;
  color: #fff;
  padding: 10px 20px;
  border: none;
  font-size: 16px;
  border-radius: 5px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
  cursor: pointer;
}

.section-playlist button {
  float: right;
}

.section-player {
  padding: 20px;
  height: calc(100vh - 15%);
  display: none;
  flex-direction: column;
  align-items: center;
}

.player-header {
  display: flex; /* Arrange elements horizontally */
  justify-content: space-between; /* Distribute space evenly between elements */
  align-items: center; /* Align elements vertically */
  max-width: 500px;
  margin: 12px auto;
}

.section-player img {
  width: 150px;
  height: 150px;
  border-radius: 50%;
  margin-bottom: 10px;
}

.track-info {
  text-align: center;
  margin-bottom: 10px;
}

.track-title {
  font-size: 18px;
  font-weight: bold;
}

.artist {
  font-size: 14px;
  color: #888;
}

.time-stats-container {
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
}

.progress-container {
  width: 100%;
}

.progress-bar {
  width: 100%;
  max-width: 500px;
  height: 5px;
  background-color: #ddd;
  border-radius: 5px;
  margin: 10px auto;
}

.progress {
  height: 5px;
  background-color: #4caf50;
  border-radius: 5px;
  width: 0%;
}

/* Volume slider */
#volume-slider {
  display: none; /* Hidden by default */
  width: 100%;
  max-width: 500px;
  -webkit-appearance: none; /* Remove default styling */
  background: #d3d3d3;
  border-radius: 5px;
  margin: 20px auto;
  height: 5px; /* Adjust as needed */
}

#volume-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 15px;
  height: 15px;
  background: #6c47ff;
  border-radius: 50%;
  cursor: pointer;
}

.controls {
  display: flex;
  justify-content: space-around;
  width: 80%;
  max-width: 500px;
  margin-top: 10px;
}

.controls button {
  border: none;
  background-color: transparent;
  cursor: pointer;
  outline: none;
  margin: 0 5px;
}

.playlist-icon {
  text-align: right;
  margin-left: auto;
}

.prev i, .next i, .playlist-icon i, .volume i {
  font-size: 18px;
  color: #333;
}

.play-pause i {
  font-size: 24px;
  background-color: lime;
  color: #333;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 48px;
  height: 48px;
  padding: auto;
  border-radius: 50%;
  box-shadow: 2px 2px 3px rgb(161, 161, 161);
}

i:hover {
  color: blue;
}

.section-playlist {
  display: none;
  padding: 20px;
  border-top: 1px solid #ddd;
}

.playlist-container {
  max-width: 500px;
  width: 90%;
  margin-left: auto;
  margin-right: auto;
}

.section-playlist h2 {
  margin-bottom: 10px;
  font-size: 16px;
}

.section-playlist ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.section-playlist li {
  padding: 10px 0;
  border-bottom: 1px solid #ddd;
  cursor: pointer;
}

.section-playlist li:hover {
  background-color: #eee;
}

/* Media queries for additional responsiveness (optional) */

@media only screen and (max-width: 480px) {
  .section-player img {
    width: 100px;
    height: 100px;
  }
}

3. Implement JavaScript Functionality:

Here's where the magic happens! Let's create a file and save it as style.js. Place this in the same folder as the HTML and style.css files. We will use JavaScript to:

  • Employ the File System Access API to prompt the user to select a folder
  • Scan the chosen folder for audio files
  • Create a dynamic playlist with song items
  • Handle user interactions with audio controls (play/pause, skip, previous)
  • Display song metadata, including fallback for files without metadata
  • Implement song progress tracking and display
  • Automatically play the next song when one ends
script.js
                        
// Get the DOM elements
const selectSection = document.querySelector(".section-browse");
const nowPlayingSection = document.getElementById("now-playing");
const playListSection = document.querySelector(".section-playlist");
const openPlayList = document.querySelector(".playlist-icon");
const playList = document.getElementById("playlist");
const trackTitle = document.querySelector(".track-title");
const artist = document.querySelector(".artist");
const songLength = document.querySelector(".length");
const timePlayed = document.querySelector(".time-played");
const progressBarElement = document.querySelector(".progress");
const volumeSlider = document.getElementById("volume-slider");
const playPauseButton = document.getElementById("play-pause");
const nextButton = document.querySelector(".next");
const prevButton = document.querySelector(".prev");
const playPauseIcon = document.querySelector(".fas.fa-play");
const volumeButton = document.querySelector(".volume");
const selectDirectory = document.getElementById("select-files");
const changeDirectory = document.getElementById("change-directory");

let audioList = [];
let songTitleList = [];
let currentTrackIndex = 0;
let currentTime = 0;

selectDirectory.addEventListener("click", scanFolder);

changeDirectory.addEventListener("click", () => {
  playListSection.style.display = "none";
  selectSection.style.display = "flex";
  nowPlayingSection.style.display = "none";
});

openPlayList.addEventListener("click", () => {
  playListSection.style.display = "block";
  selectSection.style.display = "none";
  nowPlayingSection.style.display = "none";
});

async function scanFolder() {
  try {
    const dirHandle = await window.showDirectoryPicker();
    const entries = await dirHandle.values();

    const audioFiles = [];

    // Iterate using for...of loop, compatible with FileSystemDirectoryIterator
    for await (const entry of entries) {
      if (entry.name.endsWith(".mp3") || entry.name.endsWith(".wav") || entry.name.endsWith(".m4a")) {
        audioFiles.push(entry);
      }
    }

    displayAudioInfo(audioFiles);
    playListSection.style.display = "block";
    selectSection.style.display = "none";
  } catch (error) {
    console.error("Error scanning folder:", error);
    // Handle errors gracefully, informing the user about issues
  }
}

async function displayAudioInfo(audioFiles) {
  for (const file of audioFiles) {
    try {
      const fileHandle = await file.getFile();
      const fileReader = new FileReader();

      fileReader.addEventListener("load", () => {
        const audioBlob = fileReader.result;
        const audio = new Audio(audioBlob);

        audioList.push(audio);
        songTitleList.push(file.name);

        // Create element for each audio file and add click listener
        const audioElement = document.createElement("li");
        audioElement.textContent = file.name; // Set file name as text
        audioElement.addEventListener("click", () => {
          selectedAudio = audio; // Store selected audio object
          stopAllAudio();
          playTrack(audioList.indexOf(selectedAudio)); // Get index from audioList
          playPauseIcon.classList.replace("fa-play", "fa-pause");
          console.log("Now playing: " + file.name);
          playListSection.style.display = "none";
          selectSection.style.display = "none";
          nowPlayingSection.style.display = "flex";
        });
        playList.appendChild(audioElement);

      });

      fileReader.readAsDataURL(fileHandle);
    } catch (error) {
      console.error("Error processing audio file:", error);
    }
  }

  // Play button click listener
  playPauseButton.addEventListener("click", () => {
    if (selectedAudio.paused) {
      // Audio is paused, so play it
      selectedAudio.play();
      // Update UI to reflect playing state (e.g., change icon to pause)
      playPauseIcon.classList.replace("fa-play", "fa-pause");
    } else {
      // Audio is playing, so pause it
      selectedAudio.pause();
      // Update UI to reflect paused state (e.g., change icon to play)
      playPauseIcon.classList.replace("fa-pause", "fa-play");
    }
  });

  nextButton.addEventListener("click", handleNext);
  prevButton.addEventListener("click", handlePrev);

  volumeButton.addEventListener("click", ()=>{
    // Toggle visibility based on current state:
    if (volumeSlider.style.display === "none") {
      volumeSlider.style.display = "block";
    } else {
      volumeSlider.style.display = "none";
    }
  });

  // Adjust the volume
  volumeSlider.addEventListener("input", () => {
    selectedAudio.volume = volumeSlider.value;
  });

  function handleNext() {
    currentTrackIndex++;
    if (currentTrackIndex >= audioList.length) {
      currentTrackIndex = 0; // Loop to the beginning
    }
      playTrack(currentTrackIndex);
  }

  function handlePrev() {
    currentTrackIndex--;
    if (currentTrackIndex < 0) {
      currentTrackIndex = audioList.length - 1; // Loop to the end
    }
    playTrack(currentTrackIndex);
  }

  function playTrack(index) {
    if (selectedAudio) {
      selectedAudio.currentTime = 0; // Reset playhead to beginning
      selectedAudio.pause(); // Stop current audio playback
    }

    const audio = audioList[index];
    selectedAudio = audio; // Update selected audio reference
    selectedAudio.play(); // Play the track
    // Update UI elements (play/pause icon, track title, etc.)
    let length = convertTime(selectedAudio.duration);
    let songTitle = songTitleList[index];
    let songArtist = selectedAudio.artist;
    trackTitle.innerHTML = songTitle ? songTitle : "Track " + (index + 1);
    artist.innerHTML = songArtist ? songArtist : "unknown artist";
    songLength.innerHTML = length ? length : "0:00";

    setInterval(() => {
      currentTime = selectedAudio.currentTime;
      updateProgressBarPercentage(); // Call function to update progress bar
      timePlayed.innerHTML = convertTime(currentTime);
    }, 1000);
  }

  function stopAllAudio() {
    for (const audio of audioList) {
      audio.pause();
    }
  }

  function updateProgressBarPercentage() {
    const progressPercentage = (currentTime / selectedAudio.duration) * 100;

    // Update the progress bar element's style based on the percentage
    progressBarElement.style.width = `${progressPercentage}%`;

    // Play the next song when progress reaches 100%
    if (progressPercentage === 100){
      handleNext();
    }
  }
}

function convertTime(time) {
  let mins = Math.floor(time / 60);
  if (mins < 10) {
    mins = "0" + mins;
  }
  let secs = Math.floor(time % 60);
  if (secs < 10) {
    secs = "0" + secs;
  }
  return mins + ":" + secs;
}

4. Additional Features (Optional):

This is just a basic player but you can go beyond playback basics and elevate your music listening experience with advanced features. Some features to try include:

  • Shuffle and repeat modes
  • Volume control
  • Search functionality within the playlist
  • Customization options for appearance and behavior

Conclusion

Congratulations! You've successfully built a fully functional music player using HTML, CSS, and JavaScript. We hope this gave you a better understanding of code structure, manipulating UI elements, and managing audio playback.

This is not just a finished project, but a springboard. Explore advanced features, personalize your experience, and make your player truly yours. Remember, the music doesn't stop here. Keep coding, keep creating, and keep your tunes flowing.