Herzlich willkommen zum SELF-Treffen 2026
vom 24.04. – 26.04.2026
in Halle (Saale)
Beispiel:Web-Audio-04.html
Aus SELFHTML-Wiki
<!doctype html> <html lang="de"> <head>
<meta charset="UTF-8">
<title>Mini-Klavier</title>
<style>
body {
font-family: system-ui, sans-serif;
padding: 1em;
}
#octave-bar {
display: flex;
align-items: center;
gap: .5em;
margin-top: 1em;
}
#octave-bar button {
width: 3em;
height: 2em;
cursor: pointer;
}
#octave-bar button.active {
font-weight: 700;
}
/* ── Piano container ── */
#piano {
position: relative;
display: flex;
height: 200px;
width: max-content;
user-select: none;
margin-top: 1em;
}
/* ── White keys ── */
.key {
position: relative;
width: 72px;
height: 200px;
background: #fff;
border: thin solid #aaa;
border-top: none;
border-radius: 0 0 5px 5px;
box-shadow: 1px 3px 5px rgba(0,0,0,.15);
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
padding-bottom: 20px;
z-index: 1;
flex-shrink: 0;
color: #333;
}
.key.active { background: #c8daf5; }
/* ── Black keys ── */
.key.black {
position: absolute;
width: 44px;
height: 124px;
background: #222;
border: thin solid #111;
border-top: none;
border-radius: 0 0 4px 4px;
box-shadow: 1px 4px 6px rgba(0,0,0,.45);
z-index: 2;
padding-bottom: 8px;
color: #ccc;
}
.key.black.active { background: #0055cc; }
.key .hint { color: #7aaaf0; }
.key.black .hint { color: #4477cc; }
</style>
</head> <body>
Mini-Klavier: Eine Oktave C–C. Mausklick, Touch oder Tastatur (siehe blaue Hinweise).
Oktave: <button data-shift="-1">−1</button> <button data-shift="0" class="active">0</button> <button data-shift="1">+1</button> C4 – C5
C4A
D4S
E4D
F4F
G4G
A4H
B4J
C5K
C#4W
D#4E
F#4T
G#4Z/Y
A#4U
<script>
document.addEventListener('DOMContentLoaded', function () {
const audioCtx = new AudioContext();
// Frequenz aus Halbtonabstand zu A4 = 440 Hz
function midiToFreq(semitone) {
return 440 * 2 ** ((semitone - 9) / 12);
}
const noteNames = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
function noteName(semi, shift) {
return noteNames[semi % 12] + (4 + shift + Math.floor(semi / 12));
}
// Obertöne
const partials = [
{ mult: 1, gain: 1.00 },
{ mult: 2, gain: 0.50 },
{ mult: 3, gain: 0.25 },
{ mult: 4, gain: 0.12 },
{ mult: 5, gain: 0.06 },
];
function playNote(freq, el) {
if (audioCtx.state === 'suspended') audioCtx.resume();
const t = audioCtx.currentTime;
const master = audioCtx.createGain();
master.gain.setValueAtTime(0.30, t);
master.gain.exponentialRampToValueAtTime(0.10, t + 0.3);
master.gain.setTargetAtTime(0.0001, t + 0.3, 0.6);
master.connect(audioCtx.destination);
partials.forEach(p => {
const osc = audioCtx.createOscillator();
const g = audioCtx.createGain();
osc.frequency.value = freq * p.mult;
g.gain.value = p.gain;
osc.connect(g);
g.connect(master);
osc.start(t);
osc.stop(t + 3);
});
if (el) {
el.classList.add('active');
setTimeout(() => el.classList.remove('active'), 200);
}
}
let octaveShift = 0;
// Keymap: Tastatur-Buchstabe → {freq, el}
function buildKeyMap() {
const map = {};
document.querySelectorAll('#piano .key').forEach(el => {
const key = el.dataset.key;
const semi = parseInt(el.dataset.semi, 10);
const freq = midiToFreq(semi + octaveShift * 12);
map[key] = { freq, el };
// Notennamen im Label aktualisieren
el.querySelector('[data-notename]').textContent = noteName(semi, octaveShift);
});
return map;
}
// Frequenzen und Notennamen nach Oktavwechsel aktualisieren –
// die DOM-Struktur (Tasten) bleibt unverändert
function updateOctave() {
document.getElementById('octaveDisplay').textContent =
`C${4 + octaveShift} – C${5 + octaveShift}`;
return buildKeyMap();
}
let keyMap = buildKeyMap();
// Oktav-Buttons
document.querySelectorAll('#octave-bar button').forEach(btn => {
btn.addEventListener('click', () => {
octaveShift = Number(btn.dataset.shift);
document.querySelectorAll('#octave-bar button')
.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
keyMap = updateOctave();
});
});
// Pointer-Events auf den Tasten selbst verdrahten
document.querySelectorAll('#piano .key').forEach(el => {
el.addEventListener('pointerdown', e => {
e.stopPropagation();
const key = el.dataset.key;
if (keyMap[key]) playNote(keyMap[key].freq, el);
});
});
// Tastatur
document.addEventListener('keydown', e => {
if (e.repeat) return;
const k = e.key.toLowerCase() === 'y' ? 'z' : e.key.toLowerCase();
if (keyMap[k]) playNote(keyMap[k].freq, keyMap[k].el);
});
});
</script>
</body> </html>