Herzlich willkommen zum SELF-Treffen 2026
vom 24.04. – 26.04.2026
in Halle (Saale)
Beispiel:Web-Audio-07.html
Aus SELFHTML-Wiki
<!DOCTYPE html> <html lang="de"> <head>
<meta charset="UTF-8">
<title>Mini-Mischpult für eine Audiodatei</title>
<style type="text/css">
body {
font-family: monospace;
}
.controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15em, 1fr));
gap: 3em;
max-width: 70em;
}
.control-group {
display: flex;
flex-direction: column;
gap: .5em;
}
input[type="range"] {
width: 100%;
cursor: pointer;
}
.value {
color: #888;
}
.buttons {
display: flex;
gap: 1em;
margin-bottom: 1.5rem;
}
button {
font-family: monospace;
font-size: 1em;
padding: 1em;
cursor: pointer;
}
button:disabled {
opacity: 0.35;
cursor: default;
}
#status {
color: red;
margin-bottom: 2em;
}
</style>
</head> <body>
Mini-Mischpult für eine Audiodatei
Status: Bereit.
<label for="volume">Lautstärke</label>
<input type="range" id="volume" min="0" max="1" step="0.01" value="0.8">
0.80
<label for="pan">Balance links/rechts</label>
<input type="range" id="pan" min="-1" max="1" step="0.01" value="0">
Mitte
<label for="reverbGain">Hall – Stärke</label>
<input type="range" id="reverbGain" min="0" max="0.9" step="0.01" value="0">
0.00
<label for="echoGain">Echo – Stärke</label>
<input type="range" id="echoGain" min="0" max="0.9" step="0.01" value="0">
0.00
<label for="echoDelay">Echo – Tempo (Sekunden)</label>
<input type="range" id="echoDelay" min="0.1" max="1.0" step="0.01" value="0.3">
0.30 s
<script> document.addEventListener('DOMContentLoaded', function () {
const AUDIO_URL = '/images/b/bd/Ouvertuere_trocken.mp3'; // Datei-URL im Wiki
let audioContext = null; let audioBuffer = null; let source = null;
// Nodes – werden beim ersten Start aufgebaut let gainNode = null; let pannerNode = null; let reverbDelay = null; let reverbFeedback = null; let echoDelay = null; let echoFeedback = null;
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const status = document.getElementById('status');
// --- AudioContext ---
function getAudioContext() {
if (!audioContext) {
audioContext = new AudioContext();
}
return audioContext;
}
// --- Audiodatei laden ---
async function loadAudio() {
if (audioBuffer) return audioBuffer;
status.textContent = 'Status: Lade Audiodatei';
const audioCtx = getAudioContext();
const response = await fetch(AUDIO_URL);
const arrayBuffer = await response.arrayBuffer();
audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
status.textContent = 'Status: Bereit';
return audioBuffer;
}
// --- Audiograph aufbauen ---
// BufferSource → Gain (Lautstärke) → StereoPanner (Balance)
// ↓
// ReverbDelay (Feedback-Loop)
// ↓
// EchoDelay (Feedback-Loop)
// ↓
// Destination
function buildGraph(audioCtx) {
gainNode = audioCtx.createGain();
pannerNode = audioCtx.createStereoPanner();
reverbDelay = audioCtx.createDelay(5.0);
reverbFeedback= audioCtx.createGain();
echoDelay = audioCtx.createDelay(2.0);
echoFeedback = audioCtx.createGain();
// Lautstärke → Balance → Reverb-Loop → Echo-Loop → Ausgang gainNode.connect(pannerNode); pannerNode.connect(reverbDelay); reverbDelay.connect(reverbFeedback); reverbFeedback.connect(reverbDelay); // Feedback-Schleife Hall reverbDelay.connect(echoDelay); echoDelay.connect(echoFeedback); echoFeedback.connect(echoDelay); // Feedback-Schleife Echo echoDelay.connect(audioCtx.destination);
// Aktuelle Reglerwerte übernehmen applyValues(); }
function applyValues() {
if (!gainNode) return;
gainNode.gain.value = parseFloat(document.getElementById('volume').value);
pannerNode.pan.value = parseFloat(document.getElementById('pan').value);
reverbDelay.delayTime.value = 0.03; // kurze Verzögerung für Hallcharakter
reverbFeedback.gain.value = parseFloat(document.getElementById('reverbGain').value);
echoDelay.delayTime.value = parseFloat(document.getElementById('echoDelay').value);
echoFeedback.gain.value = parseFloat(document.getElementById('echoGain').value);
}
// --- Start ---
async function startAudio() {
try {
const audioCtx = getAudioContext();
if (!audioBuffer) await loadAudio();
if (audioCtx.state === 'suspended') await audioCtx.resume();
buildGraph(audioCtx);
// Für jede Wiedergabe muss eine neue BufferSourceNode erzeugt werden –
// eine Node kann nur einmal abgespielt werden.
source = audioCtx.createBufferSource();
source.buffer = audioBuffer;
source.loop = true;
source.connect(gainNode);
source.start();
startBtn.disabled = true;
stopBtn.disabled = false;
status.textContent = 'Status: Spielt';
} catch (err) {
status.textContent = 'Status: Fehler (' + err.message + ')';
console.error(err);
}
}
// --- Stop ---
function stopAudio() {
if (source) {
source.stop();
source = null;
}
startBtn.disabled = false;
stopBtn.disabled = true;
status.textContent = 'Status: Gestoppt';
}
// --- Regler-Events ---
function linkSlider(id, valId, format, apply) {
const slider = document.getElementById(id);
const display = document.getElementById(valId);
slider.addEventListener('input', function () {
display.textContent = format(this.value);
if (gainNode) apply(parseFloat(this.value));
});
display.textContent = format(slider.value);
}
linkSlider('volume', 'volumeVal',
v => parseFloat(v).toFixed(2),
v => gainNode.gain.value = v
);
linkSlider('pan', 'panVal',
v => v == 0 ? 'Mitte' : (v < 0 ? 'L ' : 'R ') + Math.abs(parseFloat(v)).toFixed(2),
v => pannerNode.pan.value = v
);
linkSlider('reverbGain', 'reverbGainVal',
v => parseFloat(v).toFixed(2),
v => reverbFeedback.gain.value = v
);
linkSlider('echoGain', 'echoGainVal',
v => parseFloat(v).toFixed(2),
v => echoFeedback.gain.value = v
);
linkSlider('echoDelay', 'echoDelayVal',
v => parseFloat(v).toFixed(2) + ' s',
v => echoDelay.delayTime.value = v
);
startBtn.addEventListener('click', startAudio);
stopBtn.addEventListener('click', stopAudio);
}); </script>
</body> </html>