How to create Music player with pure HTML, CSS, JS
Tags: html, css, javascript, webdev
Published: 2021-09-04 14:12:06

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

{% youtube Zf_psaJ-wP4 %}

> 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qb40cly8u8kkoiqkj9ke.png)

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.

```HTML
<div class="music-player">
</div>
``` 
```CSS
*{
    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;
}
```
######Output
![Capture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/178fpohi9gv0fqf01vd5.PNG)
Now make song name, artist name.
```HTML
<!-- Inside music player element -->
<h1 class="music-name">song one</h1>
<p class="artist-name">artist</p>
``` 
And of course style them.
```CSS
.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;
}
```
>here " , " (comma) is used to give same style to more than one element.


######Output
![Capture2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lrmkk5j8i9z7dsxhdnij.PNG)

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.
```HTML
<div class="disk"></div>
``` 
>Remember all the elements we are adding inside `music player` container.
```CSS
.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);
}
```
>here, I am using `box-shadow` to create outer circle for the disk.

######Output
![Capture3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51o8qv1tnatjjpo1ags2.PNG)

But, how we make a hole at the center.  For that, we'll use `::before` pseudo element. You can find more about it [here](https://dev.to/kunaal438/all-about-css-before-and-after-element-css-pseudo-elements-5g1n). 

```CSS
.disk::before{
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: #5f7a61;
}
```
######Output
![Capture4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3snvgly9bwtl7m092fnk.PNG)

Hmm! It looking great. Now, make seek bar for the music player of course.
```HTML
<div class="song-slider">
    <input type="range" value="0" class="seek-bar">
</div>
``` 
```CSS
.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;
}
```
>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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz1s9252mrg8dnq7jpjn.PNG)
 
Make music current time and duration element now.
```HTML
<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>
```
```CSS
.current-time,
.song-duration{
    font-size: 14px;
}

.song-duration{
    position: absolute;
    right: 0;
}
```
######Output
![Capture6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rg5y26mxaxvdcx2dkofh.PNG)

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

```HTML
<div class="controls">
    <button class="play-btn">
        <span></span>
        <span></span>
    </button>
</div>
```
```CSS
.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;
}
```
>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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xhkh71a28c1zr0ho9a87.PNG)

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

```CSS
.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%);
}
``` 
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k976k1f5mfrqbh4smhf4.gif)

>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.
```CSS
.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);
    }
}
```
Add `pause` class to the `play-btn` element as default. Great! Now make forward/backward buttons.

```HTML
<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> 
```
```CSS
.btn{
    width: 40px;
    height: 40px;
    background: #d5eebb;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    border: none;
    cursor: pointer;
}
```
######Output
![Capture8](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfr98ci98pedue75tv4y.PNG)

Now the last animation we want is disk rotation. For that make an animation.
```CSS
@keyframes rotate{
    0%{
        transform: rotate(0);
    }
    100%{
        transform: rotate(360deg);
    }
}
``` 
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`.

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

.disk.play{
    animation-play-state: running;
}
```

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.
```HTML
<audio src="" id="audio"></audio>
```
2. 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. 
```JS
let currentMusic = 0;
```
Now select each and every element that we need to add some behaviour.

```JS
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');
```
Once you are done selecting all the elements. make a function to setup the music.
```JS
// 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);
}
```
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.
```JS
setMusic(0);
```

Create format time function.
```JS
// 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}`;
}
```
Now we can add click event to our play button.
```JS
playBtn.addEventListener('click', () => {
    if(playBtn.className.includes('pause')){
        music.play();
    } else{
        music.pause();
    }
    playBtn.classList.toggle('pause');
    disk.classList.toggle('play');
})
```
>`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.
```JS
// seek bar
setInterval(() => {
    seekBar.value = music.currentTime;
    currentTime.innerHTML = formatTime(music.currentTime);
}, 500)
```
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.
```JS
seekBar.addEventListener('change', () => {
    music.currentTime = seekBar.value;
})
```

We are almost done. Now create forward/backward click events.
```JS
// 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();
})
```
And yes, if you see we have `playMusic` function. Its purpose in life is to play music every time.

```JS
const playMusic = () => {
    music.play();
    playBtn.classList.remove('pause');
    disk.classList.add('play');
}
``` 
And the last thing, to make song forward when the current song end. Code this inside `500ms` interval.
```JS
setInterval(() => {
    seekBar.value = music.currentTime;
    currentTime.innerHTML = formatTime(music.currentTime);
    if(Math.floor(music.currentTime) == Math.floor(seekBar.max)){
        forwardBtn.click();
    }
}, 500)
```
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](https://dev.to/kunaal438/css-the-best-css-effects-of-all-time-most-underrated-web-ux-2chj)
2. [Infinte CSS loader](https://dev.to/kunaal438/quick-css-make-infinity-loading-animation-for-your-next-website-187k)
3. [Disney+ Clone](https://dev.to/kunaal438/how-to-create-disney-plus-clone-for-beginner-in-2021-html-css-js-m3p)
4. [Youtube API - Youtube Clone](https://dev.to/kunaal438/create-working-youtube-clone-with-search-box-youtube-api-2a6e)
5. [TMDB - Netflix Clone](https://dev.to/kunaal438/how-to-create-netflix-clone-netflix-clone-with-hmtl-css-js-989)

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

<a href="https://www.youtube.com/c/modern_web?sub_confirmation=1"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jeitpawvax07cx7r9yoa.png"></a>

[Download the assets](https://drive.google.com/drive/folders/1xLHnOMpWgOiQLHY6hpUQZ5CKf2MT1Sog?usp=sharing), [Source Code](https://www.patreon.com/posts/55755212), [Donate me on Paypal](https://paypal.me/modernwebchannel)

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](https://www.patreon.com/modernweb), [Buy me a coffee](https://ko-fi.com/modernweb), [Donate me on paypal](https://paypal.me/modernwebchannel)

Thanks For reading.