Skip to content

Avoid NaN saturation when converting black from HSL to HSB#8734

Closed
Chessing234 wants to merge 1 commit intoprocessing:mainfrom
Chessing234:fix/hsla-to-hsba-black-nan
Closed

Avoid NaN saturation when converting black from HSL to HSB#8734
Chessing234 wants to merge 1 commit intoprocessing:mainfrom
Chessing234:fix/hsla-to-hsba-black-nan

Conversation

@Chessing234
Copy link
Copy Markdown

Bug

ColorConversion._hslaToHSBA returns a NaN saturation when the input
is pure black. Any p5.Color constructed from an HSL black
(e.g. color('hsl(0, 50%, 0%)')) ends up with sat = NaN once it's
converted to the internal HSBA representation, and downstream
getters/serializers propagate that NaN.

Root cause

let val;
if (li < 0.5) {
  val = (1 + sat) * li;
} else {
  val = li + sat - li * sat;
}

// Convert saturation.
sat = 2 * (val - li) / val;

For black, li === 0, and the first branch sets val = (1 + sat) * 0 = 0.
The subsequent 2 * (val - li) / val becomes 2 * 0 / 0, which is
NaN.

Why the fix is correct

  • Pure black carries no hue and no meaningful saturation, so the
    conventional value is 0 - the same choice _rgbaToHSBA already
    makes for grayscale (chroma === 0) inputs elsewhere in the file.
  • Gating the division on val === 0 with a ternary keeps the
    existing formula unchanged for every non-black input (floating
    point val is only ever exactly 0 when li === 0, so the
    existing val > 0 code path behaviour is identical).
  • The return shape is the same; only the NaN output becomes 0.

Change

src/color/color_conversion.js: replace

sat = 2 * (val - li) / val;

with a ternary guard returning 0 when val === 0. One-line fix
(plus a brief comment).

_hslaToHSBA computes
    val = (1 + sat) * li                  (for li < 0.5)
    val = li + sat - li * sat             (otherwise)
    sat = 2 * (val - li) / val

When the input is pure black the lightness li is 0, so val is 0 as
well, and the saturation step performs 0 / 0 and returns NaN. The
p5.Color instance then carries a NaN saturation, which breaks any
downstream math or serialization that touches it.

Black has no meaningful hue or saturation, so mirror the grayscale
check used by _rgbaToHSBA and keep saturation at 0 whenever val is 0.
Non-black colors are unaffected.
@ksen0
Copy link
Copy Markdown
Member

ksen0 commented Apr 20, 2026

This does not link to an open bug report, so I will close it. Please review contribution guidelines before making future PRs. Thank you!

@ksen0 ksen0 closed this Apr 20, 2026
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.

2 participants