Herzlich willkommen zum SELF-Treffen 2026
vom 24.04. – 26.04.2026 in Halle (Saale)

Beispiel:Web-Audio-04.html

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

<!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>