DEV Community

Cover image for How to create Music player with pure HTML, CSS, JS
Modern Web
Modern Web

Posted on

How to create Music player with pure HTML, CSS, JS

Hello, Today we'll see, how we can easily create a music player using HTML, CSS and JS only. No other library. Our music player has modern looking designs, with cool animations like play/pause animation, disk rotating animation and pulse animation. Our music player has working forward/backward buttons with seek-able song slider.

To see demo or you want full coding tutorial video for better understanding. You can watch the tutorial below.

Video Tutorial

I appreciate if you can support me by subscribing my youtube channel.

Code

Before we start writing our code. Although it's not a Nodejs app but we should see its folder structure at least.

Frame 11

So, now let's start designing our music player.

First, of course write basic HTML template inside index.html file and link style.css, app.js to it. Then make a music player container.

<div class="music-player">
</div>
Enter fullscreen mode Exit fullscreen mode
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body{
    width: 100%;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background: #5f7a61;
    font-family: 'roboto', sans-serif;
}

.music-player{
    width: 350px;
    height: 550px;
    border-radius: 20px;
    background: rgba(255, 255, 255, 0.08);
    box-shadow: 0 40px 100px rgba(255, 255, 255, 0.1);
    padding: 30px;
    overflow: hidden;
    color: #d5eebb;
}
Enter fullscreen mode Exit fullscreen mode
Output

Capture
Now make song name, artist name.

<!-- Inside music player element -->
<h1 class="music-name">song one</h1>
<p class="artist-name">artist</p>
Enter fullscreen mode Exit fullscreen mode

And of course style them.

.music-name,
.artist-name{
    text-align: center;
    text-transform: capitalize;
}

.music-name{
    font-size: 40px;
    font-weight: 500;
    margin-bottom: 10px;
}

.artist-name{
    font-size: 20px;
}
Enter fullscreen mode Exit fullscreen mode

here " , " (comma) is used to give same style to more than one element.

Output

Capture2

Great! Now make disk/cover image of the song. For that we use just a single div and will give background-image to set song cover image.

<div class="disk"></div>
Enter fullscreen mode Exit fullscreen mode

Remember all the elements we are adding inside music player container.

.disk{
    position: relative;
    display: block;
    margin: 40px auto;
    width: 180px;
    height: 180px;
    border-radius: 50%;
    background-image: url('images/cover\ 1.png');
    background-size: cover;
    box-shadow: 0 0 0 10px rgba(255, 255, 255, 0.08);
}
Enter fullscreen mode Exit fullscreen mode

here, I am using box-shadow to create outer circle for the disk.

Output

Capture3

But, how we make a hole at the center. For that, we'll use ::before pseudo element. You can find more about it here.

.disk::before{
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: #5f7a61;
}
Enter fullscreen mode Exit fullscreen mode
Output

Capture4

Hmm! It looking great. Now, make seek bar for the music player of course.

<div class="song-slider">
    <input type="range" value="0" class="seek-bar">
</div>
Enter fullscreen mode Exit fullscreen mode
.song-slider{
    width: 100%;
    position: relative;
}

.seek-bar{
    -webkit-appearance: none;
    width: 100%;
    height: 5px;
    border-radius: 10px;
    background: #7fc8a9;
    overflow: hidden;
    cursor: pointer;
}

.seek-bar::-webkit-slider-thumb{
    -webkit-appearance: none;
    width: 1px;
    height: 20px;
    box-shadow: -400px 0 0 400px #d5eebb;
}
Enter fullscreen mode Exit fullscreen mode
  1. -webkit-appearance : this is used to hide all the default styles of the element done by browser.
  2. -webkit-slider-thumb : this will select the thumb or head of the slider, that we drag.
Output

Capture5

Make music current time and duration element now.

<div class="song-slider">
    <input type="range" value="0" class="seek-bar">
    <span class="current-time">00:00</span>
    <span class="song-duration">00:00</span>
</div>
Enter fullscreen mode Exit fullscreen mode
.current-time,
.song-duration{
    font-size: 14px;
}

.song-duration{
    position: absolute;
    right: 0;
}
Enter fullscreen mode Exit fullscreen mode
Output

Capture6

Now, make controls. We'll make play button icon with CSS and forward/backward button will be the images.

<div class="controls">
    <button class="play-btn">
        <span></span>
        <span></span>
    </button>
</div>
Enter fullscreen mode Exit fullscreen mode
.controls{
    width: 60%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin: auto;
    margin-top: 20px;
}

.play-btn{
    position: relative;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: #d5eebb;
    cursor: pointer;
    border: none;
}

.play-btn span{
    position: absolute;
    top: 50%;
    left: 25%;
    transform: translateY(-50%);
    width: 10px;
    height: 30px;
    border-radius: 2px;
    background: #5f7a61;
    transition: .5s;
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
}

.play-btn span:nth-child(2){
    left: 55%;
    transform-origin: center;
}
Enter fullscreen mode Exit fullscreen mode

If you notice here, in the span selector, I am setting default clip-path, this is because I want smooth transition when using clip-path for that I need to define a default value as well. And the the value of clip path is just a rectangle.

Output

Capture7

Now I want to make this pause logo to a play logo. For that I'll use an extra class pause.

.play-btn.pause span:nth-child(2){
    transform: translateY(-50%) scaleY(0);
}

.play-btn.pause span:nth-child(1){
    width: 35%;
    left: 53%;
    transform: translate(-50%, -50%);
    border-radius: 0;
    clip-path: polygon(0 0, 100% 50%, 100% 50%, 0% 100%);
}
Enter fullscreen mode Exit fullscreen mode
  1. When you have some thing like this .class.class or .class#id or #id.class have multiple selector without space. This means you are selecting the elements when having both classes/ids.

  2. What we did in the above CSS is, when the play icon have pause class, we are scaling down its 2nd span to 0 making it fully disappear. And using clip-path to make a triangle shape for the first span.

Output

Untitled design

Output when toggling the play-btn element's pause class from chrome inspector.

Now make pulse animation and add it to play-btn when having pause class.

.play-btn.pause{
    animation: pulse 2s linear infinite;
}

@keyframes pulse{
    0%{
        box-shadow: 0;
    }
    50%{
        box-shadow: 0 0 0 5px rgba(255, 255, 255, 0.1);
    }
    100%{
        box-shadow: 0 0 0 5px rgba(255, 255, 255, 0.4);
    }
}
Enter fullscreen mode Exit fullscreen mode

Add pause class to the play-btn element as default. Great! Now make forward/backward buttons.

<div class="controls">
    <button class="btn backward-btn"><img src="images/pre.png" alt=""></button>
    <button class="play-btn pause">
        <span></span>
        <span></span>
    </button>
    <button class="btn forward-btn"><img src="images/nxt.png" alt=""></button>
</div> 
Enter fullscreen mode Exit fullscreen mode
.btn{
    width: 40px;
    height: 40px;
    background: #d5eebb;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    border: none;
    cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode
Output

Capture8

Now the last animation we want is disk rotation. For that make an animation.

@keyframes rotate{
    0%{
        transform: rotate(0);
    }
    100%{
        transform: rotate(360deg);
    }
}
Enter fullscreen mode Exit fullscreen mode

But how to give this animation. Give this animation as default to .disk element. But set its animation-play-state to paused. animation-play-state is used to set animation state. In this case we want animation to be paused at start.

And change the state to running, when disk element have class play.

.disk{
    // previous styles
    animation: rotate 20s linear infinite;
    animation-play-state: paused;
}

.disk.play{
    animation-play-state: running;
}
Enter fullscreen mode Exit fullscreen mode

We are done with designing the player. Now, let's make this player function.

Before writing JS, make sure you do these things.

  1. Make an audio source. We of course need an audio source to play the music. For that just make an empty audio element at the very start and give it a id so we can access it in JS.
<audio src="" id="audio"></audio>
Enter fullscreen mode Exit fullscreen mode
  1. And 2nd thing is, make sure you add data.js file before app.js because, our data is in data.js but we can only access it in app.js if we import it before app.js.

Now we can write JS.

Initialize a variable to store current music index. Set it default value to 0.

let currentMusic = 0;
Enter fullscreen mode Exit fullscreen mode

Now select each and every element that we need to add some behaviour.

const music = document.querySelector('#audio');

const seekBar = document.querySelector('.seek-bar');
const songName = document.querySelector('.music-name');
const artistName = document.querySelector('.artist-name');
const disk = document.querySelector('.disk');
const currentTime = document.querySelector('.current-time');
const musicDuration = document.querySelector('.song-duration');
const playBtn = document.querySelector('.play-btn');
const forwardBtn = document.querySelector('.forward-btn');
const backwardBtn = document.querySelector('.backward-btn');
Enter fullscreen mode Exit fullscreen mode

Once you are done selecting all the elements. make a function to setup the music.

// setup music

const setMusic = (i) => {
    seekBar.value = 0; // set range slide value to 0;
    let song = songs[i];
    currentMusic = i;
    music.src = song.path;

    songName.innerHTML = song.name;
    artistName.innerHTML = song.artist;
    disk.style.backgroundImage = `url('${song.cover}')`;

    currentTime.innerHTML = '00:00';
    setTimeout(() => {
        seekBar.max = music.duration;
        musicDuration.innerHTML = formatTime(music.duration);
    }, 300);
}
Enter fullscreen mode Exit fullscreen mode

Here, we are setting up the song. Thing to notice here is,

  1. setTimeout - I am using this because, there is a slight time difference in setting up the music and accessing it. Which causing an error while accessing its duration.
  2. formatTime - This is a function that we'll create next. This function will take time as an argument, and return us the formatted value. For instance is we pass - formatTime(120) then it will return 02:00.

Don't forget to call the setMusic once with 0 as an argument.

setMusic(0);
Enter fullscreen mode Exit fullscreen mode

Create format time function.

// formatting time in min and seconds format

const formatTime = (time) => {
    let min = Math.floor(time / 60);
    if(min < 10){
        min = `0${min}`;
    }
    let sec = Math.floor(time % 60);
    if(sec < 10){
        sec = `0${sec}`;
    }
    return `${min} : ${sec}`;
}
Enter fullscreen mode Exit fullscreen mode

Now we can add click event to our play button.

playBtn.addEventListener('click', () => {
    if(playBtn.className.includes('pause')){
        music.play();
    } else{
        music.pause();
    }
    playBtn.classList.toggle('pause');
    disk.classList.toggle('play');
})
Enter fullscreen mode Exit fullscreen mode

element.className returns its classes and includes method is used to find a particular string is inside the text or not.

Now make seek bar update to song's current time after every 500ms.

// seek bar
setInterval(() => {
    seekBar.value = music.currentTime;
    currentTime.innerHTML = formatTime(music.currentTime);
}, 500)
Enter fullscreen mode Exit fullscreen mode

This will update the seek bar after every 500ms.
Now, we also want to jump in between song time. For that add change event to the seek bar.

seekBar.addEventListener('change', () => {
    music.currentTime = seekBar.value;
})
Enter fullscreen mode Exit fullscreen mode

We are almost done. Now create forward/backward click events.

// forward and backward button
forwardBtn.addEventListener('click', () => {
    if(currentMusic >= songs.length - 1){
        currentMusic = 0;
    } else{
        currentMusic++;
    }
    setMusic(currentMusic);
    playMusic();
})

backwardBtn.addEventListener('click', () => {
    if(currentMusic <= 0){
        currentMusic = songs.length - 1;
    } else{
        currentMusic--;
    }
    setMusic(currentMusic);
    playMusic();
})
Enter fullscreen mode Exit fullscreen mode

And yes, if you see we have playMusic function. Its purpose in life is to play music every time.

const playMusic = () => {
    music.play();
    playBtn.classList.remove('pause');
    disk.classList.add('play');
}
Enter fullscreen mode Exit fullscreen mode

And the last thing, to make song forward when the current song end. Code this inside 500ms interval.

setInterval(() => {
    seekBar.value = music.currentTime;
    currentTime.innerHTML = formatTime(music.currentTime);
    if(Math.floor(music.currentTime) == Math.floor(seekBar.max)){
        forwardBtn.click();
    }
}, 500)
Enter fullscreen mode Exit fullscreen mode

And that's it. Great Job! we are done with our music player. I hope you understood each and everything. If you have doubt or I missed something let me know in the comments.

Articles you may find Useful

  1. Best CSS Effect
  2. Infinte CSS loader
  3. Disney+ Clone
  4. Youtube API - Youtube Clone
  5. TMDB - Netflix Clone

I really appreciate if you can subscribe my youtube channel. I create awesome web contents.

Download the assets, Source Code, Donate me on Paypal

My next upcoming tutorials which you might don't want to miss.

  1. Fully working e-commerce website - Fullstack
  2. Responsive Personal Portfolio
  3. Visiting card designer with download feature.

Your donation really motivates me to do more amazing tutorials like this. Support me on patreon, Buy me a coffee, Donate me on paypal

Thanks For reading.

Top comments (12)

Collapse
 
joshuaamaju profile image
Joshua Amaju

Why not listen for timeupdate event on the audio element instead of using setInterval.

Collapse
 
tgkprog profile image
Tushar Kapila

Yes for end of song seems like a better way. Especially long songs, more deviation

Collapse
 
themodernweb profile image
Modern Web

Well I didn't knew about that. Thanks for telling this event☺️

Collapse
 
joshuaamaju profile image
Joshua Amaju

It's best to consult the doc for stuff like that

Collapse
 
jamesthomson profile image
James Thomson

I would also suggest tracking the play/pause state against a variable instead of relying on css classes to determine the current state.

Collapse
 
themodernweb profile image
Modern Web

Yup! We can use music.paused as a condition instead of checking for play class both will work☺️

Collapse
 
happyman001144 profile image
Happyman001144

Hi, I am new to HTML/CSS and am creating this project, however the "play" button is in the top left hand side of the container and not the centre, despite me checking the code multiple times I can not figure out why, and my code is exactly the same as yours, any suggestions would be appreciated!

The problem: cdn.discordapp.com/attachments/904...

Code: cdn.discordapp.com/attachments/904...

Collapse
 
dougbug98 profile image
dougbug98 • Edited

The pre and nxt buttons don't even show up exactly how your's shows up. For me they are just very narrow white lines. Where did you get those images from?

Collapse
 
themodernweb profile image
Modern Web

I created those arrows while designing the music player in figma. But you can use font awesome or you can download arrow image from google

Collapse
 
dougbug98 profile image
dougbug98 • Edited

Oh ok coo, I gotta check out figma. I actually went to Google and looked through the images but then I stopped and thought to myself that images from there won't work for the button. Also if you don't mind answering how do you memorize sush very long lines of code across different languages? It reminds me of really long math problems. It just seems like so much to memorize especially all the different numbers for the sizes of the various elements.

Thread Thread
 
themodernweb profile image
Modern Web

No programming is not about memorizing things. I don't memorize any line. Its just practice which made everything easy for us. If you are talking about how I record my tutorial in that case I use a second screen which show me what to code. So there is no memorizing 😅

Collapse
 
dermasajikun profile image
dermasajikun • Edited

I want to ask, to make a list of songs like rytmp3 is it also possible to make it like the way above?