Herzlich willkommen zum SELF-Treffen 2026
vom 24.04. – 26.04.2026
in Halle (Saale)
Beispiel:Karaoke-4.html
<!DOCTYPE html> <html lang="de">
<head>
<meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" media="screen" href="./Beispiel:Grundlayout.css"> <title>Karaoke – Scarborough Fair</title>
<style>
:root {
--zeilen-hoehe: 4.2rem;
--sichtbare-zeilen: 4;
--aktiv-farbe: #fffd59;
--inaktiv-deckkraft: 0.35;
--uebergangs-dauer: 0.35s;
--box-hintergrund: #1a1a2e;
--box-textfarbe: #e0e0e0;
--schriftgroesse: 2rem;
}
body {
font-family: sans-serif;
max-width: 60em;
margin: 1em auto;
padding: 0 1rem 3rem;
}
audio {
width: 100%;
}
.karaoke-box {
position: relative;
margin-top: 1.5rem;
background: var(--box-hintergrund);
border-radius: .75rem;
height: calc(var(--zeilen-hoehe) * var(--sichtbare-zeilen));
overflow: hidden;
}
.karaoke-box::before,
.karaoke-box::after {
content: "";
position: absolute;
left: 0;
right: 0;
height: var(--zeilen-hoehe);
z-index: 1;
pointer-events: none;
}
.karaoke-box::before {
top: 0;
background: linear-gradient(to bottom, var(--box-hintergrund), transparent);
}
.karaoke-box::after {
bottom: 0;
background: linear-gradient(to top, var(--box-hintergrund), transparent);
}
.karaoke-lauf {
transition: transform var(--uebergangs-dauer) ease;
}
.karaoke-zeile {
height: var(--zeilen-hoehe);
display: flex;
align-items: center;
justify-content: center;
font-size: var(--schriftgroesse);
color: var(--box-textfarbe);
opacity: var(--inaktiv-deckkraft);
transition: opacity var(--uebergangs-dauer) ease, color var(--uebergangs-dauer) ease;
padding: 0 1.5rem;
text-align: center;
margin: 0;
}
.karaoke-zeile.aktiv {
opacity: 1;
color: var(--aktiv-farbe);
font-weight: bold;
}
</style>
</head>
<body>
🎤 Scarborough Fair
Karaoke-Player – Text scrollt mit, aktuelle Zeile ist markiert
<audio id="abspieler" controls> <source src="/images/c/c8/Scarborough_Fair.mp3" type="audio/mpeg"> Dein Browser unterstützt das Audio-Element nicht. </audio>
<script>
(function () {
const lrcInhalt = String.raw`[ar:Traditional]
[ti:Scarborough Fair] [offset:0]
[00:00.50]Are you going to Scarborough Fair? [00:04.50]Parsley, sage, rose- mary and thyme, [00:09.50]Remember me to one who lives there, [00:15.50]She once was a true love of mine. [00:23.50]Tell her to make me a cambric shirt, [00:28.50]Parsley, sage, rose- mary and thyme, [00:33.50]Without no seams nor needle- work, [00:38.50]Then she'll be a true love of mine. [00:47.50]Tell her to find me an acre of land, [00:52.50]Parsley, sage, rose- mary and thyme, [00:57.50]Between the salt water and the sea strands, [01:02.50]Then she'll be a true love of mine. [01:11.00]Tell her to plough it with a sickle of leather, [01:16.00]Parsley, sage, rose- mary and thyme, [01:21.00]And bind it all with a bunch of heather, [01:26.00]Then she'll be a true love of mine. [01:35.00]Are you going to Scarborough Fair? [01:40.00]Parsley, sage, rose- mary and thyme, [01:45.00]Remember me to the one who lives there, [01:50.00]She once was a true love of mine.`;
function lrcParsen(rohtext) {
const zeitMuster = /\[(\d{2}):(\d{2}\.\d+)\](.+)/;
return rohtext
.split("\n")
.map(zeile => {
const treffer = zeile.match(zeitMuster);
if (!treffer) return null;
const minuten = parseFloat(treffer[1]);
const sekunden = parseFloat(treffer[2]);
return { zeit: minuten * 60 + sekunden, text: treffer[3].trim() };
})
.filter(Boolean)
.sort((a, b) => a.zeit - b.zeit);
}
const zeilen = lrcParsen(lrcInhalt);
const lauf = document.getElementById("lauf");
const abstandOben = document.createElement("div");
abstandOben.style.height = "var(--zeilen-hoehe)";
lauf.appendChild(abstandOben);
const zeilenElemente = zeilen.map(({ text }) => {
const el = document.createElement("p");
el.className = "karaoke-zeile";
el.textContent = text;
lauf.appendChild(el);
return el;
});
const abstandUnten = document.createElement("div");
abstandUnten.style.height = "var(--zeilen-hoehe)";
lauf.appendChild(abstandUnten);
const zeilenHoehe = zeilenElemente[0].getBoundingClientRect().height;
const zeitstempel = zeilen.map(z => z.zeit);
function aktivenIndexFinden(aktuelleZeit) {
for (let i = zeitstempel.length - 1; i >= 0; i--) {
if (aktuelleZeit >= zeitstempel[i]) return i;
}
return -1;
}
function zuIndexScrollen(index) {
lauf.style.transform = `translateY(${-index * zeilenHoehe}px)`;
}
let aktiverIndex = -1;
const audio = document.getElementById("abspieler");
function karaokeAktualisieren() {
const neuerIndex = aktivenIndexFinden(audio.currentTime);
if (neuerIndex === aktiverIndex) return;
if (aktiverIndex >= 0) zeilenElemente[aktiverIndex].classList.remove("aktiv");
if (neuerIndex >= 0) {
zeilenElemente[neuerIndex].classList.add("aktiv");
zuIndexScrollen(neuerIndex);
}
aktiverIndex = neuerIndex;
}
audio.addEventListener("timeupdate", karaokeAktualisieren);
audio.addEventListener("seeked", karaokeAktualisieren);
audio.addEventListener("play", karaokeAktualisieren);
})();
</script>
</body> </html>