3

I'm building a line chart with dozens of datasets. When there are a lot of them, it becomes increasingly hard to distinguish one from another.

I need a way to generate a color palette of any given number of colors, so that colors are as distinctive as possible. This means that human eye should easily tell them apart.

I expect bright colors, e. g. f00, 0f0, 00f, ff0, f0f, 0ff — to appear first, because they are the most visually distinguishable (they don't have to be exactly f00, 0f0...).

Black and white should not appear in the palette because they are the most common background colors. Alternatively, it should be possible to define exceptions (either in form of a list of colors, or minimum /maximum brightness).

Here's what I've tried:

  • Most answers from this SO question — some look really good on small palettes (9-12 colors), but on 30+ colors very close colors appear, whereas many obvious colors remain unused.

    For example, the golden angle answer is genius, but it's only using one of three dimensions, substantially limiting the variety of colors.

    I've tried applying this approach to three dimensions (HSL), but since the three spins are not logically connected, the result is quite mediocre.

  • Google's palette.js — only generates two-color gradients and rainbows that contain single-channel colors. Every two subsequent colors are very close.

  • distinct-colors library — generates a dim palette, subjectively colors are too close to each other and common bright colors are missing.
  • https://mokole.com/palette.html — perfect result, but extremely slow. Generating 30 colors takes minutes on a very fast PC.

Accepting both snippets and library recommendations. Ideally, the output should be an array of either hex color strings or chroma-js objects.

Here's my best attempt so far. It uses the golden angle approach mentioned above, but spins both hue and brightness.

import { times } from 'lodash-es';

const goldenAngle = 137.508;

// Based on https://stackoverflow.com/a/20129594/901944
function selectColor(i) {
  const hue = i * goldenAngle; // use golden angle approximation
  const saturation = 100;
  const brightness = (((i + 50) * goldenAngle) % 30) + 20;

  return `hsl(${hue},${saturation}%,${brightness}%)`;
}

export default function palette(count) {
  return times(count, (i) => selectColor(i));
}

Result

PS Yes, I understand that on large palettes the colors will inevitably seem repeating. I want to algorithmically ensure that the palette is as diverse, contrasting as it gets.

Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133
  • The question is interesting, but why limit it to Javascript? This is solvable in any language (or even purely mathematically) and a general answer should be preferred. – Duke Apr 05 '20 at 21:06
  • Perception of colour is not linear. So that function is not accurate. There are colour spaces, such as "Lab" to make this more accurate. https://uk.mathworks.com/matlabcentral/fileexchange/29702-generate-maximally-perceptually-distinct-colors – QuentinUK Apr 05 '20 at 21:30
  • Try http://medialab.github.io/iwanthue/ – Ben Bolker Jul 09 '22 at 23:46

0 Answers0