Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Color.js Released (svgees.us)
151 points by ivank on July 29, 2022 | hide | past | favorite | 20 comments


This looks fantastic for working with colors, and from a great developer too. A very very similar js library is Culori [0, 1], which I discovered on the original Oklab introduction post [2].

If you're doing generative artwork or any procedural work with a non-fixed palette, a good color library and working in linear or Oklab based colorspace is a must. Using sRGB or HSV/B when interpolating for gradients or generating palettes or complementary colors is extremely painful; a 50% brightness (HSV) yellow is visually much brighter than a 50% brightness purple, with similar issues cropping up for saturation. Balancing the lighting and contrast of programmatically generated colors is so much simpler when you have a perceptually uniform colorspace like OkLCh, or linear RGB if you're working with predefined hex values.

[0]: https://culorijs.org/

[1]: https://github.com/Evercoder/culori

[2]: https://bottosson.github.io/posts/oklab/


really glad to see this released, because it will help popularize the new lch/oklch color spaces, which are an improvement over rgb/hsl.

with that said, i did some experimentation with these new color spaces recently and found that even these are not foolproof for those of us who don't want to be steeped in the depths of color spaces and human perception. for instance, mentioned in the article is automatic gamut mapping for when you try to display a color that's outside the gamut. seems like that's a good thing since it would take care of some rare edge cases, right?

no, the edge cases are all over the place. the gamut "depth" is dependent on the component values (like lightness and chroma), so if you want to go from a light red hue to a light yellow hue, you can't just change the hue component, because the lightness and chroma values might put the target color out of bounds. that's where the automatic gamut mapping kicks in. but guess what? the various browser engines currently have their own algorithms, and they that don't pick the same nearby colors.

the evil martians oklch color picker[0] shows this visually. if you look at the hue vs. lightness graph, notice how jagged the (planar projection of the) gamut is there. for any given horizontal line (constant lightness), you might end up out of bounds on the chroma component for a chosen hue. this can happen in any of the 3 dimensions.

also, while oklch is more perceptually even generally, there are gradient examples where it's not much better (i think orange/brown gradients is one of those cases, but i'm not certain, offhand). color is apparently quite hard, so i'm not knocking the effort or the improvements, but there are still some gotchas remaining for designers/developers.

[0]: https://lch.evilmartians.io/


> the gamut "depth" is dependent on the component values (like lightness and chroma)

This is an unavoidable bug (feature?) of human vision and computer displays.

Using models like "HSL" or "HSV" is a way of trying to sweep it under the rug, but it doesn’t really work very well and ends up causing a lot more trouble.


yah, visual perception isn't uniform in all directions, but i believe it's more that those distortions (the kinks and valleys) comes from trying to make the color space perceptually even to humans (and human perception has variation as well). however, there's no perfect way of doing this in all vector directions, so it's a multi-dimensional optimization problem that can only minimize distortions/maximize evenness, rather than provide a singular best analytical solution. as a result, the color space comes out like a spikey blob, to match our numerical color scales to human perception, since the opposite of coercing human perception onto uniform color scales is not feasible.


I delved into the bewildering world of color last Christmas/New Year when developing a new reduce-palette/dither filter for my JS canvas library. The number of different color spaces available, converting between color spaces, calculating color distances - this stuff is mad and frustrating in equal measure!

I built my own solution, then afterwards came across the Color.js repo. I have a strict "no dependencies" rule for my library; discovering Color.js is the closest I've ever come to breaking that rule.


Does ‘no dependencies’ prevent you from copying code in from elsewhere?

If I see small 1 or 2 file libraries I generally just copy them in.


I'm guilty of copying code in from elsewhere. I've made an effort to acknowledge the copying by adding links to the original repos in code comments, which get translated into actual links when I generate the documentation. I also try to acknowledge where I haven't copied code, but the code I've written has been heavily inspired by other people's code.


I do this a lot too, but this is the kind of dependency I’m more okay with. It doesn’t have to integrate with systems so the rug won’t get swept out. It’s low risk and I know I could always copy it if it were to die. I would have no issue depending on it.


This is nice!

I can't help but wonder why everyone still names their API functions Foo_to_Bar when the reverse Bar_from_Foo is so much more readable when chaining conversions.

Compare the example here:

  let myoklch =
    OKLab_to_OKLCH(XYZ_to_OKLab(D65_to_D50(lin_ProPhoto_to_XYZ(lin_ProPhoto(mycolor)))))
With what it could have been if it had used Bar_from_Foo naming instead:

  let myoklch =
    OKLCH_from_OKLab(OKLab_from_XYZ(D50_from_D65(XYZ_from_linProPhoto(lin_ProPhoto(mycolor)))))


My take is most people write code in a write optimized manner. Foo_to_Bar is much friendlier for the writer as auto-complete will come up with all the Foo_to_Xs you can use, whereas the Bar_from_Foo format requires the writer to know that they want to use Bar up front.


In OCaml, a common convention is often to use functions with names like int_of_float (or Int.of_float), although it is also likely that (e.g.) the Int module will have a to_float function too. I write a lot of OCaml and it still trips me up from time to time.

The other solution in a procedural/functional language is to write code closer to the evaluation order, e.g. with a 'pipeline' operator:

  let myoklch =
    mycolor
    |> lin_proPhoto
    |> lin_proPhoto_to_XYZ
    |> ...
or with a 'fluent' interface in an OOP language:

  let myoklch =
    mycolor.to_linear().to_XYZ().to_D65().to_OKLAB().to_OKLCH();


I'm the maintainer of `color`, this looks nice and high level. Will start to point people to this when they ask for these higher level features. Great work!


`color`?



I used this recently. I wanted to create a timeline UI for DemoTime with elements on the timeline matching the gradient background.

Color.js is admittedly overkill for this, but it let me import just the interpolate function and it did the trick.


My god this would have been amazing when I worked at a custom/ print on demand merch co (like Teespring - people upload their own designs - except there was no minimum). Working through color spaces with custom uploaded images for their "shop" and print was HELL


The author has an incredible list of projects. My goodness.


The color.js site is really painful to scroll in android chrome btw. Also the menu doesn't work on mobile.


This is pretty cool, what would be interesting use cases? My guess is threejs and things like that


they already have a bunch. One is generating gradients that look right (i.e. that are better based on the way humans perceive colour) by blending in a linear perceptual space. Another is generating a palette of distinct-looking colours for something like different lines on a graph.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: