Virtual Drum Kit using Javascript
Tags: javascript, html, css, webdev
Published: 2021-05-15 06:19:31

Hello everyone, 

Hope you all are safe during this pandemic. I have started a new series where I will be posting Javascript projects for beginners.

Each post in this series mainly comprises of two things,

           1. TIL while building them.
           2. Project explanation.

So Let's start,

What we are gonna build today is a __Virtual Drum__ which,

           1. Has visual representation of the keys
           2. Plays a audio file when pressing its 
              corresponding key
           3. A small animation indicating that the particular 
              key is pressed - Scales up(make it appear big)  
              for a fraction of a second and scales down while 
              maintaining a smooth transition.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwq449kaeanbyg09o0q8.png)

__TIL__

__1. transitionend__

   It is an event Listener which gets triggered when the transition gets over as the name specifies. To know its use case read the entire post.

__2. data-*__

   Data attribute allow authors to create custom data to apply to their HTML elements to store extra data. They help extend the information that HTML can communicate to script. 

{% codepen https://codepen.io/prashanth-the-lessful/pen/wvJGxBj %}

__3. defer vs window.onload__

   Both are used to load the script after the document has finished parsing inorder to prevent the script accessing the DOM elements even before parsing stage is over. Using defer and window.onload are not the same even though they serve the same purpose of loading script. Load events are triggered only after deferred scripts run. 

__4. background-size__

  1. cover:
       	Resize the background image to cover the entire container, even if it has to stretch the image or cut a little bit off one of the edges
 
  2. contain:
        Resize the background image to make sure the image is fully visible

  3. auto:
        Default value. The background image is displayed in its original size
  

__Project Explanation__

```html
<div class="parent-key">
  <div data-key=65 class="key">
    <div>A</div>
    <span>Clap</span>
  </div>
</div>

<audio data-key=65 src="sounds/boom.wav"></audio>
```

This is the basic markup for a single key and its corresponding audio. ``audio`` is the tag which is used to include sound in html . It has got the audio api which can be used to manipulate the audio file to play, pause, reset ... and much more. Data-*(data attribute) has the value equivalent to the ASCII value of the key.

```css
body {
    background-image: url('background.jpg');
    background-size: cover;
}

.parent-key {
    display: flex;
    justify-content: space-around;
    align-items: center;
    min-height:100vh;
    flex-wrap: wrap;
}


.key {
    color: white;
    font-size: 40px;
    border:3px solid black;
    background-color: rgba(0,0,0,0.4);
    padding: 10px;
    text-align: center;
    text-transform: uppercase;
    letter-spacing: .1rem;
    transform: scale(1);
    transition: all 0.2s ease;
}

.key span {
    font-size: 14px;
    color: #ffc600;
}

.playing {
    transform: scale(1.2);
    border: 4px solid #ffc600;
    box-shadow: 0 0 1rem #ffc600;
}
```

For the styling part of the markup, I have used ``flex`` to center align the keys vertically and horizontally. Background image is included using the url .For remote image, url is specified and for image available in your directory, path is specified.The class ``playing`` is added to the element when the key corresponding to the key in the UI is pressed and removed after a fraction of a second. The class ``playing`` makes the element bigger in size(transform: scale(1.2)), applies larger border and box-shadow(highlights the box).

Till now we have completed the markup and styling part. Now let's give functionality to the __virtual drum kit__. When a key is pressed,

              1. Play the ``audio``
              2. Attach the class ``playing``
              3. Remove the class ``playing`` after the 
                 transition is over.

Let's attach an eventListener(``keydown``) to the window object so that whenever a key is pressed the eventListener gets triggered and the function execution starts. We should execute the function only if you press the keys for which the audio must be played, so let's give a if-check which looks for the audio element in the html file using the keyCode corresponding to the key pressed.
 
```js

window.addEventListener('keydown',function(e){
   let audio = document.querySelector(`audio[datakey="${e.keyCode}"]`)
   if(!audio) return;
})

```

Using the audio element we now can play the audio using the play method in the audio element api- ``audio.play()``. when you run the code , when you press the key you want to play the audio file , audio is played but there is a problem here. Lets say the audio file is for 3 seconds for a particular beat, you cannot be waiting till the audio file finishes(in the case you want to press the same key multiple times),so we must reset the time to zero(``audio.currentTime = 0;``).By now, if you press the audio keys we can hear the corresponding sound. 

But we need some animation to indicate that the particular key is pressed. This can be done by adding the class ``playing`` to pressed key element with a smooth transition using the ``transition`` property in css. Get the key from the document api and add the class to it.

```javascript
window.addEventListener('keydown',function(e){
    if(!document.querySelector(`audio[data-key="${e.keyCode}"]`)) return;
    let audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    let key = document.querySelector(`.key[data-key="${e.keyCode}"]`)
    audio.currentTime = 0;
    audio.play();
    key.classList.add('playing');
})
```

Now when we press the key, the key element in UI scales up, but we need to scale down by removing ``playing`` class after a fraction of second. One way of doing this using ``setTimeout`` function. Using this function we can delay for a fraction of a second of a second and remove the class. It works fine but the issue we will face is, sometimes it messes up with the transition property in css(``transition: all .07s ease;``). The better way of handling this situation is listening to ``transitionend`` event, which executes a function when the transition ends. So now we can remove the class ``playing`` after the transition gets over. 

```javascript

window.addEventListener('keydown',function(e){
    if(!document.querySelector(`audio[data-key="${e.keyCode}"]`)) return;
    let audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    let key = document.querySelector(`.key[data-key="${e.keyCode}"]`)
    audio.currentTime = 0;
    audio.play();
    key.classList.add('playing');
})

let keys = document.querySelectorAll('.key');
keys.forEach((key)=>{
    key.addEventListener('transitionend',function(){
        this.classList.remove('playing')
    })
})
```

I hope you were able to learn how to make a virtual drum. Follow me to read more javascript projects and don't forget to like because it will motivate me 😉.

Assets(audio files and background image) and code files available in this repo👇🏻 {% github https://github.com/prashanthsasidharan/javascript-projects %}

---

*Header Image By [PlayTheTunes](https://playthetunes.com/best-drum-heads/)*