Qix-/color

color.whiten() not working #166

ageofabenius posted onGitHub

When I use color.whiten in-browser, I get no change to the color object. My test code below:

import Color from 'color';

let color = Color("#0099cc") console.log(color.hwb()) let newColor = Color(color).whiten(0.5) console.log(newColor.hwb()) let newColor2 = Color(color).blacken(0.5) console.log(newColor2.hwb())


I am seeing another issue where Color('#000000').whiten(anyValue) is always returning absolute white instead of a shade of gray. Tested with 0, 0.01, 0.05, 0.1, 0.5, and 1.

posted by EvanWard over 5 years ago

Same issue here! @Qix- could you please look at https://github.com/Qix-/color/pull/167 ?

posted by lifenautjoe about 5 years ago

Quick note: Sorry for any spelling typos in this post. I'm in the process of learning German and it appears MacOS cannot handle two languages at once with spell check. This is what I see.

This has been my feedback for a number of "not working" issues, but I guess I'll re-form it here for the folks using the HWB color model and the associated methods (.whiten() and .blacken()).

Let me make it abundantly clear that .whiten() and .blacken() are working as intended. Please don't flame me before you read the rest of this comment, because it's starting to wear on me a bit as a maintainer.

HWB is a color model similar to HSV (the "H" means Hue, which is identical between the two cylindrical color models) with "whiten" and "blacken" channels. Basically, it takes a fully-saturated color (W=0, B=0) and 'mixes' black and/or white to achieve varying degrees of lightness (W=0, B>0 or W>0, B=0) or (de-)saturation (W>0, B>0). A full grey (RGB 127, 127, 127) is where W=100%, B=100%; the Hue no longer has any significance in such a case.

Keep in mind, color was designed to work with color models, and the methods exist to reflect that. The methods were not designed to reflect human-friendly operations per se. "Whiten" and "blacken" can mean very different things given the intended effect, color model and source color.

The problem here isn't with implementation, but with the intent of the API - color is designed as a more "academic" library than a "end-user friendly" library; corrollary: color's API is focused on manipulating color spaces in a mathematically correct way.

.whiten() increases the white (W) channel of the HWB color by a ratio - that is, W += W * r (where r is the parameter to .whiten()). Black does the same thing, just with the black channel. If you want to remove black from the black channel, you'd pass a negative parameter to the method.

If you check the source code, this is exactly what's happening. It is mathematically and academically correct behavior.

This is also why Color('#000000').whiten(n) will still result in black - the White channel in the HWB color model is 0 for #000000. Multiplying anything by zero is always zero, of course, and then adding zero to zero is - of course - zero, resulting in #000000.

.whiten() is working as intended.

If you print out the output from the OP, you'll see the White (W) channel of the color is 0, but the Black (B) channel is not 0. This is why .blacken() results in different color whereas .whiten() does not.

image

These two methods are working entirely as intended.


Now, that's not to say something can't be improved here. I fully agree that the API methods are not intuitive to most people who do not care about the color theory behind the library (why else would you use this library instead of using color-convert directly? 🙃)

However, the issue then becomes about the API naming conventions, not about the correctness of the mathematical methods. I'm very much open to having a discussion about the naming conventions used here because I fully understand there is some confusion with what this library does and what users are trying to do.

Please also keep in mind I'm not the original author of this library, and that even if I were, designing an API that is intuitive, just as powerful, mathematically sound and useful to a whole range of people (designers all the way to academics) is very, very difficult. It has been a problem with this library for a while and it's a problem I've been racking my brain on how to solve for years. Any input is appreciated.


As-is, this is a won't-fix. I know that's not the response you want to hear.

The two methods I would suggest looking into are .lighten(), which works with the HSL model's Lightness (L) channel, or instead to color.mix() the Color('white') instead.

posted by Qix- about 5 years ago

@Qix- Thanks for answer. So, I see only one way to change brighteness independently current values - use lightness() method.

let color = Color('rgb(255, 0, 0)');
color.lightness(); // 50
console.log(color.lightness(90).rgb().toString()) // rgb(255, 204, 204)
posted by vyushin about 5 years ago

If your definition of "brightness" is the lightness channel in the HSL color model, then yes :) other people might think "brightness" means saturation.

Perhaps I should add a guide of common transformations to the readme.

posted by Qix- about 5 years ago

Some more examples that demonstrate this were added to the readme.

You are indeed looking for lightness in this case. I'd also, again, recommend .mix() as it might suit you as well.

posted by Qix- about 5 years ago

Fund this Issue

$0.00
Funded

Pull requests