Have you ever wanted to play a virtual piano directly from your browser? With the power of JavaScript and libraries like Tone.js, creating an interactive piano is not only fun but also an excellent way to learn about web audio and DOM manipulation. In this tutorial, we’ll walk through how you can build a functional piano using HTML, CSS, and JavaScript.
By the end, you’ll have a mini piano you can play using both your mouse and keyboard, all within your browser! Let’s dive in.
Prerequisites
Before we begin, you need a basic understanding of:
- HTML and CSS for structuring and styling the piano keys.
- JavaScript to handle user interaction and play audio.
- The Tone.js library, which simplifies audio synthesis and manipulation.
What We Will Cover
- Creating the piano layout using HTML.
- Styling the keys with CSS.
- Using JavaScript and Tone.js to generate piano sounds.
- Adding interactivity for both mouse clicks and keyboard presses.
Follow this video for complete guidance :
Setting Up the HTML Structure
First, let’s build the structure of our piano. We’ll create the white and black keys using simple elements.
<div class="piano">
<div class="key" data-note="C">C</div>
<div class="key black" data-note="C#">C#</div>
<div class="key" data-note="D">D</div>
<div class="key black" data-note="D#">D#</div>
<div class="key" data-note="E">E</div>
<div class="key" data-note="F">F</div>
<div class="key black" data-note="F#">F#</div>
<div class="key" data-note="G">G</div>
<div class="key black" data-note="G#">G#</div>
<div class="key" data-note="A">A</div>
<div class="key black" data-note="A#">A#</div>
<div class="key" data-note="B">B</div>
</div>
Each key has a data-note attribute, which corresponds to the musical note it represents. This will help us play the correct note when the user interacts with the keys.
Styling the Piano with CSS
To make the piano look realistic, we’ll use CSS to differentiate between the white and black keys. The white keys will be larger, and the black keys will be positioned on top.
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}
.piano {
display: flex;
}
.key {
width: 60px;
height: 200px;
border: 1px solid #000;
background-color: #fff;
margin: 0 2px;
display: flex;
justify-content: center;
align-items: flex-end;
padding-bottom: 10px;
cursor: pointer;
user-select: none;
}
.key.black {
background-color: #000;
color: #fff;
height: 120px;
width: 40px;
margin: 0 -22px;
z-index: 1;
}
.key.active {
background-color: #ddd;
}
.key.black.active {
background-color: #333;
}
Playing Sounds with JavaScript and Tone.js
Now, let’s bring our piano to life by integrating sound. We’ll use Tone.js to generate the piano notes. Tone.js makes it easy to synthesize sound and handle music-related tasks in JavaScript.
We will first load the Tone.js library:
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
Next, we’ll write the JavaScript to play sounds when a key is clicked or pressed on the keyboard.
Adding Interactivity with JavaScript
We’ll use the querySelectorAll method to select all the keys and attach event listeners for mouse clicks and keyboard presses.
Here’s the JavaScript code:
const synth = new Tone.Synth().toDestination(); // Create a new Tone.js synthesizer
const keys = document.querySelectorAll('.key');
const keyMap = {
'a': 'C', 'w': 'C#', 's': 'D', 'e': 'D#', 'd': 'E', 'f': 'F',
't': 'F#', 'g': 'G', 'y': 'G#', 'h': 'A', 'u': 'A#', 'j': 'B'
};
keys.forEach(key => {
key.addEventListener('mousedown', () => playNote(key.dataset.note));
key.addEventListener('mouseup', () => stopNote());
key.addEventListener('mouseleave', () => stopNote());
});
document.addEventListener('keydown', (e) => {
if (keyMap[e.key] && !e.repeat) {
const note = keyMap[e.key];
playNote(note);
const key = document.querySelector(`.key[data-note="${note}"]`);
if (key) key.classList.add('active');
}
});
document.addEventListener('keyup', (e) => {
if (keyMap[e.key]) {
stopNote();
const note = keyMap[e.key];
const key = document.querySelector(`.key[data-note="${note}"]`);
if (key) key.classList.remove('active');
}
});
function playNote(note) {
synth.triggerAttack(`${note}4`);
}
function stopNote() {
synth.triggerRelease();
}
Complete Source Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Piano using JavaScript</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}
.piano {
display: flex;
}
.key {
width: 60px;
height: 200px;
border: 1px solid #000;
background-color: #fff;
margin: 0 2px;
display: flex;
justify-content: center;
align-items: flex-end;
padding-bottom: 10px;
cursor: pointer;
user-select: none;
}
.key.black {
background-color: #000;
color: #fff;
height: 120px;
width: 40px;
margin: 0 -22px;
z-index: 1;
}
.key.active {
background-color: #ddd;
}
.key.black.active {
background-color: #333;
}
</style>
</head>
<body>
<div class="piano">
<div class="key" data-note="C">C</div>
<div class="key black" data-note="C#">C#</div>
<div class="key" data-note="D">D</div>
<div class="key black" data-note="D#">D#</div>
<div class="key" data-note="E">E</div>
<div class="key" data-note="F">F</div>
<div class="key black" data-note="F#">F#</div>
<div class="key" data-note="G">G</div>
<div class="key black" data-note="G#">G#</div>
<div class="key" data-note="A">A</div>
<div class="key black" data-note="A#">A#</div>
<div class="key" data-note="B">B</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
<script>
const synth = new Tone.Synth().toDestination();
const keys = document.querySelectorAll('.key');
const keyMap = {
'a': 'C', 'w': 'C#', 's': 'D', 'e': 'D#', 'd': 'E', 'f': 'F',
't': 'F#', 'g': 'G', 'y': 'G#', 'h': 'A', 'u': 'A#', 'j': 'B'
};
keys.forEach(key => {
key.addEventListener('mousedown', () => playNote(key.dataset.note));
key.addEventListener('mouseup', () => stopNote());
key.addEventListener('mouseleave', () => stopNote());
});
document.addEventListener('keydown', (e) => {
if (keyMap[e.key] && !e.repeat) {
const note = keyMap[e.key];
playNote(note);
const key = document.querySelector(`.key[data-note="${note}"]`);
if (key) key.classList.add('active');
}
});
document.addEventListener('keyup', (e) => {
if (keyMap[e.key]) {
stopNote();
const note = keyMap[e.key];
const key = document.querySelector(`.key[data-note="${note}"]`);
if (key) key.classList.remove('active');
}
});
function playNote(note) {
synth.triggerAttack(`${note}4`);
}
function stopNote() {
synth.triggerRelease();
}
</script>
</body>
</html>
Playing the Piano
With this setup, you can now interact with the piano by clicking the keys or pressing corresponding letters on your keyboard:
- Clicking the Keys: When you click a key, the note corresponding to that key will play.
- Using the Keyboard: Each note is mapped to a specific letter (e.g., ‘a’ for C, ‘s’ for D). You can play the piano using these keys on your keyboard.
In this tutorial, we built a virtual piano using JavaScript, Tone.js, and some basic HTML and CSS. We learned how to:
- Create the piano layout using HTML.
- Style the keys with CSS.
- Generate sound using the Tone.js library.
- Add interactivity so that users can play notes either by clicking or pressing keys on their keyboard.
This piano is a great starting point for building more complex musical applications. You can extend it by adding more octaves, adding sustain pedals, or even recording the notes played.
Now, it’s your turn! Try enhancing this virtual piano or build your own version with additional features. Happy coding and happy music-making!
