piano practice
All checks were successful
/ build (push) Successful in 1m32s

This commit is contained in:
Nycki 2026-04-04 14:12:39 -07:00
parent 74938829fe
commit 23e0c008a8

99
static/piano/index.html Normal file
View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<title>piano</title>
<h1>piano</h1>
<div>
<textarea id="input"></textarea>
</div>
<h2>notes held</h2>
<div id="notes-held"></div>
<script>
const inputArea = document.querySelector('#input');
const notesDiv = document.querySelector('#notes-held');
const ctx = new AudioContext;
const playing = {};
let tone = {};
let setupCompleted = false;
inputArea.onfocus = start;
inputArea.onblur = pause;
// arrange notes like on a piano. the second and fourth rows are each a major scale, but the first and third rows have gaps.
function usePianoKeys() {
tone = {};
("zsxdcvgbhnjm,l.;/q2w3e4rt6y7ui9o0p-[]"
.split('')
.map((key, i) => {
tone[key] = 440 * 2 ** ((i - 20)/12);
})
);
}
// arrange notes so that moving three keys sideways is exactly an octave. makes it easy to play octaves. everything else is wonky though...
function useEqualSteps() {
const rows = [
"1234567890-=",
"qwertyuiop[]",
"asdfghjkl;'",
"zxcvbnm,./",
];
tone = {};
rows.map((row, y) => {
row.split('').map((key, x) => {
const semitone = x * 4 + y - 20;
tone[key] = 440 * 2 ** (semitone / 12);
})
});
}
function start() {
if (setupCompleted) {
addEventListener('keydown', onKeydown);
return;
}
useEqualSteps();
addEventListener('keydown', onKeydown);
addEventListener('keyup', onKeyup);
setupCompleted = true;
}
function pause() {
removeEventListener('keydown', onKeydown);
}
function onKeydown(ev) {
let key = ev.key;
if (key == ' ') key = 'Space';
if (!tone[key]) return;
if (playing[key]) return;
const gain = new GainNode(ctx, { gain: 2 ** -5 });
const oscillator = new OscillatorNode(ctx, { type: 'square', frequency: tone[key] });
gain.connect(ctx.destination);
oscillator.connect(gain);
oscillator.start();
playing[key] = { gain, oscillator };
showNotes();
}
function onKeyup(ev) {
let key = ev.key;
if (key == ' ') key = 'Space';
if (!playing[key]) return;
const { gain, oscillator } = playing[key];
oscillator.stop();
oscillator.disconnect();
gain.disconnect();
delete playing[key];
showNotes();
}
function showNotes() {
notesDiv.innerText = Object.keys(playing).join(' ');
}
</script>