CSS Color Golf!



You are a web developer, and your boss has decided to update the company's website. He has decided that less color is better, but he wants the site to look the same. You justly decide that he has no idea what he's talking about, but you're going to try anyway, because you're bored. Since the company has thousands of webpages, and each one has its own CSS, you decide to write a script to do the necessary changes. Parsing HTML not required.

All of the pages currently use a string like rgb(255,0,0) for a color. Given the three decimal values representing the RGB values of a CSS color attribute (in that order), return or print the shortest string representation of that color, such that it's usable for CSS like so: color:<your-result-here>;.

Here is a complete table of valid CSS Color Keywords. They are case-insensitive.


Note that colors can be defined with 12 or 24 bits. The pattern #ABC is a shorter version of #AABBCC. Chuck Norris is a color.

Your program will only take in 3 integers, not a string (with the exception of the "bonus" mentioned later).

0, 0, 0         ->  #000        (same as #000000, but shorter)
255, 0, 0       ->  red
0, 128, 128     ->  TEAL
139, 0, 0       ->  DarkRed     (OR #8B0000)
72, 61, 139     ->  #483D8B
255, 255, 254   ->  #fffffe
255, 85, 255    ->  #f5f        (same as #ff55ff, but shorter)

Scoring / Rules

  • Shortest code wins!
  • Standard loopholes are disallowed, with the exception of built-ins.
  • -50% bytes (the bonus is rounded down) if you accept any* valid color selector and output the shortest. So DarkSlateBlue would output #483D8B, #F00 outputs red, etc.
    • *This only includes RGB, hex codes, and names.
    • Note that some colors have alternate names due to X11 (like Fuchsia and Magenta, or Cyan and Aqua). The alternate names are included in the linked list of CSS Color Keywords according to the W3 standard.
  • CSS3 is Turing Complete. That'd be worth a bounty.




Posted 2015-10-05T21:23:23.017

Reputation: 21 944

Here's a related question: http://codegolf.stackexchange.com/questions/13132/color-rgb-int-to-hex

– mbomb007 – 2015-10-05T21:34:49.500

Does an answer wishing to score the -50% bonus need to parse hsl(...) too? What about rgba(...) and hsla(...)? :) – Timwi – 2015-10-05T22:37:51.393

According to this link on colors in CSS, White is #000000. How did you come up with #000? And if fewer than 6 digits are allowed, why not #0? http://www.w3schools.com/cssref/css_colors.asp, , the CSS

– DavidC – 2015-10-06T02:07:27.033


@DavidCarraher See http://stackoverflow.com/q/8318911/791604 for an in-depth explanation. I would suspect many of the answers here are actually not outputting minimal colors going by the flexible implementation available in browsers (but are outputting minimal colors going by the spec given in the question here).

– Daniel Wagner – 2015-10-06T03:11:00.990



Perl, 212 - 50% = 106 bytes

use aliased Graphics::ColorNames,G;sub c{tie%c,G,CSS;$_=pop;$c={reverse%c}->{$_=sprintf'%02x'x(@0=/(\d+, ?)((?1))(\d+)/)||$c{s/#(.)(.)(.)$|#/\1\1\2\2\3\3/r},@0};s/(.)\1(.)\2(.)\3|/#\1\2\3/;y///c>length$c&&$c||$_}

With newlines added:

use aliased Graphics::ColorNames,G;
sub c{
    $_=sprintf'%02x'x(@0=/(\d+, ?)((?1))(\d+)/)||$c{s/#(.)(.)(.)$|#/\1\1\2\2\3\3/r},@0

Test Cases

use feature say;

say c '#f00';
say c '#FF0000';
say c '#112233';
say c '#f0ffff';
say c 'azure';
say c 'rgb(255, 127, 80)';
say c 'rgb(255, 255, 254)';
say c 'rgb(255,228,196)';



Perl, no bonus, 144 bytes

use aliased Graphics::ColorNames,G;sub c{tie%c,G,CSS;$c={reverse%c}->{$_=sprintf'%02x'x3,@_};s/(.)\1(.)\2(.)\3|/#\1\2\3/;y///c>length$c&&$c||$_}

With newlines added:

use aliased Graphics::ColorNames,G;
sub c{

Graphics::ColorNames isn't a core module, but it's been around since 2001. You may need to install it via:

cpan install Graphics::ColorNames
cpan install Graphics::ColorNames::CSS

The hex representation is preferred if the color name has the same length.

Test Cases

use feature say;

say c 0, 0, 0;
say c 255, 0, 0;
say c 0, 128, 128;
say c 139, 0, 0;
say c 72, 61, 139;
say c 255, 255, 254;
say c 255, 85, 255;




Posted 2015-10-05T21:23:23.017

Reputation: 30 891

Any chance you could cut 4 bytes off for the tie? – mbomb007 – 2015-10-09T20:17:43.547

@mbomb007 6 bytes, in fact. – primo – 2015-10-10T02:21:55.937

Do I hear 4 more? This competition is tough. – mbomb007 – 2015-10-12T19:12:52.117

1@mbomb007 raise. – primo – 2015-10-14T07:15:55.987


C#6 527 bytes / 2 bonus = 264

EDIT: Woot! I finally got the bonus answer with a lower score than the basic answer!

I've written just a function. It requires a using statement (included.)

C# has a nice list of known colours to work with, but it insists on including the Alpha channel. The known colours also includes all system colours, two of which have names less than 7 characters long, so these needed to be stripped out.

Here's the bonus solution, supporting formats such as:

  • 12, 34, 56
  • #123
  • #123456
  • DarkSlateBlue

Completely golfed:

using System.Drawing;int H(string s,int i,int l)=>Convert.ToInt32(s.Substring(i*l+1,l),16)*(l<2?17:1);string D(string a){int i=17,j=a.Length/3,n;var d=a.Split(',');Color k,c=a[0]<36?Color.FromArgb(H(a,0,j),H(a,1,j),H(a,2,j)):Color.FromName(a);c=c.A>0?c:Color.FromArgb(int.Parse(d[0]),int.Parse(d[1]),int.Parse(d[2]));j=c.R%i+c.G%i+c.B%i<1?3:6;n=c.ToArgb();a="#"+(j<6?c.R/i<<8|c.G/i<<4|c.B/i:n&0xffffff).ToString("x"+j);for(i=26;i++<167;k=Color.FromKnownColor((KnownColor)i),a=n==k.ToArgb()&k.Name.Length<=j?k.Name:a);return a;}

Indentation and new lines for clarity:

using System.Drawing;
int H(string s,int i,int l)=>Convert.ToInt32(s.Substring(i*l+1,l),16)*(l<2?17:1);
string C(string a){
    int i=17,j=a.Length/3,n;
    var d=a.Split(',');
    Color k,c=a[0]<36
    return a;

C#, 265 bytes

Here's the basic solution.

using System.Drawing;string C(int r,int g,int b){int i=17,c=r<<16|g<<8|b,j=r%i+g%i+b%i<1?3:6;var o="#"+(j<6?r/i<<8|g/i<<4|b/i:c).ToString("x"+j);for(i=26;i++<167;){var k=Color.FromKnownColor((KnownColor)i);o=c<<8==k.ToArgb()<<8&k.Name.Length<=j?k.Name:o;}return o;}

Indentation and new lines for clarity:

using System.Drawing;

string C(int r,int g,int b){
    int i=17,
    var o="#"+(j<6?r/i<<8|g/i<<4|b/i:c).ToString("x"+j);
        var k=Color.FromKnownColor((KnownColor)i);
    return o;


Posted 2015-10-05T21:23:23.017

Reputation: 7 912

@mbomb007: The code works for me. – raznagul – 2015-10-06T16:07:37.603


Mathematica 207 242 500-250=250 bytes

This works with inputs consisting of rgb triples, color names, or hex numbers.

(12 bit) color-depth output now works fine, thanks to an excellent article on Color Bit Depth Reduction. The basic idea is that, if an RGB triple {r,g,b}, where r, g, and b are in the range of 0-255, (i.e. hex 00-ff) can be represented without loss as a number in the range of 0-15 (i.e. 0-f), then one may use the 3 digit hex number instead of the 6 digit number. Turns out that this will occur whenever 17 (i.e 255/15) divides r, g, and b.

Only built-in functions are used. Mathematica has rules for replacing HTML colors names with RGB triples. For example, one rule is "Teal"-> RGBColor[0, 128, 128]. When such rules are inverted, rgb values (recalibrated to the range, {0, 255}) can be replaced with color names.

j=q/.{(c_ -> RGBColor[r_,g_,b_]):> (Floor[255{r,g,b}]-> c)};
z@d_:= (If[StringQ@d,If[StringTake[d,1]=="#",e=o@d,e=m@d],e=d];f@e)


z /@ {{0, 0, 0}, {255, 0, 0}, {0, 128, 128}, {139, 0, 0}, {255, 255, 
   255}, {72, 61, 139}, {255, 255, 254}, {255, 85, 255}}

{"#000", "Red", "Teal", "#8b0000", "#fff", "#483d8b", "#fffffe","#f5f"}

z /@ {"Red", "DarkSlateBlue", "Teal", "Black"}

{"Red", "#483c8b", "Teal", "#000"}

z /@ {"#000", "#f00", "#008080", "#8b0000", "#fff", "#483d8b", "#fffffe", "#f5f"}

{"#000", "Red", "Teal", "#8b0000", "#fff", "#483d8b", "#fffffe", \ "#f5f"}

Commented, Ungolfed Code

(* rules for replacing a color name with a color swatch, e.g. RGBColor{255,17,0}


(* rules for replacing a list of 3 integers with the respective color name, if one exists. And the same rules, reversed. *)

rulesListsToColorNames=(q)/.{(c_ -> RGBColor[r_,g_,b_]):> (Floor[255{r,g,b}]-> c)};


(*tests whether a 24 bit hex color can be represented as a 12 bit color with no loss. reduce can change the 24 bit expression to a 12 bit expression. *)


(*RGB list changed to a Hex number *)


(* Hex number changed to RGB list. *)


(* More conversions *)


(* choses the shortest valid CSS expression of a color *)


(* converts any input into an RGB list and calls f *)



Posted 2015-10-05T21:23:23.017

Reputation: 24 524

Isn't 255 in base 10 equal to FF in Base 16? So that should make #FFFFFF, which is longer than White. I'm finding #000 unusual but will look into the quirks of CSS to be sure. – DavidC – 2015-10-06T01:53:05.600

3@DavidCarraher: CSS colors may be specified as 24 bits or 12 bits. #FFF is the same as #FFFFFF (all bits one) which is the same as white. Also, #0 is invalid because it's neither 24 bits nor 12 bits – slebetman – 2015-10-06T04:09:08.307

slebetman, Thanks for the clarification, which confirms @mbomb007's observation. – DavidC – 2015-10-06T13:37:09.507


CoffeeScript, 411 404 388 384 382/2 = 191

UPD: Pretty sure it is final result

Hope, it's okay to use window.document.*. Check rgb function and eval call.

s=(c,rgb=(r,g,b)->(2**24+(r<<16)+(g<<8)+b).toString 16)->d=y=document.body;r=(q=(a)->y.style.color=a;d[b='#'+eval(getComputedStyle(y).color)[1...].replace /(.)\1(.)\2(.)\3/,'$1$2$3']=a;b) c;(d='NavyGreenTealIndigoMaroonPurpleOliveGraySiennaBrownSilverPeruTanOrchidPlumVioletKhakiAzureWheatBeigeSalmonLinenTomatoCoralOrangePinkGoldBisqueSnowIvoryRed'.match /.[a-z]+/g).map(q);d[r]||r

Test results

rgb(   0,   0,   0 ) -> #000
rgb( 255,   0,   0 ) -> Red
rgb(   0, 128, 128 ) -> Teal
rgb( 139,   0,   0 ) -> #8b0000
rgb(  72,  61, 139 ) -> #483d8b
rgb( 255, 255, 254 ) -> #fffffe
rgb( 255,  85, 255 ) -> #f5f
darkslateblue        -> #483d8b
indigo               -> Indigo
#f00                 -> Red

Commented code

s = ( c,
    rgb = ( r, g, b ) ->
        return ( 2 ** 24 + ( r << 16 ) + ( g << 8 ) + b )
        .toString( 16 )
) ->

This will save some bytes.

    d = y = document.body

q function will place input color to document.body.style.color and get compiled color as rgb(...). Also it will store result as hexColor:inputColor in d. Notice eval use. rgb(100,100,100) is a valid JavaScript function call with three number arguments. rgb function will convert arguments to single 24/12 HEX notation (like #fff, #f0f0f0).

    r = (
        q = ( a ) ->
            y.style.color = a
            b = '#' + eval( getComputedStyle( y ).color )[ 1... ].replace( /(.)\1(.)\2(.)\3/, '$1$2$3' )
            d[ b ] = a
            return b
    )( c )

Split string to array of color names, create lookup object.

    ( d = 'NavyGreenTeal...'.match( /.[a-z]+/g )).map( q )

And return HEX if no shorter variant in d.

    return d[ r ] or r


Posted 2015-10-05T21:23:23.017

Reputation: 91


Stylus, 238 234/2 = 117

More CSS-like solution! Stylus already has great support for color manipulation, so desired function is pretty small and not golfed a lot.

f(c){for n in split(' ''navy green teal indigo maroon purple olive gray sienna brown silver peru tan orchid plum violet khaki azure wheat beige salmon linen tomato coral orange pink gold bisque snow ivory red'){lookup(n)==c?c=s(n):c}}

Test it here

  color f(rgb(0, 0, 0))
  color f(rgb(255, 0, 0))
  color f(rgb(0, 128, 128))
  color f(rgb(139, 0, 0))
  color f(rgb(72, 61, 139))
  color f(rgb(255, 255, 254))
  color f(rgb(255, 85, 255))
  color f(darkslateblue)
  color f(indigo)
  color f(#f00)
  color f(rgba(255,0,0,1))
  color f(rgba(255,0,0,0.5))


Posted 2015-10-05T21:23:23.017

Reputation: 91

Welcome to PPCG! That's a really nice solution! – mbomb007 – 2015-10-07T21:10:28.510

not golfed a lot. You'd better try to golf it some more if you want to win. Someone is beating you by one. – mbomb007 – 2015-10-10T18:16:01.437

@mbomb007, alright, golfed a bit. – argh-argh – 2015-10-11T08:25:38.187

It looks as though you've been beaten. Maybe you can try cramming the colors together without spaces, but padded to five characters, then split every five and trim? Not sure if that would save bytes? – mbomb007 – 2015-10-14T13:45:50.103


Matlab 617

A lot of preprocessing and hardcoding. The minimal set of colour names you have to consider are these. Too bad Matlab does not have built in color names=/

function s=f(r,g,b);
a=[0 t t;240 t t;245 245 220;t 228 196;0 0 t;165 42 42;t 127 80;0 t t;t 215 0;75 0 130;t t 240;240 230 140;0 t 0;250 240 230;t 165 0;218 112 214;205 133 63;t 192 203;221 160 221;t 0 0;250 128 114;160 82 45;t 250 250;210 180 140;t 99 71;238 130 238;245 222 179;t t t;t t 0];
s=textscan('aqua azure beige bisque blue brown coral cyan gold indigo ivory khaki lime linen orange orchid peru pink plum red salmon sienna snow tan tomato violet wheat white yellow','%s');
if i>0;s=s{1}{i};elseif ~any(mod(c,16));k=16;end;d=dec2hex(c/k,2-(k>1))';s=['#',d(:)']


Posted 2015-10-05T21:23:23.017

Reputation: 40 560

You've left the hardcoded r=0;g=16;b=0; in there, though it's not counted in your score. – Hand-E-Food – 2015-10-06T00:34:33.887

Oh, I pasted the wrong version, thanks for letting me know! – flawr – 2015-10-06T07:54:17.683


Python 3, 857 795 bytes

Having to manually specify all of the accepted colours required did increase the byte count on this one :/

c(a) takes one argument, a, which comes in the form of rgb(#,#,#). From that, the rgb and brackets are removed and the string is then split by commas into an array. c(x,y,z) takes 3 ints, the r, g and b values of the rgb colour to process. We put those together in an array a. We then use Python's built-in hex function which converts a Base 10 number to a Base 16 number on our array and create a hex string (this is done in the for loop). The if statements convert colours like 000000 to 000, and replace the known colours using a dictionary.

Here it is (thanks to @undergroundmonorail for tip about ; in Python):

def c(x,y,z):
  for i in a:
    if len(i)<2:i="0"+i;
  if b[:3]==b[3:]:b=b[:3];
  if b in k:b=k[b];
  return b

Old Version:

def c(a):
  for i in a:
    if len(i)<2:
  k = {"00ffff":"AQUA","f0ffff":"AZURE","f5f5dc":"BEIGE","ffe4c4":"BISQUE","0000ff":"BLUE","a52a2a":"BROWN","ff7f50":"CORAL","ffd700":"GOLD","808080":"GRAY","008000":"GREEN","4b0082":"INDIGO","fffff0":"IVORY","f0e68c":"KHAKI","00ff00":"LIME","faf0e6":"LINEN","800000":"MAROON","000080":"NAVY","808000":"OLIVE","ffa500":"ORANGE","da70d6":"ORCHID","cd853f":"PERU","ffc0cb":"PINK","dda0dd":"PLUM","800080":"PURPLE","ff0000":"RED","fa8072":"SALMON","a0522d":"SIENNA","c0c0c0":"SILVER","fffafa":"SNOW","d2b48c":"TAN","008080":"TEAL","ff6347":"TOMATO","ee82ee":"VIOLET","f5deb3":"WHEAT","ffff00":"YELLOW"}
  if b[:3]==b[3:]:
  if b in k:
  return "color:"+b+";"

Maybe I'll add the bonus to it, I don't know yet. It could definitly do with 50% bytes off!



Posted 2015-10-05T21:23:23.017

Reputation: 121

2general python golfing tips: many of your newlines can be replaced by ;, saving the indentation. for example: a=a[4:-1].split(",");b="". any if, else, for, while, etc block with only one line can be written as if b in k:b=k[b].finally, you can lose a lot of your whitespace in the middle of lines, e.g. k = { -> k={ and return "color: -> return"color: – undergroundmonorail – 2015-10-06T08:32:20.870

also, welcome to PPCG :) – undergroundmonorail – 2015-10-06T08:32:51.000


That literal k is huge. As all color codes are in lowercase and all color names are in uppercase, you could just write it as a single string, then parse it with re.findall() (so will also need import re): http://pastebin.com/rQHqgxXS

– manatwork – 2015-10-06T09:37:24.180

@undergroundmonorail Thanks for that, should save a lot of space :D – Toastrackenigma – 2015-10-08T05:03:19.057

@mbomb007 OK, I wasn't quite sure how you wanted the output or input - got a little confused there :P I did run the code on the test cases to ensure it works properly, do you want me to upload my results? – Toastrackenigma – 2015-10-08T05:05:07.813


JavaScript (ES6), 499 611

Edit Added the test cases in the question

Note: I kept just the color names that are shorter than the hex equivalent.

Note 2: this can surely be golfed more...


.forEach(([r,g,b,t])=>(x=f(r,g,b),o+=r+','+g+','+b+' -> '+x+' '+(x.toUpperCase()==t.toUpperCase()?'ok':'error('+t+')')+'\n'),o='')

function go()
  var r,g,b
  [r,g,b] = I.value.match(/\d+/g)
  O.innerHTML=r+','+g+','+b+' -> '+f(r,g,b)+'\n'+O.innerHTML
R,G,B: <input id=I><button onclick="go()">--></button>
<pre id=O></pre>

Less golfed

f=(r,g,b) => (
     ? 1<<24|r<<16|g<<8|b
     : 4096|r/17<<8|g/17<<4|b/17
   s = "#d2b48cTAN#f00RED#ff7f50CORAL#f5deb3WHEAT#ff6347TOMATO#ffd700GOLD#008000GREEN#faf0e6LINEN#f5f5dcBEIGE#da70d6ORCHID#4b0082INDIGO#ffc0cbPINK#f0e68cKHAKI#008080TEAL#ee82eeVIOLET#dda0ddPLUM#fa8072SALMON#ffa500ORANGE#a0522dSIENNA#800000MAROON#800080PURPLE#ffe4c4BISQUE#f0ffffAZURE#fffff0IVORY#cd853fPERU#808000OLIVE#c0c0c0SILVER#fffafaSNOW#a52a2aBROWN#000080NAVY#808080GRAY",
   m = s.match(k+'([A-Z]+)'), // names are upper, hex codes are lower
   (m || [,k])[1] // if no match use the code


Posted 2015-10-05T21:23:23.017

Reputation: 31 086

Those underscores… According to my counting, would be shorter without them. Just quote the codes starting with a digit and keep the others unquoted: {f00:"red";"008000":"green"}. – manatwork – 2015-10-06T10:12:56.673

@manatwork nice trick. But a literal object is rarely a good choice for code golf. A simple string is better – edc65 – 2015-10-06T10:52:30.880

Correct. I already made a comment in that sense to our fresh sitemate.

– manatwork – 2015-10-06T10:56:13.100

1@mbomb007 Test cases added. The snippet won't work in chrome being EcmaScript 6, and Chrome is not fully compliant (quite tired to repeat this again and again). Test in Firefox. – edc65 – 2015-10-06T14:35:48.017