256 ANSI rainbow colors

The default 256 ANSI color palette is not well suited for picking colors or gradients. A simple transformation reveals the underlying rainbow spectrum.

Terminal ANSI color codes are constructed with a technical approach on RGB values and thus cannot be simply enumerated by perceived color. While this can be convenient for looking up the nearest supported code for a color known in advance, the representation can be considered not human-friendly. In particular, it can be hard to:

The majority of ANSI escape codes for 256 colors are 216 entries that are directly mapped onto a (6×6×6) RGB colorspace cube. These colors can usually be enabled by their decimal number as terminal ASCII escape code. (For example, see lolcat.)

For reference, the ANSI 256 RGB color palette is often shown as follows, which already tries to provide some kind of grouping:

While this table resembles the underlying method for mapping RGB values, it is poorly designed for manually choosing a gradient or spectrum.

A simple reordering of exactly the same colors in a different layout can enable these usecases in a more sane and convenient way. The middle column denotes the unique main color that follows the spectrum. Brightness gradients are shown aside per row, only here duplicates are possible. There is no overlap between the main and pastel colors; All possible values are covered overall.

Rainbow Colors Palette

Pastel Color Space

ANSI Standard Colors

Apart from the 216 step RGB colorscale above, the 256 ANSI escape codes also cover 16 standard colors (which can vary across systems) and 24 intermediate grayscales. For completeness:

RGB to ANSI Converter

This online calculator converts arbitrary RGB colors into their nearest ANSI color and code, including the deviation introduced by resolution limits. Further variants of the result can then be found by using the tables above.

Given the center input color in RGB hex notation, the corresponding color code, actual color, and euclidean distance as unit of (in)accuracy is shown – for both the standardized RGB and grayscale spaces, respectively. Optimizing for minimum per-channel deviation might not always give the best match when it comes to human perception. So in addition to the nearest neighbor, the next darkest and brightest color is shown, too. See below for details.

Background

Iterating through RGB coordinates individually creates repetitive but not coherent tables, as initially shown. For enumerating colors as how they are perceived, a common approach is the conversion to the HSV color space first. The hue component should allow to compare the position on the spectrum, with the saturation and value as secondary order. Applying this method to the ANSI color range does not seem to give satisfying results, though – for the related HSL and HSI hue mappings, too. The very discrete RGB values at hand (only six in each dimension) might contribute to that. However, the 360° “color wheel” in HSV does map the whole RGB spectrum by mixing only two primary colors at a time.

Graphical representation of RGB coordinates given values for HSV – Wikipedia

When considering an RGB space cube, this cycle is just walking along the outer edges, in-between the black and white point corners. The first segment starts at full red (x axis), gains green (y) for yellow, and decrements red to 0 as green – and so on.

Walking along edges of the RGB cube

This walk on the available RGB values form the main rainbow spectrum table. The decreasing and increasing brightness can be seen as following the diagonal towards the black and white points, respectively. As there are six possible values in either dimension, the outer surface cube of 6×6×6 “pure” colors contains two smaller cubes, with a side length of 4×4×4 and 2×2×2. The same approach on those yields the “pastel” color maps. Note that this kind of cube representation relates to the cylinder or bicone HSV interpretation.

RGB Values and Conversion

The six discrete values for each of the three channels directly map to [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff], such that colors with a different R, G, or B value cannot be represented.

spacecolor = ⟨0, 95, 135, 175, 215, 255⟩, dim = 6
spacegray = ⟨8, 18, 28, 38, …, 208, 218, 228, 238⟩, dim = 24

Given the corresponding color space indices, conversion to an ANSI color code is done by:

codecolor = 36 × r + 6 × g + b + 16, r, g, b ∈ [0, 5]
codegray = y + 232, y ∈ [0, 23]

Note that the available color spaces values are not evenly distributed, i.e., the resolution can vary for especially dark or bright colors. However, this might be a senseful optimization that exploits limited human perception in such cases.

The color converter above does round each RGB channel to the nearest available value, thereby optimizing for smallest overall deviation. Additionally, all channels rounded down/up together is also shown as result, with an increased deviation but a possibly reduced input hue skew. Other approaches for tone mapping might be more beneficial when it comes to preserving the perceived color at the cost of brightness accuracy.

Color to grayscale conversion bases on luminance – as given by the weighted average “NTSC formula”:

Y = (R × 0.299) + (G × 0.587) + (B × 0.114)

As this sums up to 1.0, already gray-ish inputs are not affected but used directly and mapped to the nearest available value.