DEV Community

loading...
Cover image for Let's build an actual working digital Drum kit🥁 you can play with your keyboard⌨️🤘

Let's build an actual working digital Drum kit🥁 you can play with your keyboard⌨️🤘

thormeier profile image Pascal Thormeier ・6 min read

Yes. I've built a guitar already, you can find it here:

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>
Enter fullscreen mode Exit fullscreen mode

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)"
  />
Enter fullscreen mode Exit fullscreen mode

With this result:

SVG Drum kit

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:

Keyboard layout with some keys marked showing which drum they correspond to

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
Enter fullscreen mode Exit fullscreen mode

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
    // ...
  }
})
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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')
  }
})
Enter fullscreen mode Exit fullscreen mode

And now play a (silent) drum solo:

The SVG drum being played

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'))
Enter fullscreen mode Exit fullscreen mode

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();
  // ...
})
Enter fullscreen mode Exit fullscreen mode

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 🐦!

Buy me a coffee button

Discussion (4)

pic
Editor guide
Collapse
raddevus profile image
raddevus • Edited

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.👍🏽👍🏽👍🏽

Collapse
thormeier profile image
Pascal Thormeier Author

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 :)

Collapse
daviddalbusco profile image
David Dal Busco

Sehr cool Pascal 🥁👍

Collapse
thormeier profile image
Pascal Thormeier Author

Freut mich, dass es dir gefällt! :)