- Lab
- Core Tech

Guided: Build a Drum Machine with JavaScript
In this code lab, learners will build an interactive web-based drum machine that allows users to create and modify rhythmic patterns for various drum elements like kick, snare, and hihat. Key learning topics covered include DOM Manipulation, event handling, data structures, looping & iteration, timed actions, and string manipulation.

Path Info
Table of Contents
-
Challenge
Overview
In this lab, you'll develop an interactive drum machine for the web. This tool will let users design patterns for drum elements such as kick, snare, and hihat. You will use 'x' to indicate an active beat and '-' for a rest. Users can toggle between these beats with a simple click. The patterns loop at a set BPM, and the drum sounds come from HTML audio samples.
How to Use the Drum Machine
You can find the working drum machine in the Web Browser tab on the right. As you add functionality, you'll gain the ability to start and stop the beat playback.
Note:
- After adding new functionality, refresh the Web Browser tab to see the updates.
- If you create or modify a beat pattern in the UI, stop the playback first, then start it again to hear the changes.
Let's get started!
-
Challenge
Creating Track Elements
Overview
The
createTrackElement
function is designed to create a DOM element that visualizes a drum track's label, such as "Kick:", "Snare:" or "Hihat".Function Signature:
createTrackElement(track)
:- Input:
track
: A string that symbolizes the name of a drum track, like 'kick', 'snare', or 'hihat'.
- Output: A DOM
<div>
element that portrays the drum track's label.
Construct the Element.
Create a
<div>
element and assign it to a variable namedtrackEl
.👣 Steps
- Use the
document.createElement
method. - Specify the type of the element, in this case,
div
. - Assign this new element to the
trackEl
variable.
🔍 Hint
To create a new HTML
<div>
element in JavaScript, use thedocument.createElement
method. Specifydiv
as the type of element you want to create. After creation, assign it to a variable namedtrackEl
.🔑 Solution
const trackEl = document.createElement('div');
Assigning Class Names.
Set the
className
oftrackEl
totrack
.👣 Steps
- Access and modify the
className
property oftrackEl
.
🔍 Hint
You can set the desired class for
trackEl
by updating itsclassName
property.🔑 Solution
trackEl.className = 'track';
Inserting the Track Name.
Update
trackEl
'sinnerHTML
with a string that includes a<span>
element. The text inside the span should be the track's name, with the first letter in uppercase.👣 Steps
- Modify the
innerHTML
oftrackEl
. - Use a string with embedded JavaScript expressions through template literals.
- Adjust the
track
string to display its first character in uppercase.
🔍 Hint
Consider how you can insert content into an HTML element. The goal is to display the first letter of the
track
variable in uppercase, followed by the remaining string. Template literals (using backticks) can help you embed dynamic content within the HTML structure.🔑 Solution
trackEl.innerHTML = `<span>${track[0].toUpperCase() + track.slice(1)}</span>`;
Return the Created Element.
Return the
trackEl
variable.👣 Steps
- Use the
return
keyword before the variable you want to return.
🔍 Hint
To return a result from a function, use the
return
keyword.🔑 Solution
return trackEl;
- Input:
-
Challenge
Creating Step Elements
Overview
The
createStepElement
function is designed to create a visual representation for each beat of a drum track. Each beat can have a toggle event to activate or deactivate it.Function Signature:
createStepElement(track, pattern, index)
:- Inputs:
track
: A string representing the drum track's name.pattern
: A string indicating the drum track's pattern (e.g., "x-------x-------").index
: An integer pointing to a specific beat within the pattern.
- Output: A DOM
<div>
element representing a specific beat within a drum track's pattern.
Create a New
<div>
Element.Create a div element and assign it to a variable named
stepEl
.👣 Steps
- Call the
document.createElement
method. - Specify the element type, in this case,
div
. - Assign the created element to the
stepEl
variable.
🔍 Hint
To create a new HTML
<div>
element, use thedocument.createElement
method, specifyingdiv
as the element type. After creation, assign it to a variable calledstepEl
.🔑 Solution
const stepEl = document.createElement('div');
Assign Class Names to the Element.
Set the
className
ofstepEl
tostep
. If the beat at the current index in the pattern is'x'
, also add theactive
class.👣 Steps
- Modify the
className
property of the element to assign class names. - Use a ternary operator to conditionally add the
active
class based on the current index of the pattern.
🔍 Hint
You can modify the
className
property of an element to assign classes. To conditionally add theactive
class based on the current index of the pattern, use a ternary operator to check if the beat is'x'
. If true, add theactive
class; otherwise, simply set it tostep
.🔑 Solution
stepEl.className = `step ${pattern[index] === 'x' ? 'active' : ''}`;
Add an Onclick Event Listener.
Attach an onclick event listener to
stepEl
. This listener should toggle theactive
class and update the pattern for the track intrackData
.👣 Steps
- Assign a function to the
onclick
property of thestepEl
to add the event listener. - Inside the function, use the
classList.toggle
method to toggle theactive
class. - Also, make sure the pattern in
trackData
is updated using the appropriate method.
🔍 Hint
To attach an onclick event listener to an element, assign a function to its
onclick
property. Within this function, you can use theclassList.toggle
method to toggle theactive
class onstepEl
. Also, remember to update thetrackData
pattern using the relevant method.🔑 Solution
stepEl.onclick = () => { stepEl.classList.toggle('active'); trackData[track] = updatePattern(trackData[track], index, stepEl.classList.contains('active')); };
Return the Created Element.
Return the
stepEl
variable.👣 Steps
- Use the
return
keyword before the variable you intend to return.
🔍 Hint
Use the
return
keyword to return a result from a function.🔑 Solution
return stepEl;
- Inputs:
-
Challenge
Creating Track Visuals
Overview
Description: The
createTrackVisuals
function is central to rendering the drum machine's visual representation. It processes each track and its corresponding pattern, then generates the necessary visual elements to represent the beats in each track.Function Signature:
createTrackVisuals()
:- Inputs:
- None.
- Output:
- No direct output, but it modifies the DOM by adding the necessary visual elements to represent the tracks and beats.
Building the
createTrackVisuals
function.Iterate over all tracks and their patterns, create visual elements for them, and add them to the main tracks container in the DOM.
👣 Steps
-
Access the Main Tracks Container:
- Retrieve the main tracks container element (
tracksEl
) using thedocument.getElementById
method and the ID 'tracks'.
- Retrieve the main tracks container element (
-
Iterate Over Track Data:
- Loop through each track and its associated pattern in
trackData
usingObject.entries
.
- Loop through each track and its associated pattern in
-
Generate Track Element:
- For each track, create a visual element (
trackEl
) using thecreateTrackElement
function with the track as its argument.
- For each track, create a visual element (
-
Process Each Beat in the Pattern:
- Convert the pattern string into an array and loop through each beat.
-
Create Beat Element:
- For each beat, generate a visual element (
stepEl
) using thecreateStepElement
function. This function should accept the track, pattern, and beat index as its arguments.
- For each beat, generate a visual element (
-
Attach Beat to Track:
- Append the created beat element (
stepEl
) to the track element (trackEl
).
- Append the created beat element (
-
Add Track to Main Container:
- Once all beats are processed and added to the track, append the entire track element (
trackEl
) to the main tracks container (tracksEl
).
- Once all beats are processed and added to the track, append the entire track element (
🔍 Hints
- The
createTrackVisuals
function revolves around the central idea of translating the data (tracks and patterns) into visual elements in the DOM. - Use the
Object.entries
method to access both the track name and its pattern. - The
Array.from
method can transform the pattern string into an array, which can be looped through for beat creation. - Ensure each beat element is appended to its parent track element, and then the track element gets appended to the main container.
🔑 Solution
function createTrackVisuals() { // 1. Access the main tracks container. const tracksEl = document.getElementById('tracks'); // 2. Iterate over track data. for (let [track, pattern] of Object.entries(trackData)) { // 3. Generate track element. const trackEl = createTrackElement(track); // 4. Process each beat in the pattern. Array.from(pattern).forEach((beat, i) => { // 5. Create beat element. const stepEl = createStepElement(track, pattern, i); // 6. Attach beat to track. trackEl.appendChild(stepEl); }); // 7. Add track to main container. tracksEl.appendChild(trackEl); } }
The
createTrackVisuals
function sets up the visual layout of the drum machine, drawing from thetrackData
to generate both track and beat elements in the DOM. These visual components facilitate user interaction with the drum machine.
- Inputs:
-
Challenge
Updating Track Patterns
Overview
The
updatePattern
function is pivotal to the drum machine's functionality. It allows users to change a specific beat in a drum track's pattern, ensuring that the UI's visual cues match the underlying pattern data.Function Signature:
updatePattern(pattern, index, active)
:- Inputs:
pattern
: A string detailing the current drum beat pattern, such as "x--x--x--".index
: An integer, highlighting which beat's status needs modification within the pattern.active
: A boolean denoting the beat's new status. Iftrue
, the beat is active ('x'); otherwise, it's inactive ('-').
- Output:
- Returns a string portraying the revised drum beat pattern.
Creating the
updatePattern
function.Modify the
pattern
string to reflect the status of a specific beat, then return this updated pattern. You'll be furnished with the current pattern, the beat's index to change, and its desired status (active or not).👣 Steps
-
Initiate Function Definition:
- Start by creating a function named
updatePattern
, taking inpattern
,index
, andactive
as parameters.
- Start by creating a function named
-
Segment the Pattern Before the Targeted Index:
- Employ the string's
slice
method onpattern
to capture characters leading up to the givenindex
.
- Employ the string's
-
Create the Beat's Character:
- Use the ternary operation to decide the character based on the
active
parameter:- 'x' is chosen if
active
is true (indicating an active beat). - '-' is selected otherwise (indicating an inactive beat).
- 'x' is chosen if
- Use the ternary operation to decide the character based on the
-
Segment the Pattern Post the Targeted Index:
- Once more, utilize the
slice
method onpattern
to gather characters post the statedindex
.
- Once more, utilize the
-
Merge All Segments:
- String together the initial segment, the beat character, and the latter segment to produce the updated pattern.
-
Return the Updated Pattern:
- Use the
return
keyword to send the newly formed string back from the function.
- Use the
-
Close the Function:
- Finish the function using the suitable closing curly brace.
🔍 Hints
Bear in mind, the
pattern
string signifies beats with either 'x' for the active or '-' for the inactive ones. Given the beat's index and its active status, your mission is to modify that particular beat without affecting the rest.Here's a plan:
- Use the
slice
method to derive the part of thepattern
before the beat you're altering. - Get the new beat's value.
- Extract the section of the
pattern
following the beat under modification. - Merge these segments to produce the updated pattern.
🔑 Solution
function updatePattern(pattern, index, active) { return `${pattern.slice(0, index)}${active ? 'x' : '-'}${pattern.slice(index + 1)}`; }
Using the
slice
method, this function preserves all components of the pattern, save for the beat at the designated index. This specific beat is adjusted as per theactive
parameter, with 'x' symbolizing an active beat and '-' a passive one.
- Inputs:
-
Challenge
Starting the Drum Machine
Overview
The
startDrumMachine
function manages the rhythmic playback of drum samples based on user-defined drum patterns and BPM (Beats Per Minute).Function Steps:
- Stop any ongoing drum playback.
- Convert BPM to milliseconds.
- Define the drum tracks.
- Start the rhythmic playback loop.
Stopping Previous Drum Playback
Stop any currently running drum sequences to prevent overlapping.
stopDrumMachine();
Convert BPM to Milliseconds
Convert the provided BPM value to its equivalent in milliseconds to determine the beat duration.
👣 Steps
-
Retrieve BPM Value:
- Get the DOM element with the ID
bpmInput
. - Extract its value.
- Convert this value to a float using
parseFloat
. - Store the result in a constant called
bpm
.
- Get the DOM element with the ID
-
Calculate Beat Duration:
- Divide 60,000 (milliseconds in a minute) by
bpm
to get the duration of each beat. - Store this in a constant named
step_time
.
- Divide 60,000 (milliseconds in a minute) by
🔍 Hints
Remember that BPM stands for beats per minute. For a BPM of 60, there's one beat every second, which is 1000 milliseconds.
🔑 Solution
const bpm = parseFloat(document.getElementById('bpmInput').value); const step_time = 60000 / bpm;
Define Drum Tracks
Set up the
tracks
array. For each drum type ('kick', 'snare', 'hihat'), get its audio usingdocument.getElementById
and associate it with its rhythm fromtrackData
.👣 Steps
- Set Up Drum Tracks:
- Associate each drum type with its audio and pattern.
🔍 Hints
Consider using arrays and objects to link each drum type with its audio and pattern for easier playback.
🔑 Solution
const tracks = [ { sample: document.getElementById('kick'), steps: trackData.kick }, { sample: document.getElementById('snare'), steps: trackData.snare }, { sample: document.getElementById('hihat'), steps: trackData.hihat } ];
Start Playback Loop
Create a loop that goes through drum patterns, triggering the relevant samples when an active beat (represented by 'x') is found.
👣 Steps
-
Initialize Counter:
- Start a counter called
step
at0
.
- Start a counter called
-
Use
setInterval
:- Use
setInterval
to run a block of code repeatedly at thestep_time
interval. - Within this function:
- Loop through each
track
in thetracks
array. - Check if the current
step
of the track has an active beat ('x'
). - If true, reset the sample's playback position and play the sample.
- After checking all tracks, increment
step
. - Loop
step
back to the start when it reaches the end of the pattern.
- Loop through each
- Use
🔍 Hints
The drum machine should cycle through each position of the drum pattern. Check if each drum should play at the current step. If a drum has an active beat (
'x'
), reset its playback position and play it. Loop back to the start when reaching the end of the pattern.🔑 Solution
let step = 0; drumInterval = setInterval(() => { tracks.forEach(track => { if (track.steps[step] === 'x') { track.sample.currentTime = 0; track.sample.play(); } }); step = (step + 1) % tracks[0].steps.length; }, step_time);
-
Challenge
Stopping the Drum Machine
Overview
stopDrumMachine()
Function- Inputs: None.
- Output: None (Stops ongoing drum playback).
Define the
stopDrumMachine
function.Make sure any ongoing drum sequences are stopped, allowing the drum machine to be cleanly restarted or stopped.
👣 Steps
-
Function Definition:
- Declare the function
stopDrumMachine
.
- Declare the function
-
Active Interval Check:
- Determine if
drumInterval
is assigned (which means the drum machine is active).
- Determine if
-
Stop Playback:
- If the drum machine is active, use
clearInterval
to end the playback loop. ProvidedrumInterval
toclearInterval
.
- If the drum machine is active, use
-
Clear Interval Reference:
- Reset
drumInterval
tonull
to avoid any remaining references and show the drum machine has stopped.
- Reset
-
End Function:
- Close the function using the corresponding curly brace.
🔍 Hint
To stop an interval in JavaScript, keep a reference to that interval. Use
clearInterval
with that reference to stop it. Resetting the reference prevents overlaps when restarting the drum machine.🔑 Solution
function stopDrumMachine() { if (drumInterval) { clearInterval(drumInterval); drumInterval = null; } }
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.