6
$\begingroup$

This is a bit of an unusual puzzle - one that is purely "real world" :)

My console can produce a load of colours. But I can't find any obvious way to relate the colour to its number.

I'd hoped that some bits would map to some of RGB, but if they do, I can't see how. Obviously red-like colours are at 001, 009, 052, 088. Obviously green-like colours are at 002, 010, 022-024, 028-037. It looks almost arbitrary.

But there are hints of pattern. There seem to be repeated "fade to lighter colours" for yellow-white, that usually - but not always - contain 6 items (118-123, 148-151, 154-159, 178-181, 190-195, 220-225, 226-231), repeated changes of shading for red-magenta that also usually contain 6 items, this time always 36 apart (016-021, 052-057, 088-093, 124-129, 160-165, 196-201), and a long greyscale sequence (232-255).

There's got to be a systematic pattern... hasn't there? Surely someone didn't arbitrarily think "Which 255 colours shall we have" and number them 0-255 as they came to mind? But apart from the repeated-but-not-always 6-ness of it, I'm stumped. What could it be?

enter image description here

And again, arranged in 6's. There's clearly a pattern here, but not one I can figure out with any exactness.

enter image description here

I've put info on the system, shell, and terminal type that produced these, behind a spoiler tag....

zsh on FreeBSD 11.2, rendered threough PuTTY with term type = "putty"

$\endgroup$
2
  • 1
    $\begingroup$ Can you put it in columns of 8 please? So 000-007 in the top row. It looks like binary in decimal for those first 8. So 000, 001, 010, 011, 100, 101, 110, 111 where they are toggling rgb values. But that falls apart after 16... $\endgroup$
    – Dr Xorile
    Commented Feb 19, 2019 at 6:50
  • $\begingroup$ Has a correct answer been given? If so, please don't forget to $\color{green}{\checkmark \small\text{Accept}}$ it :) $\endgroup$
    – Rubio
    Commented Feb 23, 2019 at 10:36

2 Answers 2

10
$\begingroup$

I think the history is something like this.

  1. Start with 8 colours because 8 is a small power of 2. These are simple: bit 0 for red, bit 1 for green, bit 2 for blue, but 000 means grey rather than black because you're only controlling the foreground and not the background.

  2. Then add another 8 colours. Actually, in the image here it looks as if 8-15 may be the same as 0-7; I think maybe they used to mean "flashing" or something?

  3. Now extend from 4 bits to 8 bits (i.e., using twice as much storage to describe a given number of characters). This takes you from 16 to 256 colours, leaving another 240 to add. So, write that as 216+24; use the first 216 to go through a 6x6x6 cube of colours, and then the final 24 for a range of greys. For no obvious reason, in traversing the cube B varies fastest, then G, then R, which is the opposite ordering from those first 8 colours (which are a 2x2x2 cube).

Here's a bit of crappy Javascript and the result of putting it into a web page:

function c(n) {
  var r,g,b;
  if (n < 16) {
    r = (n&1) ? 255 : 64; // ELMY: 85 not 64
    g = (n&2) ? 255 : 64; // ELMY: 85 not 64
    b = (n&4) ? 255 : 64; // ELMY: 85 not 64
  }
  else if (n < 16+216) {
    var m = n-16;
    r = Math.floor(m/36); m -= 36*r;
    g = Math.floor(m/ 6); m -=  6*g;
    b = m;
    r = 32*(r+3)-1; // ELMY: 40*r + 55*(r>0)
    g = 32*(g+3)-1; // ELMY: 40*g + 55*(g>0)
    b = 32*(b+3)-1; // ELMY: 40*b + 55*(b>0)
  }
  else {
    var m = n-(16+216);
    r = 10*(m+1); // ELMY: 10*m+8
    g = r; b = r;
  }
  r = r.toString(16); if (r.length==1) r = "0"+r;
  g = g.toString(16); if (g.length==1) g = "0"+g;
  b = b.toString(16); if (b.length==1) b = "0"+b;
  return "#" + r + g + b;
}

colours

I made some rather arbitrary choices in the above and the match with the image in the OP isn't perfect, but it's pretty good.

[EDITED to add:] Elmy's answer (which presents code basically equivalent to mine above) has what he says -- and I see no reason to disbelieve it -- are the correct values for some parameters in the code above to reproduce the OP's colours exactly. The comments saying // ELMY: in that code give his values.

$\endgroup$
2
  • $\begingroup$ Can you explain what you mean with "but 000 means grey rather than black because you're only controlling the foreground and not the background."? If 000 is medium grey and you only control the foreground, how can different values yield lighter and darker greys? $\endgroup$
    – Elmy
    Commented Feb 19, 2019 at 17:24
  • $\begingroup$ What I mean -- which, actually, might be wrong -- is that this set of colours is intended only for foreground and not background colours, with the assumption that the background is black, so there's no need for a way to specify black because black-on-black text is kinda useless. $\endgroup$
    – Gareth McCaughan
    Commented Feb 19, 2019 at 19:02
2
$\begingroup$

It's 3 iterations of colors. I recreated the exact same colors as given in the question with rather simple code.


The first rows of 16 colors are a binary count to 8 (repeated twice with exactly the same colors). The first bit represents red, the second bit green and the third bit blue. I know the binary numbers are backwards, but this way it fits into the RGB schema.

| count | 000 | 100 | 010 | 110 | 001 | 101 | 011 | 111
| R     |  85 | 255 |  85 | 255 |  85 | 255 |  85 | 255
| G     |  85 |  85 | 255 | 255 |  85 |  85 | 255 | 255
| B     |  85 |  85 |  85 |  85 | 255 | 255 | 255 | 255

Then follows an iteration through finer RGB values. There are 6 steps for each color value:

steps: 0 |  95 | 135 | 175 | 215 | 255

The iterations are nested like this:

loop through 6 steps of red
{
  loop through 6 steps of green
  {
    loop through 6 steps of blue
    {
      print number in color(red, green, blue);
    }
  }
}

That's why the color runs smoothly through 36 similar tones and then suddenly shifts back to red. The innermost loop (blue value) is executed without interuption, when it finishes, the middle loop (green value) executes one single step, before the innermost loop executes again. The red value doesn't change for 36 steps, until the middle loop finishes and the outermost loop (red value) executes one single step.

An example of RGB values is:

| # | 028 | 029 | 030 | 031 | 032 | 033 | 034 | 035 | 036 | 037 | 038 | 039
| R |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0
| G | 135 | 135 | 135 | 135 | 135 | 135 | 175 | 175 | 175 | 175 | 175 | 175
| B |   0 |  95 | 135 | 175 | 215 | 255 |   0 |  95 | 135 | 175 | 215 | 255

The blue-value-loop finishes at #033. The green-value-loop executes one single step and the blue-value-loop starts again at #034.


After that it's an iteration through grey values at an interval of 10. It starts with RGB(8, 8, 8), then RGB(18, 18, 18), then RGB(28, 28, 28) and so forth for 24 colors, which yields RGB(238, 238, 238) as the lightest grey.


Result:

Code to reproduce (Delphi):

procedure TForm1.FormCreate(Sender: TObject);
const gradient: array[0..5] of integer = (0, 95, 135, 175, 215, 255);
var
  red, green, blue: integer;
  col, row: integer;
  i, j: integer;
begin
  row := 0;
  // iterate base colors
  for col := 1 to 8 do begin
    if col mod 2 = 0 then red := 85 else red := 255;
    if Math.floor(col / 2) mod 2 = 0 then green := 85 else green := 255;
    if Math.floor(col / 4) <= 0 then blue := 85 else blue := 255;
    PaintColorRect(row, col, red, green, blue);
  end;
  row := 1;

  // iterate gradient colors
  for i := 0 to 5 do begin
    red := gradient[i];
    for j := 0 to 5 do begin
      green := gradient[j];
      for col := 0 to 5 do begin
        blue := gradient[col];
        PaintColorRect(row, col, red, green, blue);
      end;
      row := row+1;
    end;
  end;

  // iterate grey values
  red := 8;
  green := 8;
  blue := 8;
  for i := 0 to 3 do begin
    for col := 0 to 5 do begin
      PaintColorRect(row, col, red, green, blue);
      red := red + 10;
      green := red;
      blue := red;
    end;
    row := row+1;
  end;

end;

procedure TForm1.PaintColorRect(row, col, red, green, blue: integer);
begin
  Image1.Canvas.Brush.Color := RGB(red, green, blue);
  Image1.Canvas.FillRect(Rect(col*60, row*20, (col+1)*60, (row+1)*20));
end;

I know Delphi is not the most widespread programming language ;-) so I'll see if I can reproduce the same program in C#.

$\endgroup$
3
  • $\begingroup$ This says almost exactly the same thing as my answer posted several hours before. If you are correct in saying that you've got the exact right colour values, that's a definite improvement on mine, but that's a matter of changing three numbers in the Javascript code in my answer. Actually, not quite because the values in the 6x6x6 cube aren't uniformly spaced in yours. Still, did this really merit a whole new answer? $\endgroup$
    – Gareth McCaughan
    Commented Feb 20, 2019 at 2:36
  • $\begingroup$ @GarethMcCaughan It's not about exact color values or uniform spaces. I wrote my own answer because I found the exact rules that created the pattern. Your explanation for anything but the first 16 colors is "for no obvious reason" and "I made some rather arbitrary choices". I did not. I analyzed the given sample and explained how to recreate it. $\endgroup$
    – Elmy
    Commented Feb 20, 2019 at 5:07
  • $\begingroup$ I'm sorry, but that's completely inaccurate. The only thing I said was "for no obvious reason" was the fact that the 6x6x6 cube traversal (colours 16 on) has R,G,B in the reverse order to the 2x2x2 cube traversal (colours 0-15). My answer has "exact rules" in exactly the same way as yours does -- except, again, for the fact that we have some different parameter values. (And, again, if yours are exactly correct then that is a genuine improvement on my answer; but, again, they're just different parameter values.) $\endgroup$
    – Gareth McCaughan
    Commented Feb 20, 2019 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.