Repository navigation
feat(core): interpolation space option for mix/interpolate/generateScale - #200
Conversation
Add an optional `InterpolationOptions` argument ({ space, hue,
premultiplied }) to `mix()`, `interpolate()`, and `generateScale()`.
Spaces: oklch (default), oklab, srgb, linear-srgb, p3, linear-p3.
- Option-less calls keep the exact legacy OKLCH behavior.
- With options, results follow CSS Color 4 `color-mix()` semantics:
hue fix-up modes (shorter/longer/increasing/decreasing) for OKLCH,
powerless achromatic hues take the other endpoint's hue, and
rectangular spaces interpolate with premultiplied alpha.
- No gamut mapping; extended-range inputs stay unclamped. Only
float noise is clamped when both RGB channels were in range and
0 <= t <= 1.
- The engine lives in its own module, and the plane gradient query
uses a legacy-only helper, so size-limit budgets are unchanged.
- Reference-accuracy suite compares against colorjs.io mix/steps
(max error ~4e-15), and bench/mix-space-bench.mjs reports midpoints.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c681f718a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The option-driven (CSS) interpolation path treated OKLCH hues as powerless below chroma 1e-4, which discarded valid hues such as oklch(0.5 0.00005 0). Use the CSS Color 4 OKLCH epsilon instead (spec sample code OKLab_to_OKLCH: chroma <= 0.000004). The option-less legacy path keeps its 0.001 cutoff. The reference-accuracy helper masked every hue below 1e-4 as `none`, so it could not catch this; it now masks only at the CSS epsilon, and the OKLCH comparisons include faint-chroma pairs around it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
With premultiplied interpolation, a fully transparent endpoint collapsed to
transparent black even at its own position, so generateScale could replace
a transparent colored stop with black.
- t = 0 and t = 1 now return exact copies of the inputs (no conversion
round trip).
- Where the interpolated alpha is exactly 0, the channels are interpolated
with straight alpha. CSS Color 4 ("if the interpolated alpha value is
zero, the un-premultiplied value is the premultiplied value") and
colorjs.io keep the all-zero premultiplied values, which makes the result
transparent black. Both are invisible; this is a documented deviation.
- Reference suite: colorjs.io comparisons use interior t values plus an
exact-endpoint test, and alpha-0 samples compare against straight alpha.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The mix-space bench derived Y via toRgb(), which rounds to 8 bits and clips before linearizing, so Y was quantized and out-of-gamut midpoints (the OKLCH rows) were clipped. Derive linear sRGB directly from OKLab (unclamped) and use the CSS Color 4 linear-sRGB -> XYZ Y row. OKLab L already came from toOklab() without clamping. The hex column is labeled as clipped display output. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use the corrected bench:mix-space table (unclamped Y from linear sRGB) and drop the 8-bit-vs-unclamped caveat. Compute the demo's Y with the CSS Color 4 Y row and show 4 decimals so live values match the table. Describe the CSS-path powerless-hue cutoff (chroma <= 4e-6) vs the legacy 0.001, exact t = 0/1 endpoints, and the straight-alpha fallback when interpolated alpha is 0. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use the corrected bench:mix-space table (unclamped Y from linear sRGB) and drop the 8-bit-vs-unclamped caveat. Compute the demo's Y with the CSS Color 4 Y row and show 4 decimals so live values match the table. Describe the CSS-path powerless-hue cutoff (chroma <= 4e-6) vs the legacy 0.001, exact t = 0/1 endpoints, and the straight-alpha fallback when interpolated alpha is 0. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use the corrected bench:mix-space table (unclamped Y from linear sRGB) and drop the 8-bit-vs-unclamped caveat. Compute the demo's Y with the CSS Color 4 Y row and show 4 decimals so live values match the table. Describe the CSS-path powerless-hue cutoff (chroma <= 4e-6) vs the legacy 0.001, exact t = 0/1 endpoints, and the straight-alpha fallback when interpolated alpha is 0. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stack 4/4. New guide at `/docs/guides/graphics-and-performance` (new Guides nav section) demonstrating #200–#202 with live demos, API examples, comparisons to existing APIs and pmndrs/math, and adopt-in-addition vs replace recommendations. - Linear-mix demo: pickers/presets, per-space gradient strips, midpoint hex / OKLab L / Y / out-of-sRGB flag. - `mixInto` rAF loop with frame counter (reduced-motion aware). - WebGL2 quad colored via `packColors` (linear vs gamma blending, packed floats shown). - Code shown is the demo source (`?raw`), demos lazy-loaded per chunk. Verified in browser: no console errors, interactions work, 375px no horizontal scroll. Docs tsc errors on this branch are only the 4 pre-existing home-route errors fixed by #199. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

Stack 1/4 (pmndrs-inspired ideas). Adds
options?: InterpolationOptions(space: oklch | oklab | srgb | linear-srgb | p3 | linear-p3;hue: CSS Color 4 modes;premultiplied) tomix,interpolate,generateScale, following CSScolor-mix()semantics.bench:mix-spaceadded.oklchdefaults to straight alpha (legacy);premultiplied: truegives CSS parity.Recommendation: adopt in addition — OKLCH stays the UI-ramp default; linear light is for compositing/lighting/GPU parity.
🤖 Generated with Claude Code