Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: color-js/apps
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: color-js/apps
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: gamut-mapping-procedural
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 3 commits
  • 16 files changed
  • 2 contributors

Commits on Jun 24, 2026

  1. Refactor gamut-mapping methods to the procedural Color.js API

    The benchmark times each method's compute(), but the OOP Color.js API
    (new Color, .to(), .set(), .inGamut(), .clone()) re-wraps every result in
    a fresh Color — defining per-coordinate getters/setters each time — and
    the methods leaned on it to wildly varying degrees. So the timings partly
    measured each method's coding style rather than its algorithm (Clip, for
    one, looked slower than it is).
    
    Switch the GMA compute() functions and the shared harness (normalize,
    restoreLH, getDeltas) to the procedural colorjs.io/fn API, operating on
    plain {space, coords, alpha} objects. Pass color-space objects imported
    from colorjs.io/fn (OKLCH, P3, P3_Linear, …) rather than string ids, so
    timed conversions skip registry lookups, and reuse the objects to()/clone()
    already return instead of minting new literals. methods.js imports
    colorjs.io/spaces to register spaces (needed for the "oklch.l" coordinate
    refs and as a safety net). src/ deep-imports remain only for utilities with
    no /fn export (matrix math, white points, angle/okhsl helpers).
    
    The two consumers adapt at their boundaries: the benchmark builds plain
    OKLCh inputs and serializes results procedurally; the gradients view
    serializes mapped colors for its CSS variable (outside its timed region).
    
    Output is unchanged — verified bit-identical to the OOP version across a
    2880-color grid.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    LeaVerou and claude committed Jun 24, 2026
    Configuration menu
    Copy the full SHA
    eee8e8c View commit details
    Browse the repository at this point in the history
  2. Add a cached OKLCh cubic variant

    oklch-cubic recomputes its per-hue cubic structure (getHueData) on every
    call. With the OOP overhead gone, that per-call cost is now visible: the
    closed-form solve (~510ns) is genuinely slower than Edge Seeker's LUT
    lookup (~205ns), so it ranks below it in the benchmark.
    
    Re-add the per-hue cache as a separate method so the two can be compared.
    Restore the makeCompute(hueData) factory (removed in f33084d) so the cached
    and no-cache variants share the whole solver — they differ only by the
    injected hue-data provider, staying DRY. Key the cache on the hue rounded
    to 4 significant digits (toPrecision) so arbitrary float hues can't grow
    the Map unbounded, which is why the original cache was dropped.
    
    On the benchmark's sweep (the same hues recur across every lightness row)
    the cached variant runs ~2.3x faster (508 -> 217ns), landing alongside
    Edge Seeker. Output matches the exact solver to within hue quantization
    (~0.1deg, imperceptible).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    LeaVerou and claude committed Jun 24, 2026
    Configuration menu
    Copy the full SHA
    9067fa1 View commit details
    Browse the repository at this point in the history
  3. Cross-origin isolate the benchmark for a higher-resolution timer

    performance.now() is coarsened (~100µs) unless the page is cross-origin
    isolated, which clamps the benchmark's per-call GMA timings. Send
    COOP: same-origin + COEP: require-corp on the benchmark path so it gets
    the ~5µs timer. The page and its entire module graph are same-origin, so
    require-corp blocks no subresources. Scoped to /gamut-mapping/benchmark/*
    so COEP isn't imposed on the other apps.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    LeaVerou and claude committed Jun 24, 2026
    Configuration menu
    Copy the full SHA
    ca401f8 View commit details
    Browse the repository at this point in the history
Loading