Given an input of a color in #rrggbb hex format, output its RGB complement in the same format.

The RGB complement R2G2B2 of any color R1G1B1 is defined as the color with R2 value 255 - R1, B2 value 255 - B1, and G2 value 255 - G1.

Hex digits may be either in uppercase (#FFAA20) or lowercase (#ffaa20). The case of the input and output need not be consistent (so you may take input in lowercase but output in uppercase, and vice versa).

Since this is , the shortest code in bytes wins.

Test cases (note that since giving your program/function its own output should result in the original input (it is involutory), the test cases should work in both directions):

In/Out   Out/In
#ffffff  #000000
#abcdef  #543210
#badcab  #452354
#133742  #ecc8bd
#a1b2c3  #5e4d3c
#7f7f80  #80807f
  • 2
    \$\begingroup\$ I'm sorry, but sRGB doesn't work that way. You should convert to linear space first, which hex-codes aren't in. \$\endgroup\$ – John Dvorak Jan 1 '16 at 7:38
  • 2
    \$\begingroup\$ @JanDvorak Oh well. The state of the challenge will reflect my ignorance, then, since I can't really change it now. :P \$\endgroup\$ – Doorknob Jan 1 '16 at 14:42
  • \$\begingroup\$ Averaging two values in sRGB could be a decent separate challenge, though. sRGB = RGB^0.45 over most of the range, but linear near the bottom of the range. \$\endgroup\$ – John Dvorak Jan 1 '16 at 14:46

25 Answers 25


Pyth, 9 8 bytes

Thanks to @isaacg for -1 byte!


Subtracting a certain color's value from 255 is equivalent to subtracting each of its hexadecimal digits from 15. Say a number is 16a+b. Then the value of the number created by subtracting its digits from 15 is 16(15-a) + (15-b) = 255 - (16a+b).

sXz.HM16     implicit: z=input()
   .HM        map hex representation over range
   .HM16     '0123456789abcdef'
  z           the input string
 X            Translate characters in x1 present in x2 to reversed x2
              that is, '0' becomes f, '1' becomes 'e', and so on.
              The initial '#' is unchanged.
s             That produced a list, so join into a string by reducing +

Try it here. Test suite.

  • \$\begingroup\$ Nice answer! I borrowed your idea of using '0123456789abcdef' to convert to hex (instead of dec2hex function) \$\endgroup\$ – Luis Mendo Jan 1 '16 at 1:08
  • \$\begingroup\$ I think your link is incorrect. (Copy-paste FTW.) \$\endgroup\$ – PurkkaKoodari Jan 1 '16 at 1:10
  • \$\begingroup\$ @Pietu1998 Whoops; I'll fix it. \$\endgroup\$ – lirtosiast Jan 1 '16 at 1:42
  • \$\begingroup\$ The U is unnecessary - it's implicitly filled in by M. \$\endgroup\$ – isaacg Jan 1 '16 at 11:39

Retina, 13 10 bytes


There are three parts to the code, separated by backticks (`): T specifies transliterate mode, which replaces each character in the second part with its corresponding character in the third part.

w is the same as traditional regex's \w, or _0-9A-Za-z, which is expanded to _0123456789ABCDEFGH....

The second part is expanded to GFEDCBA9876543210, thanks to Retina's nifty ability to expand in reverse order. Put these on top of each other, and we get:


Note that the last character, 0, is repeated to fit the length of the longer string, but we only care about the hexadecimal characters, shown by carets.

Thanks to Martin Büttner for suggesting this approach.

Try the test suite online.


Marbelous, 41 bytes


Online Interpreter here. Input should be in uppercase.


The 00 and ]] at the bottom will fetch the first character (the #) and it will fall to the bottom and be outputted before anything else.

The first 3 lines are a loop to fetch all the remaining characters.

First we need to convert the hex digit characters to 0-15, by doing x -= 48, x -= x > 9 ? 7 : 0 (since 'A' - '9' is 8).

To find the complement, we simply need to convert every digit x to 15-x. This is equivalent to (for 8-bit values) (~x)+16 = ~(x-16).

Finally, we have to convert these numbers back into hex digits, by doing x += x > 9 ? 7 : 0, x += 48.

So now we have x -= 48, x -= x > 9 ? 7 : 0, x = ~(x - 16), x += x > 9 ? 7 : 0, x += 48.

Note that if we remove the expression with the first ternary operator, then input digits A-F will result in a negative x after negation.

Thus we can change the previous expression to: x -= 48, x -= 16, x = ~x, x += (x > 9 || x < 0) ? 7 : 0, x += 48, which is equal to x -= 64, x = ~x, x += (x > 9 || x < 0) ? 7 : 0, x += 48.

The above code is just an implementation of the last expression. -W is x -= 32 and +O is x += 24. Since Marbelous uses unsigned 8-bit arithmetic, the condition <A covers both the case of x > 9 and x < 0.

  • \$\begingroup\$ Did you create Marbelous? \$\endgroup\$ – Rɪᴋᴇʀ Jan 1 '16 at 1:16
  • \$\begingroup\$ @RikerW Marbelous was created/fleshed out by several PPCG users including myself: See here. \$\endgroup\$ – es1024 Jan 1 '16 at 1:19
  • \$\begingroup\$ Okay. Thanks for letting me know. \$\endgroup\$ – Rɪᴋᴇʀ Jan 1 '16 at 1:21

JavaScript ES6, 61 bytes 66 68 48 53 64

Saves quite a few bytes thanks to @Cᴏɴᴏʀ O'Bʀɪᴇɴ, @NinjaBearMonkey, and @nderscore


Takes advantage of auto-type-casting. Fixing the zeros killed the byte count

  • \$\begingroup\$ Use eval(`0x${s.slice(1)}`) instead of parseInt \$\endgroup\$ – Conor O'Brien Jan 1 '16 at 0:07
  • \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ That's the same length? \$\endgroup\$ – PurkkaKoodari Jan 1 '16 at 0:08
  • \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ thanks, I used - instead of eval which saved even more bytes \$\endgroup\$ – Downgoat Jan 1 '16 at 0:09
  • \$\begingroup\$ Oh, forgot about auto type casting :D \$\endgroup\$ – Conor O'Brien Jan 1 '16 at 0:09
  • \$\begingroup\$ Try it with the input #FFFFFF. Returns #0. \$\endgroup\$ – Conor O'Brien Jan 1 '16 at 0:19

JavaScript ES6, 63 58 52 49 bytes


Thanks to nderscore for saving 11 bytes!

  • \$\begingroup\$ -5 bytes: c=>"#"+[...c].map(x=>"fedcba9876543210"[+('0x'+x)]).join`` \$\endgroup\$ – nderscore Jan 1 '16 at 8:06
  • \$\begingroup\$ @nder exactly, thought about it already :) Thanks. Was going to use it, but with "eval". \$\endgroup\$ – nicael Jan 1 '16 at 8:09
  • 1
    \$\begingroup\$ ah, I've got one more -6: c=>c.replace(/\w/g,x=>"fedcba9876543210"[+('0x'+x)]) \$\endgroup\$ – nderscore Jan 1 '16 at 8:13

Jolf, 17 bytes

pq#-w^88C Li1~560
pq                pad
         _Li1     the input, w/out the first char
        C    ~5   parsed as a base-16 integer (~5 = 16)
    w^88          8 ^ 8 - 1 (the magic number)
   -              subtract the parsed input from said number
  #               convert result to hexadecimal
               60 pad the result with 6 0's

Try it here!, Test suite (Use full run, which now works.)


Julia, 74 49 bytes

h->"#"join([hex(15-parse(Int,i,16))for i=h[2:7]])

Pretty long at the moment but it's a start. This is a lambda function that accepts a string and returns a string. The output will be in lowercase but the input can be in either.

As Thomas noted, subtracting each 2-digit color component from 255 is equivalent to subtracting each individual digit in the hexadecimal input from 15. Looping over the input string, excluding the leading #, we convert 15 - the parsed digit to hexadecimal. We join all of these then tack on a # and call it good.


Japt, 35 32 22 20 16 15 bytes

¡Y?(F-XnG)sG :X


¡                 //Take input and map (shortcut to "Um@"). Input should in the form of "#123456"
 Y?               //if Y is not 0, then return (F-XnG)sG, otherwise last step...
    F-XnG           //Subtract X, converted from hexadecimal (G is 16) to decimal, from 15
          sG        //convert decimal to hexadecimal
             :X   //...otherwise return X unchanged (happens only with #, the first char)
  • \$\begingroup\$ Nice one! I saw this last night and said to myself, "Tomorrow I'll help to golf it down." But by now you've golfed it further than I thought possible. :) \$\endgroup\$ – ETHproductions Jan 1 '16 at 19:54

Perl, 30 bytes

includes +1 for -p


usage: echo #000000 | perl -p file.pl
or echo #000000 | perl -pe 's/\w/sprintf"%x",15&~hex$&/eg'.


MATL, 21 bytes


This uses release 6.0.0 of the language/compiler, which is earlier than the challenge.

Input digits should be uppercase.


This has been executed on Octave:

>> matl
 > 35,5Y216,j6L)!16ZA-)h
> #FFAA20

Edit (June 12, 2016)

The code can now be tried online. Commas need to be replaced by spaces, and 6L by 4L, to conform to changes in the language.


35,             % number literal: ASCII code of '#'
5Y2             % '0123456789ABCDEF'
16,             % number literal
j               % input string
6L)             % remove first element
!               % transpose
16ZA            % convert from hex to dec
-               % subtract from 16
)               % index into '0123456789ABCDEF' to convert back to hex
h               % prepend 35, which gets automatically converted into '#'

Pyth, 20 19 bytes

1 byte thanks to xnor.

%"#%06x"-t^8 8itz16

Try it online. Test suite.


  • z is the input
  • tz removes the #
  • itz16 parses as a hexadecimal number
  • t^8 8 calculates 88 - 1
  • -t^8 8itz16 calculates 88 - 1 - input
  • %"#%06x"-t^2 24itz16 formats it into a zero-padded 6-character hex string and adds the #
  • \$\begingroup\$ How about 8^8 for 2^24? \$\endgroup\$ – xnor Jan 1 '16 at 0:14

Haskell, 85 bytes

My first submission, it will probably be the longest (85 bytes) but hey, you got to start somewhere. In Haskell:

import Numeric;f=('#':).concatMap((flip showHex)"".(15-).fst.head.readHex.(:[])).tail

It's using the same subtract from 15 trick I saw other people use.

I also tried using printf along with the other trick (subtract 8^8 - 1) and it works in ghci but for some reason it doesn't compile:

g=printf "#%06x" .(8^8-1-).fst.head.readHex.tail

If someone could make this work that would be great!

  • \$\begingroup\$ Welcome to Programming Puzzles and Code Golf. These are very good answers (better than I could do in Haskell!). However, there is a common answer format for code-golf challenges which lets the Leaderboard snippet put your answer in the leaderboard. I'll edit it in for you; you can accept or deny the edit. \$\endgroup\$ – wizzwizz4 Jan 3 '16 at 11:44
  • \$\begingroup\$ To reply to other users' comments, type @username. \$\endgroup\$ – wizzwizz4 Jan 3 '16 at 11:49
  • \$\begingroup\$ @wizzwizz4 Many thanks for your assistance. \$\endgroup\$ – lpapez Jan 3 '16 at 11:58
  • \$\begingroup\$ Any time! Do you want to have a go at some of my challenges? Analyse your chair and Map string to Hilbert Curve are my favourites, although the Hilbert curve one is really hard. \$\endgroup\$ – wizzwizz4 Jan 3 '16 at 12:05
  • \$\begingroup\$ @wizzwizz4 Certainly, I'll see what I can do. \$\endgroup\$ – lpapez Jan 3 '16 at 12:10

Mathematica, 69 60 bytes


Once again, it's the string processing that kills me here.


C, 94 bytes

t(char*b){for(int i;i=*b;++b)*b=i>96&i<103?150-i:i>64&i<71|i>47&i<54?118-i:i>53&i<58?111-i:i;}

Function takes in a char array, returns inverse value. Produces uppercase letters for response. The code flips each ASCII hex character to its inverse if it's valid, ignores it otherwise.

  • \$\begingroup\$ You can save some space by globally declaring i before the function: i; \$\endgroup\$ – Liam Jun 12 '16 at 16:49

𝔼𝕊𝕄𝕚𝕟 2, 18 chars / 34 bytes


Try it here (Firefox only).

Using a version created after the challenge.


ïē/\w⌿,↪(ḏ-`ᶍ⦃$}”ⓧ // implicit: ï=input, ḏ=15
ïē/\w⌿,             // replace all alphanumeric chars in ï with:
       ↪(ḏ-`ᶍ⦃$}”ⓧ // (15 - char's decimal form) converted to hex
                    // implicit output

Non-competitive solution, 15 chars / 29 bytes


Uses transliteration.

  • \$\begingroup\$ Can you add a competitive version as well, so we can see how the language stacks up? \$\endgroup\$ – lirtosiast Jan 5 '16 at 22:36
  • \$\begingroup\$ Sure. See new edit. \$\endgroup\$ – Mama Fun Roll Jan 5 '16 at 23:24
  • \$\begingroup\$ Nice use of the APL symbol . \$\endgroup\$ – lirtosiast Jan 6 '16 at 0:01
  • \$\begingroup\$ Hehe, that symbol was the one that made the most sense for /g. \$\endgroup\$ – Mama Fun Roll Jan 6 '16 at 0:39

Python, 96

for i in range(3):

First code golf, please give opinions :)

  • \$\begingroup\$ You are allowed to require the input be in "quotes" btw, so input() works. You don't need the newline and indent on the for loop, and range works just fine. There are also a couple spaces you can remove. \$\endgroup\$ – Rɪᴋᴇʀ Jun 13 '16 at 0:33
  • \$\begingroup\$ int("ff", 16) can be replaced with just 255. \$\endgroup\$ – Doorknob Jun 13 '16 at 1:54
  • \$\begingroup\$ Do you need the parentheses inside the brackets after each x? \$\endgroup\$ – Blue Jun 13 '16 at 2:08

CJam, 16 bytes


This is fairly long because CJam handles base changes differently, so it was shorter to just do transliteration. See my Retina answer for more on transliteration.

Try it online.


q      e# Get the input
A,     e# Push [0 1 ... 8 9]
'G,65> e# Push "ABCDEF"
+s     e# Combine and convert to string
_W%    e# Make a copy and reverse it
er     e# Replace each character in the first string with
       e# the corresponding character in the second

Python 3, 44 bytes

Originally I used 256^3, then 16^6. Then I saw Pietu1998's 8^8 and now this solution uses that instead.


Java, 95 90 bytes

String f(String v){return v.format("#%06x",0xFFFFFF^Integer.parseInt(v.substring(1),16));}

Bitwise XOR.


Bash + tr, 35 bytes

tr 0-9A-Fa-f fedcba9876543210543210

Output is always lowercase.

Unfortunately "tr" does not take ranges in reverse order so I had to spell them out.


C, 147 bytes

void p(char s[]){char c[3];int i=1;printf("#");while(i<6){strncpy(c,s+i++,2);i++;int x=255-strtol(c,NULL,16);x<10?printf("0%x",x):printf("%x",x);}}

Used strtol to convert from hex string to int then subtracted the number from 255 to get the compliment like the original post said. I am, however, wondering if there is a way to pass a range of characters from s to strtol so I don't have to waste a bunch of bytes copying to a new string?

  • \$\begingroup\$ I don't think compilers usually enforce function returns and default to int, so you an probably omit the void return type? \$\endgroup\$ – Liam Jun 12 '16 at 16:52

R, 62 bytes


sed, 48 bytes


Or 36 bytes if you only need to support one case.

  • \$\begingroup\$ You don't need to support both lowercase and uppercase. \$\endgroup\$ – Martin Ender Jan 2 '16 at 0:29
  • \$\begingroup\$ @MartinBüttner the question seems to require support for both cases in the input, but not in the output. Like my bash+tr solution, this solution supports both on input but only writes lower-case output. \$\endgroup\$ – Glenn Randers-Pehrson Jan 2 '16 at 13:30
  • \$\begingroup\$ You've got an extra 0 in your code, between the 9 and A (byte count is correct though, must be a copying error). \$\endgroup\$ – hobbs Jan 2 '16 at 19:00
  • \$\begingroup\$ The question says you can choose which case your input and output are. \$\endgroup\$ – lirtosiast Jan 5 '16 at 4:22

PowerShell, 48 bytes


Necessarily takes input via param($a) as a string, delimited with ' or ", since C:\Tools\Scripts\Golfing> .\complementary-colors #a1b2c3 on the PowerShell command line will treat #a1b2c3 as a comment and summarily ignore it.

Left-to-right, the "#{0:x6}"-f(...) formats our output calculations back into hexadecimal with a guaranteed 6 characters (to account for input #ffffff). Inside the parens, we subtract our input number from 0xffffff. We do this by leveraging the fact that PowerShell parses hexadecimal numbers in format 0xNNN, so we construct a proper-format hex number from our input number $a. (Note that concatenation plus .Trim() is shorter than .Replace() here by one byte.) We also leverage the MB unary operator via 16MB-1 to construct 16777215 instead of 0xffffff.


TeaScript, 24 bytes


Bugs in the interpreter aren't letting me get this shorter :(

Try it online

  • \$\begingroup\$ Downvote explanation? It works for all the test cases \$\endgroup\$ – Downgoat Jan 1 '16 at 17:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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