Spaces:
Running
Running
Different colours and polyphony
Browse files- keyboard.html +1 -1
- static/keyboard.js +13 -2
- static/styles.css +24 -0
keyboard.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
| 10 |
<!-- Welcome Header -->
|
| 11 |
<div class="welcome-header">
|
| 12 |
<h1 class="neon-text">🎹 VIRTUAL MIDI KEYBOARD</h1>
|
| 13 |
-
<p class="subtitle">Play
|
| 14 |
</div>
|
| 15 |
|
| 16 |
<div id="mainContainer">
|
|
|
|
| 10 |
<!-- Welcome Header -->
|
| 11 |
<div class="welcome-header">
|
| 12 |
<h1 class="neon-text">🎹 VIRTUAL MIDI KEYBOARD</h1>
|
| 13 |
+
<p class="subtitle">Play chords & melodies • Full polyphony (24 voices) • Record & export • Real-time MIDI monitoring</p>
|
| 14 |
</div>
|
| 15 |
|
| 16 |
<div id="mainContainer">
|
static/keyboard.js
CHANGED
|
@@ -85,32 +85,38 @@ const pressedKeys = new Set();
|
|
| 85 |
|
| 86 |
const instruments = {
|
| 87 |
synth: () => new Tone.PolySynth(Tone.Synth, {
|
|
|
|
| 88 |
oscillator: { type: 'sine' },
|
| 89 |
envelope: { attack: 0.005, decay: 0.1, sustain: 0.3, release: 1 }
|
| 90 |
}).toDestination(),
|
| 91 |
|
| 92 |
piano: () => new Tone.PolySynth(Tone.Synth, {
|
|
|
|
| 93 |
oscillator: { type: 'triangle' },
|
| 94 |
envelope: { attack: 0.001, decay: 0.2, sustain: 0.1, release: 2 }
|
| 95 |
}).toDestination(),
|
| 96 |
|
| 97 |
organ: () => new Tone.PolySynth(Tone.Synth, {
|
|
|
|
| 98 |
oscillator: { type: 'sine4' },
|
| 99 |
envelope: { attack: 0.001, decay: 0, sustain: 1, release: 0.1 }
|
| 100 |
}).toDestination(),
|
| 101 |
|
| 102 |
bass: () => new Tone.PolySynth(Tone.Synth, {
|
|
|
|
| 103 |
oscillator: { type: 'sawtooth' },
|
| 104 |
envelope: { attack: 0.01, decay: 0.1, sustain: 0.4, release: 1.5 },
|
| 105 |
filter: { Q: 2, type: 'lowpass', rolloff: -12 }
|
| 106 |
}).toDestination(),
|
| 107 |
|
| 108 |
pluck: () => new Tone.PolySynth(Tone.Synth, {
|
|
|
|
| 109 |
oscillator: { type: 'triangle' },
|
| 110 |
envelope: { attack: 0.001, decay: 0.3, sustain: 0, release: 0.3 }
|
| 111 |
}).toDestination(),
|
| 112 |
|
| 113 |
fm: () => new Tone.PolySynth(Tone.FMSynth, {
|
|
|
|
| 114 |
harmonicity: 3,
|
| 115 |
modulationIndex: 10,
|
| 116 |
envelope: { attack: 0.01, decay: 0.2, sustain: 0.2, release: 1 }
|
|
@@ -140,7 +146,7 @@ function buildKeyboard() {
|
|
| 140 |
keyEl.dataset.midi = midiNote;
|
| 141 |
|
| 142 |
const shortcut = keyShortcuts[midiNote] || '';
|
| 143 |
-
const shortcutHtml = shortcut ? `<div
|
| 144 |
keyEl.innerHTML = `<div style="padding-bottom:6px;font-size:11px">${shortcutHtml}${k.name}${octaveNum}</div>`;
|
| 145 |
|
| 146 |
keyboardEl.appendChild(keyEl);
|
|
@@ -421,7 +427,12 @@ instrumentSelect.addEventListener('change', () => {
|
|
| 421 |
});
|
| 422 |
|
| 423 |
keyboardToggle.addEventListener('change', () => {
|
| 424 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
// Release all currently pressed keyboard keys
|
| 426 |
pressedKeys.forEach(key => {
|
| 427 |
const midiNote = keyMap[key];
|
|
|
|
| 85 |
|
| 86 |
const instruments = {
|
| 87 |
synth: () => new Tone.PolySynth(Tone.Synth, {
|
| 88 |
+
maxPolyphony: 24,
|
| 89 |
oscillator: { type: 'sine' },
|
| 90 |
envelope: { attack: 0.005, decay: 0.1, sustain: 0.3, release: 1 }
|
| 91 |
}).toDestination(),
|
| 92 |
|
| 93 |
piano: () => new Tone.PolySynth(Tone.Synth, {
|
| 94 |
+
maxPolyphony: 24,
|
| 95 |
oscillator: { type: 'triangle' },
|
| 96 |
envelope: { attack: 0.001, decay: 0.2, sustain: 0.1, release: 2 }
|
| 97 |
}).toDestination(),
|
| 98 |
|
| 99 |
organ: () => new Tone.PolySynth(Tone.Synth, {
|
| 100 |
+
maxPolyphony: 24,
|
| 101 |
oscillator: { type: 'sine4' },
|
| 102 |
envelope: { attack: 0.001, decay: 0, sustain: 1, release: 0.1 }
|
| 103 |
}).toDestination(),
|
| 104 |
|
| 105 |
bass: () => new Tone.PolySynth(Tone.Synth, {
|
| 106 |
+
maxPolyphony: 24,
|
| 107 |
oscillator: { type: 'sawtooth' },
|
| 108 |
envelope: { attack: 0.01, decay: 0.1, sustain: 0.4, release: 1.5 },
|
| 109 |
filter: { Q: 2, type: 'lowpass', rolloff: -12 }
|
| 110 |
}).toDestination(),
|
| 111 |
|
| 112 |
pluck: () => new Tone.PolySynth(Tone.Synth, {
|
| 113 |
+
maxPolyphony: 24,
|
| 114 |
oscillator: { type: 'triangle' },
|
| 115 |
envelope: { attack: 0.001, decay: 0.3, sustain: 0, release: 0.3 }
|
| 116 |
}).toDestination(),
|
| 117 |
|
| 118 |
fm: () => new Tone.PolySynth(Tone.FMSynth, {
|
| 119 |
+
maxPolyphony: 24,
|
| 120 |
harmonicity: 3,
|
| 121 |
modulationIndex: 10,
|
| 122 |
envelope: { attack: 0.01, decay: 0.2, sustain: 0.2, release: 1 }
|
|
|
|
| 146 |
keyEl.dataset.midi = midiNote;
|
| 147 |
|
| 148 |
const shortcut = keyShortcuts[midiNote] || '';
|
| 149 |
+
const shortcutHtml = shortcut ? `<div class="shortcut-hint">${shortcut}</div>` : '';
|
| 150 |
keyEl.innerHTML = `<div style="padding-bottom:6px;font-size:11px">${shortcutHtml}${k.name}${octaveNum}</div>`;
|
| 151 |
|
| 152 |
keyboardEl.appendChild(keyEl);
|
|
|
|
| 427 |
});
|
| 428 |
|
| 429 |
keyboardToggle.addEventListener('change', () => {
|
| 430 |
+
if (keyboardToggle.checked) {
|
| 431 |
+
// Show keyboard shortcuts
|
| 432 |
+
keyboardEl.classList.add('shortcuts-visible');
|
| 433 |
+
} else {
|
| 434 |
+
// Hide keyboard shortcuts
|
| 435 |
+
keyboardEl.classList.remove('shortcuts-visible');
|
| 436 |
// Release all currently pressed keyboard keys
|
| 437 |
pressedKeys.forEach(key => {
|
| 438 |
const midiNote = keyMap[key];
|
static/styles.css
CHANGED
|
@@ -91,6 +91,30 @@ body {
|
|
| 91 |
box-shadow:
|
| 92 |
0 4px 8px rgba(0, 0, 0, 0.3),
|
| 93 |
inset 0 -2px 5px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
}
|
| 95 |
|
| 96 |
.key:hover {
|
|
|
|
| 91 |
box-shadow:
|
| 92 |
0 4px 8px rgba(0, 0, 0, 0.3),
|
| 93 |
inset 0 -2px 5px rgba(0, 0, 0, 0.1);
|
| 94 |
+
color: #2a0050;
|
| 95 |
+
font-weight: bold;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
.key .shortcut-hint {
|
| 99 |
+
font-size: 10px;
|
| 100 |
+
opacity: 0;
|
| 101 |
+
color: #5a00cc;
|
| 102 |
+
font-weight: bold;
|
| 103 |
+
text-shadow: 0 0 2px rgba(90, 0, 204, 0.3);
|
| 104 |
+
transition: opacity 0.2s ease;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
.key.black {
|
| 108 |
+
color: #00ffff;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
.key.black .shortcut-hint {
|
| 112 |
+
color: #00ffff;
|
| 113 |
+
text-shadow: 0 0 3px rgba(0, 255, 255, 0.5);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
.shortcuts-visible .key .shortcut-hint {
|
| 117 |
+
opacity: 1;
|
| 118 |
}
|
| 119 |
|
| 120 |
.key:hover {
|