diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs
new file mode 100644
index 000000000..dd8772d96
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Hsl.cs
@@ -0,0 +1,243 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.ColorProfiles;
+
+///
+/// Represents a Hsl (hue, saturation, lightness) color.
+///
+public readonly struct Hsl : IColorProfile
+{
+ private static readonly Vector3 Min = Vector3.Zero;
+ private static readonly Vector3 Max = new(360, 1, 1);
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The h hue component.
+ /// The s saturation component.
+ /// The l value (lightness) component.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Hsl(float h, float s, float l)
+ : this(new Vector3(h, s, l))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The vector representing the h, s, l components.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Hsl(Vector3 vector)
+ {
+ vector = Vector3.Clamp(vector, Min, Max);
+ this.H = vector.X;
+ this.S = vector.Y;
+ this.L = vector.Z;
+ }
+
+ ///
+ /// Gets the hue component.
+ /// A value ranging between 0 and 360.
+ ///
+ public readonly float H { get; }
+
+ ///
+ /// Gets the saturation component.
+ /// A value ranging between 0 and 1.
+ ///
+ public readonly float S { get; }
+
+ ///
+ /// Gets the lightness component.
+ /// A value ranging between 0 and 1.
+ ///
+ public readonly float L { get; }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Hsl left, Hsl right) => left.Equals(right);
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right);
+
+ ///
+ public static Hsl FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source)
+ {
+ float r = source.R;
+ float g = source.G;
+ float b = source.B;
+
+ float max = MathF.Max(r, MathF.Max(g, b));
+ float min = MathF.Min(r, MathF.Min(g, b));
+ float chroma = max - min;
+ float h = 0F;
+ float s = 0F;
+ float l = (max + min) / 2F;
+
+ if (MathF.Abs(chroma) < Constants.Epsilon)
+ {
+ return new Hsl(0F, s, l);
+ }
+
+ if (MathF.Abs(r - max) < Constants.Epsilon)
+ {
+ h = (g - b) / chroma;
+ }
+ else if (MathF.Abs(g - max) < Constants.Epsilon)
+ {
+ h = 2F + ((b - r) / chroma);
+ }
+ else if (MathF.Abs(b - max) < Constants.Epsilon)
+ {
+ h = 4F + ((r - g) / chroma);
+ }
+
+ h *= 60F;
+ if (h < 0F)
+ {
+ h += 360F;
+ }
+
+ if (l <= .5F)
+ {
+ s = chroma / (max + min);
+ }
+ else
+ {
+ s = chroma / (2F - max - min);
+ }
+
+ return new Hsl(h, s, l);
+ }
+
+ ///
+ public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ {
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+ for (int i = 0; i < source.Length; i++)
+ {
+ Rgb rgb = source[i];
+ destination[i] = FromProfileConnectingSpace(options, in rgb);
+ }
+ }
+
+ ///
+ public Rgb ToProfileConnectingSpace(ColorConversionOptions options)
+ {
+ float rangedH = this.H / 360F;
+ float r = 0;
+ float g = 0;
+ float b = 0;
+ float s = this.S;
+ float l = this.L;
+
+ if (MathF.Abs(l) > Constants.Epsilon)
+ {
+ if (MathF.Abs(s) < Constants.Epsilon)
+ {
+ r = g = b = l;
+ }
+ else
+ {
+ float temp2 = (l < .5F) ? l * (1F + s) : l + s - (l * s);
+ float temp1 = (2F * l) - temp2;
+
+ r = GetColorComponent(temp1, temp2, rangedH + 0.3333333F);
+ g = GetColorComponent(temp1, temp2, rangedH);
+ b = GetColorComponent(temp1, temp2, rangedH - 0.3333333F);
+ }
+ }
+
+ return new Rgb(r, g, b);
+ }
+
+ ///
+ public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ {
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+ for (int i = 0; i < source.Length; i++)
+ {
+ Hsl hsl = source[i];
+ destination[i] = hsl.ToProfileConnectingSpace(options);
+ }
+ }
+
+ ///
+ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource()
+ => ChromaticAdaptionWhitePointSource.RgbWorkingSpace;
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L);
+
+ ///
+ public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})");
+
+ ///
+ public override bool Equals(object? obj) => obj is Hsl other && this.Equals(other);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(Hsl other)
+ => this.H.Equals(other.H)
+ && this.S.Equals(other.S)
+ && this.L.Equals(other.L);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float GetColorComponent(float first, float second, float third)
+ {
+ third = MoveIntoRange(third);
+ if (third < 0.1666667F)
+ {
+ return first + ((second - first) * 6F * third);
+ }
+
+ if (third < .5F)
+ {
+ return second;
+ }
+
+ if (third < 0.6666667F)
+ {
+ return first + ((second - first) * (0.6666667F - third) * 6F);
+ }
+
+ return first;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float MoveIntoRange(float value)
+ {
+ if (value < 0F)
+ {
+ value++;
+ }
+ else if (value > 1F)
+ {
+ value--;
+ }
+
+ return value;
+ }
+}