8000 GitHub - nvlang/egal: Reparametrization of OkLCh/HCT to simplify uniformity in color saturation.
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

nvlang/egal

Repository files navigation



Note

Getting started: Refer to the README of the standalone package, PostCSS plugin, or Lightning CSS plugin, depending on your use case.

Why?

Problem

Color spaces like OkLCh and HCT aim to be perceptually uniform, and they can be incredibly useful if consistency of lightness across hues is important. However, it can be difficult to ensure consistency of chroma (saturation) across hues. This is because not all hues can deliver the same chroma at a given lightness.

For example, if you take the color oklch(50% 0.275 280) #5627f8, you get a very saturated purple. However, if you shift the hue to 200, you'll find that an sRGB monitor cannot truly display the color oklch(50% 0.275 200), and instead will silently fall back to oklch(50% 0.0848 200) #017176.

Idea

Now, how can we make colors of different hues but equal lightness appear equally saturated? One way, of course, is to 8000 set the chroma to zero, but this would just yield the same shade of gray independent of hue. Alternatively, one can try to find the largest chroma value that all the different hues can still deliver at the given lightness. That's precisely what égal (IPA /e.ɡal/ — from French, meaning "equal" or "indifferent") does. It maps the absolute scale of chroma embedded in OkLCh into a scale that is relative to the aforementioned maximal chroma.


Graph titled 'oklch( 0.5, [0,0.35], [0,360) )', with hue as x-axis and chroma as y-axis. The hue ranges from 0 to 360 degrees, and the chroma ranges from 0 to 0.35 on the left side, which is labeled 'oklch', and from 0% to 400% on the right side, which is labeled 'egal'. At coordinate (x,y), the graph shows a pixel of color `oklch(0.5,y,x)`, where y is understood with the oklch scale for chroma. Only colors which are visible in sRGB are shown in the graph, yielding a shape with a colorful but irregular top with occasional spikes, and a monotonous gray bottom. A dashed, white, straight horizontal line is drawn across this colorful shape around y coordinate 0.085 in the oklch scale and 100% in the egal scale, and intersects with the global minimum of the shape, around hue 200 degrees.
At 50% lightness, the bottleneck for chroma is around hue 200 degrees.

The tradeoff here is that we lose consistency of chroma values across lightnesses:


Graph titled 'oklch( [0,1], [0,0.35], 0 )', with lightness as x-axis and chroma as y-axis (both using the OkLCh scale). A pyramid-like shape colored bright magenta at the top, black at the bottom left corner, and white at the bottom right corner, with smooth color gradients between the corners, illustrates which colors in the range referred to by the title are visible in the sRGB gamut. Separately, a white line starting at the bottom left corner goes straight toward a peak at around lightness 80 and chroma 0.1, and then goes to the bottom right corner in a staight line from there. This line illustrates the chroma value on the OkLCh chroma scale that a chroma value of 100% in the egal reparametrization corresponds to as a function of the lightness.
What a chroma of 100% means for egal depends on the lightness.

However, given that the consistency of chroma across lightness was still vulnerable to being broken by fallbacks arising from the limitations of the sRGB (or P3, Rec2020, etc.) color gamut, this tradeoff might not be as bas as it seems, depending on the use case.

In Detail

As mentioned before, the core idea behind égal is as follows:

For a given lightness, find the largest chroma such that any hue will be still able to deliver that chroma at the given lightness.

Now, this can be generalized into the following:

For a given lightness $\ell$, provide a linear mapping $f_\ell\colon[0,\infty)\to[0,\infty)$ such that $f_\ell(0) = 0$ and $f_\ell(100)$ equals the largest chroma such that, at the given lightness, any hue will still be able to deliver that chroma.

These functions $f_\ell$ are then used to define a mapping into the input space of OkLCh as follows:

$$ \begin{aligned} \texttt{egal} &\colon [0,100]\times[0,\infty)\times[0,360) \to [0,100]\times[0,\infty)\times[0,360), \\ \texttt{egal} &\colon \langle \ell, c, h \rangle \mapsto \langle \ell, f_\ell(c), h \rangle. \end{aligned} $$

Note that these functions are sensitive to the color gamut that we are targeting, since the maximum chroma values directly depend on that gamut. This is because the gamut is what specifies when a color is no longer considered displayable. For example, P3 monitors are able to display more saturated colors than sRGB monitors, and therefore the maximum chroma values for P3 are larger than those for sRGB. Because of this, the target gamut can be specified as an option passed to the egal function.

Furthermore, if you prefer to use HCT instead of OkLCh behind the scenes, that's also supported.

Other Resources

Software

Articles

Discussions

  • HCT Color Space: Hacker News discussion on the HCT color space, involving its creator.

Color Systems

About

Reparametrization of OkLCh/HCT to simplify uniformity in color saturation.

Topics

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •  
0