Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

[css-color-4] Add sample pseudocode for the EdgeSeeker gamut mapping algorithm - #14426

#14426
Open
svgeesus wants to merge 2 commits into
mainw3c/csswg-drafts:mainfrom
edgeseeker-pseudocodew3c/csswg-drafts:edgeseeker-pseudocodeCopy head branch name to clipboard
Open

[css-color-4] Add sample pseudocode for the EdgeSeeker gamut mapping algorithm#14426
svgeesus wants to merge 2 commits into
mainw3c/csswg-drafts:mainfrom
edgeseeker-pseudocodew3c/csswg-drafts:edgeseeker-pseudocodeCopy head branch name to clipboard

Conversation

@svgeesus

@svgeesus svgeesus commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

CSS Color 4 defines three CSS gamut mapping algorithms: Binary Search with Local MINDE, EdgeSeeker, and Ray Trace. The first and third already had sample pseudocode; EdgeSeeker only had a prose description and a placeholder Issue: add pseudocode for EdgeSeeker GMA.

This adds pseudocode for EdgeSeeker, matching the structure/style of the existing two algorithms:

  • Constructing the LUT (sampling cusp and curve points per hue slice for the destination gamut)
  • Calculating the boundary curvature from the sampled points
  • The per-color mapping algorithm (lookup + linear/arc chroma reduction + residual clip)
  • The two helper algorithms it depends on (max chroma lookup, arc intersection)

Derived from reading the reference implementation in color-js/apps (already cited in the spec as [[colorjs-EdgeSeeker]]).

Test plan

  • Built locally with Bikeshed; spec builds cleanly with no new errors or warnings
  • All four new dfns resolve and cross-reference correctly (verified in local build output)
  • Review from other editors/implementers on correctness of the math (esp. curvature/arc-intersection derivation) before merging

…algorithm

Binary Search and Ray Trace already had pseudocode; EdgeSeeker only had
prose. Add pseudocode for LUT construction, curvature calculation, and
the per-color mapping steps, derived from the color.js reference
implementation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@svgeesus

svgeesus commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Fixes errors in the EdgeSeeker pseudocode found by checking it against
the reference implementation and by numerical testing against a
bisection ground truth.

- Key LUT entries by the OkLCh hue of the sampled color, not by the HSL
  hue used to generate it. The two differ by up to 50deg in sRGB, so the
  LUT was misindexed; RMS error against ground truth was 0.0475, with a
  peak overestimate of +0.21 chroma, which left the residual clip doing
  essentially all the work. This also reconciles the pseudocode with the
  existing note about hue non-monotonicity, which can only arise under
  OkLCh-hue keying.
- Interpolate the curve point at the cusp's hue rather than pairing it
  with the cusp sampled from the same HSL hue; the two lightnesses give
  OkLCh hues up to 16deg apart. Matches getColor() in makeLut.js.
- Sample each 60deg hue region separately, so the six primaries and
  secondaries are sampled exactly, and clarify that N is per region.
- Add the wrap-around entries at 0deg and 360deg, so every hue is
  bracketed, and normalize hue before lookup, since OkLCh hue in CSS is
  not restricted to a single turn.
- Correct the direction of x in the arc intersection: it is 1 at the
  cusp and 0 at white, not the reverse. State the normalization
  convention where the curvature is derived.
- Handle a missing (powerless) hue.
- Use the sRGB LUT for HSL and HWB destinations.

After these corrections, RMS error against ground truth is 0.00014 for
sRGB and 0.00017 for display-p3, with a peak residual of 0.004 chroma,
comfortably handled by the final clip.

Also autolink the four new dfns instead of bolding them, fix the
colorjs-EdgeSeeker href, which had moved to gamut-mapping/methods/,
and fix "less than than" and a missing </li>.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@facelessuser

Copy link
Copy Markdown
Contributor

I will try to take a look sometime soon.

@jparnzen

jparnzen commented Sep 3, 2026

Copy link
Copy Markdown

Likewise, I’ll try to take a look in the next day or so.

@facelessuser

Copy link
Copy Markdown
Contributor

One thing I'd probably note. I would probably prefer to update the spec to specifically suggest building the LUT in a linear RGB space. I know the reference implementation does so using gamma corrected P3, but using the linear P3 space would distort the initial data less, requiring less correction, I imagine.

I'm not done going through it, but I just thought I'd make that a suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.