The 256 color table and its partitioning
The color range of a 256 color terminal consists of 4 parts, often 5, in which case you actually get 258 colors:
Color numbers 0 to 7 are the default terminal colors, the actual RGB value of which is not standardized and can often be configured.
Color numbers 8 to 15 are the "bright" colors. Most of the time these are a lighter shade of the color with index - 8. They are also not standardized and can often be configured. Depending on terminal and shell, they are often used instead of or in conjunction with bold font faces.
Color numbers 16 to 231 are RGB colors. These 216 colors are defined by 6 values on each of the three RGB axes. That is, instead of values 0 - 255, each color only ranges from 0 - 5.
The color number is then calculated like this:
number = 16 + 36 * r + 6 * g + b
with r
, g
and b
in the range 0 - 5.
The color numbers 232 to 255 are grayscale with 24 shades of gray from dark to light.
The default colors for foreground and background. In many terminals they can be configured independently from the 256 indexed colors, giving an additional two configurable colors . You get them when not setting any other color or disabling other colors (i.e. print '\e[m'
).
Some sources:
Default RGB values
Theoretically, in order to get an equally distributed range of colors, the RGB values for the colors in the range 16 - 231 could be calculated like this:
# example in Python: // is integer divison, % is modulo
rgb_R = ((number - 16) // 36) * 51
rgb_G = (((number - 16) % 36) // 6) * 51
rgb_B = ((number - 16) % 6) * 51
But it seems that the actual method is different:
Any terminal emulators I tested seems to follow XTerm and map the values [0, 1, 2, 3, 4, 5]
for red, green and blue to the values [0, 95, 135, 175, 215, 255]
on the RGB color axes. (I tested with XTerm (297) URxvt (v9.19), ROXTerm (2.8.1), gnome-terminal (3.6.2) and xfce4-terminal (0.6.3))
The RGB values for a given index can be calculated with this algorithm:
# example in Python: 'a = b if c else d' is 'a = (c) ? b : d` in C, Perl, etc.
index_R = ((number - 16) // 36)
rgb_R = 55 + index_R * 40 if index_R > 0 else 0
index_G = (((number - 16) % 36) // 6)
rgb_G = 55 + index_G * 40 if index_G > 0 else 0
index_B = ((number - 16) % 6)
rgb_B = 55 + index_B * 40 if index_B > 0 else 0
The grayscale seems to follow this simple formula:
rgb_R = rgb_G = rgb_B = (number - 232) * 10 + 8
256colres.pl
in the root of the XTerm sources (version 313) uses a similar algorithm to generate 256colres.h
, which contains the color definitions for 256 color mode:
$line1="COLOR_RES(\"%d\",";
$line2="\tscreen.Acolors[%d],";
$line3="\tDFT_COLOR(\"rgb:%2.2x/%2.2x/%2.2x\")),\n";
# colors 16-231 are a 6x6x6 color cube
for ($red = 0; $red < 6; $red++) {
for ($green = 0; $green < 6; $green++) {
for ($blue = 0; $blue < 6; $blue++) {
$code = 16 + ($red * 36) + ($green * 6) + $blue;
printf($line1, $code);
printf($line2, $code);
printf($line3,
($red ? ($red * 40 + 55) : 0),
($green ? ($green * 40 + 55) : 0),
($blue ? ($blue * 40 + 55) : 0));
}
}
}
# colors 232-255 are a grayscale ramp, intentionally leaving out
# black and white
$code=232;
for ($gray = 0; $gray < 24; $gray++) {
$level = ($gray * 10) + 8;
$code = 232 + $gray;
printf($line1, $code);
printf($line2, $code);
printf($line3,
$level, $level, $level);
}
Showing available colors in a terminal
Here is a zsh function that prints all colors on a 256 color terminal (if TERM
is set to a 256 color value):
function termcolors ()
{
print TERM
print -P "Foreground: >█<"
print -P "Background: >%S█%s<\n"
print " 0 1 2 3 4 5 6 7"
for b (0 1)
do
printf "%d %2d " $b $(( 8 * b ))
for r (0 1 2 3 4 5 6 7)
do
c=$(( 8 * b + r ))
print -nP "%K{$c} %k"
done
printf " %2d\n" $(( 8 * b + 7 ))
done
print
print RGB
for r (0 1 2 3 4 5)
do
print "$r $(( 16 + 36 * r )) - $(( 16 + 36 * r + 35 ))\n 0 1 2 3 4 5"
for g (0 1 2 3 4 5)
do
printf "%d %3d " $g $(( 16 + 36 * r + 6 * g ))
for b (0 1 2 3 4 5)
do
c=$(( 16 + 36 * r + 6 * g + b ))
print -nP "%K{$c} %k"
done
printf " %3d\n" $(( 16 + 36 * r + 6 * g + 5))
done
print
done
print
print GRAY
for g in $(seq 0 23)
do
c=$(( 232 + g ))
printf "%2d %3d " $g $c
print -P "%K{$c} %k"
done
}
Changing RGB values during runtime
In some terminals (at least xterm
, gnome-terminal
, termite
and urxvt
) all those colors can be changed during runtime by sending one of the following XTerm Control Sequences:
OSC 4; c ; spec BEL
OSC 4; c ; spec ST
where:
OSC
is the escape character (\e
or \033
) followed by ]
c
is the color number (0 - 255)
spec
is a color specification (e.g. red
, #ff0000
, rgb:ff/00/00
, rgbi:1/0/0
- what actually works might depend on the terminal)
BEL
is the bell character (\a
or \007
)
ST
is the string terminator \e\\
or \033\\
These control sequences can be sent by simply printing them with echo
:
echo -en "\e]4;COLOR;SPEC\a"
echo -en "\e]4;COLOR;SPEC\a"
For example, in order to set color number 5 (usually some shade of magenta) to red, either of these should work:
echo -en "\e]4;5;red\a"
echo -en "\e]4;5;#ff0000\e\\"
echo -en "\033]4;5;rgb:ff/00/00\007"
Those colors can be reset to their (configured) default with one of the control sequences
OSC 104 ; c BEL
OSC 104 ; c ST
So the following loop will reset all colors from 0 to 255 to their configured or default value:
for c in {0..255}; do
echo -en "\e]104;$c\a"
done
For the default foreground and background colors the control sequences are OSC 10 ; spec BEL
and OSC 11 ; spec BEL
, respectively. For example:
echo -en "\e]10;red\a"
echo -en "\e]11;green\a"
Those can be reset with OSC 110 BEL
and OSC 111 BEL
respectively:
echo -en "\e]110\a"
echo -en "\e]111\a"
Digital Color Meter
in the Applications/Utilities folder on OSX to sample colors in any app - including Terminal. – Creel