How to make a basic Javascript Drum Machine

How to make a basic Javascript Drum Machine

·

6 min read

Today I'm going to show you how I made a basic drum machine using Javascript and CSS.

The full code can be downloaded from Github. You can view the demo at codepen.

First, we're going to create the drum-machine element

let drumMachine = document.createElement('div');
drumMachine.id = 'drum-machine';

Next, we create the display element and append it to the drum-machine element

let display = document.createElement('div');
display.id = 'display';
drumMachine.append(display);

followed by the drum-pad-wrapper element

let drumPadWrapper = document.createElement('div');
drumPadWrapper.id = 'drum-pad-wrapper';

Inside the drum-pad-wrapper there are going to be 9 pads to press: 'Q', 'W', 'E', 'A', 'S', 'D', 'Z', 'X' and 'C' in that order. Each pad will be linked to an audio tag as well as a description for the audio which will show on the display. Furthermore, each pad's letter must have a keyboard event which will fire the corresponding pad's audio. In order to achieve this I decided to create an array of objects for the pads which includes the key, a description and a url for the audio

let pads = [
    {
        key: 'Q',
        id: 'Heat-1',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3'
    },
    {
        key: 'W',
        id: 'Heat-2',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-2.mp3'
    },
    {
        key: 'E',
        id: 'Heat-3',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-3.mp3'
    },
    {
        key: 'A',
        id: 'Heat-4',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-4_1.mp3'
    },
    {
        key: 'S',
        id: 'Clap',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-6.mp3'
    },
    {
        key: 'D',
        id: 'Open-HH',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Dsc_Oh.mp3'
    },
    {
        key: 'Z',
        id: 'Kick-n-Hat',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Kick_n_Hat.mp3'
    },
    {
        key: 'X',
        id: 'Kick',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/RP4_KICK_1.mp3'
    },
    {
        key: 'C',
        id: 'Closed-HH',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3'
    }
];

I then iterated over the array

for(i = 0; i < pads.length; i++){}

within that loop I set j to equal i because, for whatever reason, functions within a loop don't pick up on the value of i, but they do pick up a variable set within the loop

let j = i;

next I created the drum-pad element.

let drumPad = document.createElement('div');
drumPad.id = pads[i].id;
drumPad.className = 'drum-pad';

There, you'll notice pads[i].id. If you take a look at the pads array of objects we created a minute ago, you'll see an id property within each object. Where i equals 0 on the first iteration, pads[i].id would translate to pads[0].id which has a value of 'Heat-1'.

Next I created the drum-pad-text element which represents the keyboard key for each pad and appended it to the drum-pad element.

let drumPadText = document.createElement('span');
drumPadText.className = 'drum-pad-text';
drumPadText.innerText = pads[i].key;
drumPad.append(drumPadText);

Then I added in the audio element, set its source to the iteration's object url property and appended that to the drum-pad element.

let clip = document.createElement('audio');
clip.id = pads[i].key;
clip.className = 'clip';
clip.src = pads[i].url;
drumPad.append(clip);

Next I set the click function of the drum-pad element to play the audio clip and change the text in the display to the corresponding description

drumPad.onclick = function(){
    clip.play();
    display.innerText = pads[j].id.replace(/-/g, ' ');
}

followed by setting an event listener for the iteration's object key, ie "Q", "W", "E" etc. to play the audio clip and change the text in the display to the corresponding description when the keys are pressed

document.addEventListener('keydown', function(event) {
    if(event.code == 'Key' + pads[j].key) {
        clip.play();
        display.innerText = pads[j].id.replace(/-/g, ' ');
    }
});

The final thing to do in the loop was to append the drumPad to the drumPadWrapper

drumPadWrapper.append(drumPad);

Then all we need to do is apppend the drumPadWrapper to the drumMachine and add the drumMachine to the body

drumMachine.append(drumPadWrapper);
document.body.appendChild(drumMachine);

That's it for the Javascript. Now when you load the page, you're going to see a list of letters going down the screen, which play a sound when you click them and shows a description of the sound. You could even press the keyboard keys and you should get the same sounds. Now we need to add in a bit of style. I'm going to keep it as minimalist as possible. Not much point in getting fancy here, but your imagination's the limit when you're making your own.

First I set some defaults for the body along with a gradient background. I used a css gradient generator to get the background property, so much better than trying to guess the colour combinations from memory.

body{
    text-align: left;
    font-size: 19px;
    font-family:arial;
    letter-spacing: 1px;
    line-height: 27px;
    margin:0;
    background: rgb(2,0,36);
    background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(11,23,112,1) 45%, rgba(19,93,108,1) 100%);
    user-select: none;
}

Next, we want to make sure the audio elements are hidden. I mean, I couldn't see them on the page anyway, but just in case I added the property in the stylsheet anyway

audio{
    display:none;
}

Next it was basically just positioning and sizing the drum-machine and display elements, you can mess about with these properties and make them your own. In fact, I recommend messing about with all the properties and see how you can make it your own.

#drum-machine{
    position: relative;
    top: calc(50vh - 191px);
    width: 374px;
    height: 383px;
    margin: auto;
}

#display{
    border: 1px solid;
    height: 37px;
    background: #dbdbdb;
    color: #276a27;
    text-align: center;
    padding-top: 9px;
    font-size: 32px;
    font-weight: bold;
}

The pads are a 3 x 3 block. To achieve this I used the grid function on the drum-pad-wrapper element. First I set the grid area for the keys

#Q {grid-area:Q;}
#W {grid-area:W;}
#E {grid-area:E;}
#A {grid-area:A;}
#S {grid-area:S;}
#D {grid-area:D;}
#Z {grid-area:Z;}
#X {grid-area:X;}
#C {grid-area:C;}

Then I set the drum-pad-wrapper element to grid and stated the positions of the above grid areas, along with more sizing

#drum-pad-wrapper{
    display: grid;
    grid-template-areas:
        'Q W E'
        'A S D'
        'Z X C';
    height:300px;
    width:375px;
    margin-top:17px;
}

More sizing and positiong for the pads. Here I set a margin to move the pads away from each other

.drum-pad{
    border:1px solid;
    padding: 28px 0;
    text-align: center;
    margin: 7px;
    border-radius: 15px;
    background: #fff;
    cursor: pointer;
}

and finally set the font size and weight of the pad's text

.drum-pad-text{
    font-size: 28px;
    font-weight:bold;
}

and that's all. Now when you refresh the page you should see something like

drum machine screenshot

The full code:

Javascript

let drumMachine = document.createElement('div');
drumMachine.id = 'drum-machine';

let display = document.createElement('div');
display.id = 'display';
drumMachine.append(display);

let drumPadWrapper = document.createElement('div');
drumPadWrapper.id = 'drum-pad-wrapper';

let pads = [
    {
        key: 'Q',
        id: 'Heat-1',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3'
    },
    {
        key: 'W',
        id: 'Heat-2',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-2.mp3'
    },
    {
        key: 'E',
        id: 'Heat-3',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-3.mp3'
    },
    {
        key: 'A',
        id: 'Heat-4',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-4_1.mp3'
    },
    {
        key: 'S',
        id: 'Clap',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-6.mp3'
    },
    {
        key: 'D',
        id: 'Open-HH',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Dsc_Oh.mp3'
    },
    {
        key: 'Z',
        id: 'Kick-n-Hat',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Kick_n_Hat.mp3'
    },
    {
        key: 'X',
        id: 'Kick',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/RP4_KICK_1.mp3'
    },
    {
        key: 'C',
        id: 'Closed-HH',
        url: 'https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3'
    }
];
for(i = 0; i < pads.length; i++){
    let j = i;
    let drumPad = document.createElement('div');
    drumPad.id = pads[i].id;
    drumPad.className = 'drum-pad';
    let drumPadText = document.createElement('span');
    drumPadText.className = 'drum-pad-text';
    drumPadText.innerText = pads[i].key;
    drumPad.append(drumPadText);
    let clip = document.createElement('audio');
    clip.id = pads[i].key;
    clip.className = 'clip';
    clip.src = pads[i].url;
    drumPad.append(clip);
    drumPad.onclick = function(){
        clip.play();
        display.innerText = pads[j].id.replace(/-/g, ' ');
    }
    document.addEventListener('keydown', function(event) {
        if(event.code == 'Key' + pads[j].key) {
            clip.play();
            display.innerText = pads[j].id.replace(/-/g, ' ');
        }
    });
    drumPadWrapper.append(drumPad);
}
drumMachine.append(drumPadWrapper);
document.body.appendChild(drumMachine);

css

body{
    text-align: left;
    font-size: 19px;
    font-family:arial;
    letter-spacing: 1px;
    line-height: 27px;
    margin:0;
    background: rgb(2,0,36);
    background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(11,23,112,1) 45%, rgba(19,93,108,1) 100%);
    user-select: none;
}

audio{
    display:none;
}

#drum-machine{
    position: relative;
    top: calc(50vh - 191px);
    width: 374px;
    height: 383px;
    margin: auto;
}

#display{
    border: 1px solid;
    height: 37px;
    background: #dbdbdb;
    color: #276a27;
    text-align: center;
    padding-top: 9px;
    font-size: 32px;
    font-weight: bold;
}

#Q {grid-area:Q;}
#W {grid-area:W;}
#E {grid-area:E;}
#A {grid-area:A;}
#S {grid-area:S;}
#D {grid-area:D;}
#Z {grid-area:Z;}
#X {grid-area:X;}
#C {grid-area:C;}

#drum-pad-wrapper{
    display: grid;
    grid-template-areas:
        'Q W E'
        'A S D'
        'Z X C';
    height:300px;
    width:375px;
    margin-top:17px;
}

.drum-pad{
    border:1px solid;
    padding: 28px 0;
    text-align: center;
    margin: 7px;
    border-radius: 15px;
    background: #fff;
    cursor: pointer;
    transition:.4s;
}

.drum-pad-text{
    font-size: 28px;
    font-weight:bold;
}