HSL to RGB

Auf dieser Seite erkläre ich, wie man die Farbtonwerte des hsl() Farbsystems in RGB Werte umwandelt. Wir lernen das Prinzip der Farbmodelle HSL und RGB kennen und programmieren Schritt für Schritt, einen Javascript Konverter von HSL zu RGB.

Farbton zu RGB

Damit es zu Anfang nicht zu kompliziert ist, lasse ich die Sättigung auf 100% und Helligkeit auf 50%. Das heißt es geht erstmal nur um die Umrechnung der 360 Farbtonwerte auf dem 360° Farbkreis in RGB also in die Werte Rot, Grün und Blau .

Basics zu RGB und HSB findet ihr in meinem Grundlagen Bildverarbeitung & Farbmodelle Tutorial

Auf den HSL-Reglern kannst du die Farbe in RGB anzeigen lassen. Belasse die Sättigung auf 100% und die Helligkeit auf 50%. Das ergibt eine kräftige satte Farbe.

Verändere den Farbton von einer Seite zur anderen und beobachte, wie die Werte für Rot, Grün und Blau ansteigen, eine Zeit lang stehen bleiben und dann wieder absteigen.

Ihr habt die Funktion und könnt mit folgendem Slider die Werte der Variablen beobachten.

        function hToRgb(h) {
        // 1. Den Winkel auf 0-360 normieren
        h = h % 360;

        // 2. Wir teilen den Kreis in 6 Teile (je 60 Grad)
        // Ein Teil entspricht 255 Einheiten in RGB
        const sector = Math.floor(h / 60);
        const fraction = (h / 60) - sector;
        const x = Math.round(255 * fraction); // Der "Anstieg" oder "Abfall"
        const invX = 255 - x;

        switch(sector) {
        case 0: return `rgb(255, ${x}, 0)`; // Rot voll, Grün steigt
        case 1: return `rgb(${invX}, 255, 0)`; // Grün voll, Rot fällt
        case 2: return `rgb(0, 255, ${x})`; // Grün voll, Blau steigt
        case 3: return `rgb(0, ${invX}, 255)`; // Blau voll, Grün fällt
        case 4: return `rgb(${x}, 0, 255)`; // Blau voll, Rot steigt
        case 5: return `rgb(255, 0, ${invX})`; // Rot voll, Blau fällt
        }
        }

      

Farbton und Sättigung zu RGB

Nun folgt der nächste Schritt. Wir behalten die Helligkeit bei 50% und es kommt die Sättigung hinzu. Wenn die Sättigung (S) sinkt, „ziehen“ wir die RGB-Werte zur Mitte hin zusammen.Bei 100 % Sättigung liegt der tiefste Wert bei 0. Wenn wir die Sättigung senken, steigt dieser Boden an. Die Farbe wird „blasser“, weil sich die RGB-Kanäle angleichen. Bedenke, wenn alle 3 RGB Werte gleich sind, erhält man einen Grauton.

Was hat sich mathematisch geändert?

        function hsToRgb(h, s) {
        h = h % 360;
        s = s / 100; // Umwandlung in 0 bis 1

        // Bei 50% Helligkeit ist dies der maximale Ausschlag nach oben/unten
        const range = 255 * s;
        const high = 127.5 + (range / 2); // Der höchste Wert (bei 100% S = 255)
        const low = 127.5 - (range / 2); // Der tiefste Wert (bei 100% S = 0)

        const sector = Math.floor(h / 60);
        const fraction = (h / 60) - sector;

        // Berechnung der ansteigenden und abfallenden Flanke innerhalb der Range
        const rise = Math.round(low + (high - low) * fraction);
        const fall = Math.round(high - (high - low) * fraction);

        const hi = Math.round(high);
        const lo = Math.round(low);

        switch(sector) {
        case 0: return `rgb(${hi}, ${rise}, ${lo})`; // Rot stabil hoch, Grün steigt
        case 1: return `rgb(${fall}, ${hi}, ${lo})`; // Grün stabil hoch, Rot fällt
        case 2: return `rgb(${lo}, ${hi}, ${rise})`; // Grün stabil hoch, Blau steigt
        case 3: return `rgb(${lo}, ${fall}, ${hi})`; // Blau stabil hoch, Grün fällt
        case 4: return `rgb(${rise}, ${lo}, ${hi})`; // Blau stabil hoch, Rot steigt
        case 5: return `rgb(${hi}, ${lo}, ${fall})`; // Rot stabil hoch, Blau fällt
        }
        }

        // Vergleich:
        console.log(hsToRgb(0, 100)); // "rgb(255, 0, 0)" (Klassisches Rot)
        console.log(hsToRgb(0, 50)); // "rgb(191, 64, 64)" (Blasses Rot)
        console.log(hsToRgb(0, 0)); // "rgb(128, 128, 128)" (Grau - alle Kanäle gleich)

      

HSL zu RGB

Hier ist der finale Schritt. Um die Helligkeit (L) zu integrieren, müssen wir das „Fenster“ (die Werte für high und low) vertikal verschieben.

Berechnung der Range: Wie stark weichen die Farben voneinander ab?
Die Range ist bei 50% Helligkeit am größten und wird zu 0% und 100% hin kleiner.

Die Formel range = s * (1 - Math.abs(2 * l - 1)) sorgt mathematisch dafür, dass dieser Spielraum zu den Rändern (Schwarz und Weiß) hin schrumpft. Mit Math.abs() wird eine Zeltform erzeugt: hoch bei 50%, runter bei den Enden.
Schritt für Schritt erklärt:
  1. Der Kern: (2 * l - 1)
    Da l von 0 bis 1 geht, verschiebt dieser Teil den Bereich:
    • Ist l = 0.5 (50%), ergibt das: 2 * 0.5 - 1 = 0 .
    • Ist l = 1 (100%), ergibt das: 2 * 1 - 1 = 1 .
    • Ist l = 0 (0%), ergibt das: 2 * 0 - 1 = -1 .
  2. Der Betrag: Math.abs(...)
    Hierdurch wird alles positiv. Wir wollen wissen, wie weit wir von der „perfekten Mitte“ (0.5) entfernt sind:
    • Bei 50% Helligkeit ist der Abstand 0
    • Bei 0% und 100% Helligkeit ist der Abstand 1
  3. Die Umkehrung: 1 - Abstand
    Jetzt drehen wir es um, um die verfügbare Range zu erhalten:
    • In der Mitte (Abstand 0) ist die Range: 1 - 0 = 1 (Maximaler Platz für Farbe).
    • An den Enden (Abstand 1) ist die Range: 1 - 1 = 0 (Kein Platz für Farbe, alles wird Schwarz oder Weiß).
  4. Der Sättigungs-Faktor: s * ...
    Zum Schluss multiplizieren wir das mit der Sättigung. Wenn du nur 50% Sättigung willst, nutzt du eben nur die Hälfte der maximal möglichen Range aus.
Ein konkretes Rechenbeispiel:
Stell dir vor, du hast Helligkeit 75% (l = 0.75 ) und Sättigung 100% (s = 1 ):
Das Ergebnis: Deine Farbe schwankt nicht mehr zwischen 0 und 255, sondern nur noch in einem Fenster von 127.5 Einheiten (zwischen 127.5 und 255), weil der „Boden“ durch die Helligkeit angehoben wurde.

        function hslToRgb(h, s, l) {
        h = h % 360;
        s /= 100;
        l /= 100;

        // Berechnung der Range: Wie stark weichen die Farben voneinander ab?
        // Die Range ist bei 50% Helligkeit am größten und wird zu 0% und 100% hin kleiner.
        const range = s * (1 - Math.abs(2 * l - 1));

        const high = (l + range / 2) * 255;
        const low = (l - range / 2) * 255;

        const sector = Math.floor(h / 60);
        const fraction = (h / 60) - sector;

        const rise = Math.round(low + (high - low) * fraction);
        const fall = Math.round(high - (high - low) * fraction);
        const hi = Math.round(high);
        const lo = Math.round(low);

        switch(sector) {
        case 0: return `rgb(${hi}, ${rise}, ${lo})`; // Rot-Gelb
        case 1: return `rgb(${fall}, ${hi}, ${lo})`; // Gelb-Grün
        case 2: return `rgb(${lo}, ${hi}, ${rise})`; // Grün-Cyan
        case 3: return `rgb(${lo}, ${fall}, ${hi})`; // Cyan-Blau
        case 4: return `rgb(${rise}, ${lo}, ${hi})`; // Blau-Magenta
        case 5: return `rgb(${hi}, ${lo}, ${fall})`; // Magenta-Rot
        }
        }

        // Tests:
        console.log(hslToRgb(0, 100, 50)); // "rgb(255, 0, 0)" (Reines Rot)
        console.log(hslToRgb(0, 100, 25)); // "rgb(128, 0, 0)" (Dunkelrot)
        console.log(hslToRgb(0, 100, 75)); // "rgb(255, 128, 128)" (Hellrot/Rosa)
        console.log(hslToRgb(0, 0, 50)); // "rgb(128, 128, 128)" (Grau)