Yes. I've built a guitar already, you can find it here:
Let's build an actual working Guitar🎸 with JavaScript 💻🤘
Pascal Thormeier ・ Feb 2 '21
And now a drum kit. Am I running out of ideas? Well, not exactly. If you want to form a digital rock band, you need more than one instrument, right? And this is only part two, so you can expect more.
Without further ado, let's get back into the instrument workshop and get going!
Assemble the kit
A drum kit has a lot of parts. To build them all and make them look good, I use a few linear gradients and a radial one:
<defs>
<radialGradient id="head" fx="26%" fy="26%">
<stop offset="0%" style="stop-color:#f0ede6;" />
<stop offset="100%" style="stop-color:#f5e9c9;" />
</radialGradient>
<radialGradient id="case" fx="30%" fy="30%">
<stop offset="0%" style="stop-color:#82827f;" />
<stop offset="100%" style="stop-color:#6b6b64;" />
</radialGradient>
<linearGradient id="caseColor">
<stop offset="0%" style="stop-color:#6193ba;" />
<stop offset="20%" style="stop-color:#a8c9e3;" />
<stop offset="100%" style="stop-color:#6b6b64;" />
</linearGradient>
<linearGradient id="cymbal" gradientTransform="rotate(25)">
<stop offset="0%" style="stop-color:#ede58c;" />
<stop offset="30%" style="stop-color:#f2eec4;" />
<stop offset="60%" style="stop-color:#f2eec4;" />
<stop offset="100%" style="stop-color:#ede58c;" />
</linearGradient>
</defs>
Then I add the parts of the drum kit, namely a bass drum (made with a single circle), the snare drum and three toms (ellipse + two paths), a hi hat cymbal (two ellipses + one path) and a crash (a singe ellipse and a single path).
<!-- Hi-Tom -->
<path stroke-width="20" stroke="url(#case)" d="M 1000 700 1001 1200" fill="none" />
<path stroke-width="20" stroke="url(#case)" fill="url(#caseColor)" stroke-linejoin="round" d="
M 880 700
880 820
A 1000 1900 0 0 0 1120 820
L 1120 700 Z
" />
<ellipse id="hitom" fill="url(#head)" stroke-width="20" stroke="url(#case)" cx="1000" cy="700" rx="120" ry="20" />
<!-- Mid-Tom -->
<path stroke-width="20" stroke="url(#case)" d="M 1350 700 1351 1200" fill="none" />
<path stroke-width="20" stroke="url(#case)" fill="url(#caseColor)" stroke-linejoin="round" d="
M 1220 700
1220 860
A 1000 1900 0 0 0 1480 860
L 1480 700 Z
" />
<ellipse id="midtom" fill="url(#head)" stroke-width="20" stroke="url(#case)" cx="1350" cy="700" rx="130" ry="20" />
<!-- Bass drum -->
<circle id="bass" fill="url(#head)" cx="1200" cy="1200" r="270" stroke-width="20" stroke="url(#case)" />
<!-- Snare drum -->
<path stroke-width="20" stroke="url(#case)" d="M 900 910 901 1410 780 1460 M 901 1410 1020 1460" fill="none" />
<path stroke-width="20" stroke="url(#case)" fill="url(#caseColor)" stroke-linejoin="round" d="
M 710 900
710 1050
A 950 1700 0 0 0 1110 1050
L 1110 900 Z
" />
<ellipse id="snare" fill="url(#head)" stroke-width="20" stroke="url(#case)" cx="910" cy="900" rx="200" ry="50" />
<!-- Floor tom -->
<path stroke-width="20" stroke="url(#case)" d="M 1700 1200 1740 1480 M 1500 1200 1450 1480" fill="none" />
<path stroke-width="20" stroke="url(#case)" fill="url(#caseColor)" stroke-linejoin="round" d="
M 1380 1020
1380 1350
A 950 1700 0 0 0 1820 1350
L 1820 1020 Z
" />
<ellipse id="floortom" fill="url(#head)" stroke-width="20" stroke="url(#case)" cx="1600" cy="1020" rx="220" ry="60" />
<!-- Hihat -->
<path stroke-width="20" stroke="url(#case)" d="M 500 830 500 1410 580 1460 M 500 1410 430 1460" fill="none" />
<ellipse
cx="500" cy="830" rx="200" ry="40"
fill="url(#cymbal)" stroke="#222" stroke-width="1"
/>
<ellipse
id="hihat-head"
cx="500" cy="800" rx="200" ry="40"
fill="url(#cymbal)" stroke="#222" stroke-width="1"
/>
<!-- Crash -->
<path stroke-width="20" stroke="url(#case)" d="M 1850 600 1851 1410" fill="none" />
<ellipse
id="crash"
cx="1850" cy="600" rx="300" ry="50"
fill="url(#cymbal)" stroke="#222" stroke-width="1" transform="rotate(-15 1850 600)"
/>
With this result:
There's really only the pedals missing, but I won't need those anyways when I play everything with my keyboard, right?
And how do I play this?
I need to come up with some kind of pattern of keys. I want this to almost feel like a real drum kit, so I kind-of replicate the layout of a drum kit on a keyboard:
So, basically
Hihat open: A
Hihat closed: Shift+A
Hi tom: F
Mid tom: J
Crash cymbal: O
Snare drum: B
Bass drum/kick: Space bar
In JS, I add an event listener to the window and analyse the key
attribute of the keydown
event:
let isShiftPressed = false
const hihatHead = document.querySelector('#hihat-head')
const hitom = document.querySelector('#hitom')
const midtom = document.querySelector('#midtom')
const floortom = document.querySelector('#floortom')
const snare = document.querySelector('#snare')
const crash = document.querySelector('#crash')
const bass = document.querySelector('#bass')
/**
* Finds out which drum was played.
* @param key
* @returns {string|null}
*/
const getInstrument = key => {
switch (key.toLowerCase()) {
case 'a':
return hihatHead
case 'f':
return hitom
case 'j':
return midtom
case 'l':
return floortom
case 'b':
return snare
case 'o':
return crash
case ' ':
return bass
}
return null
}
window.addEventListener('keydown', e => {
if (e.key === 'Shift') {
isShiftPressed = true
return
}
const drum = getInstrument(e.key)
if (drum === null) {
return
}
// ...
})
window.addEventListener('keyup', e => {
if (e.key === 'Shift') {
isShiftPressed = false
// ...
}
})
Next, I add some animations to give a visual feedback. I use some CSS classes for this that I'll remove shortly after with a timeout. I use clearTimeout
to not run into weird behaviour when playing a drum really fast:
#hihat-head.closed {
transform: translateY(10px);
}
.played {
transform: translateY(5px);
}
#bass.played {
transform: scale(0.98);
transform-origin: 1200px 1200px;
}
#crash.played {
fill: url(#cymbal);
transform: rotate(-20deg);
transform-origin: 1850px 600px;
}
#hihat-head.played {
fill: url(#cymbal);
transform: rotate(5deg);
transform-origin: 500px 830px;
}
And adding and removing the classes:
const timeouts = new Map()
window.addEventListener('keydown', e => {
if (e.key === 'Shift') {
isShiftPressed = true
hihatHead.classList.add('closed')
return
}
const drum = getInstrument(e.key)
if (!drum) {
return
}
drum.classList.add('played')
if (timeouts.has(drum)) {
clearTimeout(timeouts.get(drum))
}
timeouts.set(drum, setTimeout(() => {
drum.classList.remove('played')
}, 100))
})
window.addEventListener('keyup', e => {
if (e.key === 'Shift') {
isShiftPressed = false
hihatHead.classList.remove('closed')
}
})
And now play a (silent) drum solo:
Time for the sound check
I'll use a midi font, lust like with the guitar, but a different one: github.com/johntu/midi-js-gm1-percussion The README tells me which note/mp3 file is corresponding to which drum, so I create another map:
const sounds = new Map()
sounds.set(hihatHead, {
open: new Audio('./sound/Bb2.mp3'),
closed: new Audio('./sound/Gb2.mp3'),
})
sounds.set(hitom, new Audio('./sound/D3.mp3'))
sounds.set(midtom, new Audio('./sound/B2.mp3'))
sounds.set(floortom, new Audio('./sound/G2.mp3'))
sounds.set(snare, new Audio('./sound/D2.mp3'))
sounds.set(crash, new Audio('./sound/Db3.mp3'))
sounds.set(bass, new Audio('./sound/C2.mp3'))
Now I can adjust my event listener to actually play the sound:
window.addEventListener('keydown', e => {
// ...
const drum = getInstrument(e.key)
// ...
let sound = sounds.get(drum)
if (drum === hihatHead) {
sound = isShiftPressed ? sound.closed : sound.open
}
const audio = new Audio('./sound/' + sound + '.mp3')
audio.play()
const drum = getInstrument(e.key);
// ..
let sound = sounds.get(drum);
if (drum === hihatHead) {
sound = isShiftPressed ? sound.closed : sound.open;
}
sound.pause();
sound.currentTime = 0;
sound.play();
// ...
})
That's it! A working drum kit to accompany the guitar! Here's a live demo for you to play:
Takeaway thoughts
I was a lot faster with this one, now that I've mastered the art of SVG-instrument-forging. Nevertheless, I had a lot of fun building this. If you like, you can record your best solo and post a link to the video in the description!
In the next post of this series, I'm going to connect the two instruments so you can finally form your own digital rock band!
I hope you enjoyed reading this article as much as I enjoyed writing it! If so, leave a ❤️ or a 🦄! I write tech articles in my free time and like to drink coffee every once in a while.
If you want to support my efforts, buy me a coffee ☕ or follow me on Twitter 🐦!
Top comments (4)
That's really cool and very novel idea. Very cool to see someone take some ideas and put them together in a totally new way. Lot's of fun and a great article and nice code too.
Also very cool to see someone carry out an entire idea -- not just show me a few scripts or algos. This is an entire idea completed. Lots of time and effort put into this.
Thanks for sharing.👍🏽👍🏽👍🏽
So glad you like it! I built the guitar originally as an Easter egg for a project I was working on but thought it was too awesome not to share. The idea with the digital rock band was formed in the comments of the guitar-post, so I thought "why not actually do that?" - the next step was logical: Create more instruments :)
Sehr cool Pascal 🥁👍
Freut mich, dass es dir gefällt! :)