Skip to content

Instantly share code, notes, and snippets.

@yxiao1996
Created June 18, 2021 13:46
Show Gist options
  • Save yxiao1996/53058be48b83b0ef2bd4a1305ca6079c to your computer and use it in GitHub Desktop.
Save yxiao1996/53058be48b83b0ef2bd4a1305ca6079c to your computer and use it in GitHub Desktop.
An Instrument on your Laptop's Keyboard?

Here's the link to a full view on codepen: https://codepen.io/yxiao96/full/LYWgwdg

Hello, I'm here to put down some tracking for a little code project I did over the weekend. This little project is about a web app instrument I build. The instrument can be accessed through a website and the control is your keyboard.

The main idea behind is to allow people play a lot of notes with just a few keys. For this project, there are 6 keys in total, while allowing people to play 24 different notes. People may think that this is no surprise since that 6 variables could have 2 to the power of 6 equals 32 combinations. Yes, that's true, it's possible to have more notes but making an instrument is somewhat different from simply combining switches.

The other aspect of the issue is to make this intrument playable. Imagine that if we simply use binary encoding to map to the keys, like 000000 -> C3, 000001 -> C#3, 000002 -> D3. This encoding could be very hard for the play to remember where to put his/her finger when he/she want to play a certain note. The goal to make the instrument intuitive to play. Here we borrow the concept from guitar, which has essentially 2 sets of controls: one set of controls for controling the pitch, the other set of controls for playing the note. In this project, 3 keys on your left hand side control the pitch, while the 3 keys on your right hand side play the note.

// Audio synth
const synth = new Tone.Synth().toDestination();
var keyIsDown = false;
const rankMap = {
'z' : 0,
'x' : 0,
'c' : 0
};
const stringMap = {
'b' : ["G3", "A3", "B3", "C4", "G#3", "A#3", "B#3", "C#4"],
'n' : ["B3", "C4", "D4", "E4", "B#3", "C#4", "D#4", "E#4"],
'm' : ["E4", "F4", "G4", "A4", "E#4", "F#4", "G#4", "A#4"]
};
const stringStates = {};
for (const [key, value] of Object.entries(stringMap)) {
stringStates[key] = 0;
}
function currentRank() {
var rank = 0;
var i = 0;
for (const [key, value] of Object.entries(rankMap)) {
rank = rank + value * Math.pow(2, i);
i = i + 1;
}
return rank;
}
document.addEventListener('keydown', (event) => {
const keyName = event.key;
if (Object.keys(stringMap).includes(keyName) && keyIsDown == false) {
var rank = currentRank();
synth.triggerAttack(stringMap[keyName][rank], Tone.now());
keyIsDown = true;
stringStates[keyName] = 1;
}
if (Object.keys(rankMap).includes(keyName)) {
rankMap[keyName] = 1;
}
}, false);
document.addEventListener('keyup', (event) => {
const keyName = event.key;
if (Object.keys(stringMap).includes(keyName)) {
synth.triggerRelease(Tone.now());
keyIsDown = false;
stringStates[keyName] = 0;
}
if (Object.keys(rankMap).includes(keyName)) {
rankMap[keyName] = 0;
}
}, false);
// Visual rendering
const canvasSizeFactor = 0.9;
let canvasWidth, canvasHeight;
function setup() {
canvasWidth = windowWidth * canvasSizeFactor;
canvasHeight = windowHeight * canvasSizeFactor;
createCanvas(canvasWidth, canvasHeight);
}
function draw() {
clear();
const squareSize = 100;
var boxIndex = 0;
const numInputs = Object.keys(stringMap).length;
let boxX;
for (const [stringName, notes] of Object.entries(stringMap)) {
boxX = (boxIndex + 1) / (numInputs + 1) * canvasWidth;
drawInputBox(
boxX,
canvasHeight / 2,
squareSize,
stringStates[stringName],
stringMap[stringName][currentRank()]
);
boxIndex = boxIndex + 1;
}
addStaticText();
}
function drawInputBox(x, y, size, state, note) {
if (state == 1) {
fill(0, 255, 0);
} else {
fill(255, 255, 255);
}
square(x-size/2, y-size/2, size);
fill(0, 0, 0);
textSize(32);
text(note, x-size/2, y);
}
function addStaticText() {
const staticText = "Use z, x, c to contorl pitch. Use b, n, m to play note";
fill(0, 0, 0);
textSize(20);
text(staticText, 0, 20);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment