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
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;
}