Skip to content

feat(core): interpolation space option for mix/interpolate/generateScale - #200

Merged
pbroom merged 4 commits into
mainfrom
feat/linear-light-mix
Oct 10, 2026
Merged

pbroom merged 4 commits into
mainfrom
feat/linear-light-mix

Conversation

@pbroom

@pbroom pbroom commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Stack 1/4 (pmndrs-inspired ideas). Adds options?: InterpolationOptions (space: oklch | oklab | srgb | linear-srgb | p3 | linear-p3; hue: CSS Color 4 modes; premultiplied) to mix, interpolate, generateScale, following CSS color-mix() semantics.

  • Defaults unchanged (OKLCH lerp); option-less calls are bit-identical.
  • Accuracy vs colorjs.io ≤ 4e-15 across spaces, hue modes, alpha, extended range.
  • Linear light keeps luminance: red↔lime midpoint Y 0.467 (linear-srgb) vs 0.200 (srgb). bench:mix-space added.
  • Deviation: oklch defaults to straight alpha (legacy); premultiplied: true gives CSS parity.

Recommendation: adopt in addition — OKLCH stays the UI-ramp default; linear light is for compositing/lighting/GPU parity.

🤖 Generated with Claude Code

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]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T22:25:34.647588Z c681f71 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

pbroom commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Adds optional interpolation space parameter to color mixing functions.

The PR is not ready to merge because rectangular-space scales can fail to preserve a fully transparent colored endpoint.

Fix All in CursorFindings

  1. P1 Transparent endpoints lose color ▶

Summary

Adds selectable interpolation spaces, hue methods, and alpha handling to mix(), interpolate(), and generateScale(), while routing option-less calls through legacy behavior.

  • Adds interpolation tests, reference comparisons, a benchmark, and API documentation.
  • Moves legacy scale interpolation to a separate module for the plane gradient query.

Reviews (1) · Last reviewed commit: "feat(core): interpolation space option f..."

Comment thread packages/core/src/interpolation/index.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/core/src/interpolation/index.ts Outdated
Comment thread packages/core/bench/mix-space-bench.mjs Outdated
pbroom and others added 3 commits September 25, 2026 19:02
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]>
pbroom added a commit that referenced this pull request Sep 25, 2026
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]>
pbroom added a commit that referenced this pull request Sep 25, 2026
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]>
This was referenced Oct 6, 2026

pbroom commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner Author

Merge activity

  • Oct 10, 6:47 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Oct 10, 6:47 PM UTC: @pbroom merged this pull request with Graphite.

@pbroom
pbroom merged commit a14d09d into main Oct 10, 2026
5 checks passed
@pbroom
pbroom deleted the feat/linear-light-mix branch October 10, 2026 18:47
pbroom added a commit that referenced this pull request Oct 10, 2026
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]>
pbroom added a commit that referenced this pull request Oct 10, 2026
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)
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.

1 participant