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\$ Commented Jan 1, 2016 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
    Commented Jan 1, 2016 at 14:42
  • 2
    \$\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\$ Commented Jan 1, 2016 at 14:46

37 Answers 37


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
    Commented Jan 1, 2016 at 1:08
  • \$\begingroup\$ I think your link is incorrect. (Copy-paste FTW.) \$\endgroup\$ Commented Jan 1, 2016 at 1:10
  • \$\begingroup\$ @Pietu1998 Whoops; I'll fix it. \$\endgroup\$
    – lirtosiast
    Commented Jan 1, 2016 at 1:42
  • \$\begingroup\$ The U is unnecessary - it's implicitly filled in by M. \$\endgroup\$
    – isaacg
    Commented Jan 1, 2016 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.

  • \$\begingroup\$ Did R not exist at the time? In 0.8.2 you can use Rd instead of 9-0, saving a byte. You can also write T`dA-F`Ro for the same 9 bytes. \$\endgroup\$
    – Neil
    Commented Jul 12, 2022 at 7:21

Julia 0.6, 74 49 bytes

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

Try it online!

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.


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\$
    – Riker
    Commented Jan 1, 2016 at 1:16
  • \$\begingroup\$ @RikerW Marbelous was created/fleshed out by several PPCG users including myself: See here. \$\endgroup\$
    – es1024
    Commented Jan 1, 2016 at 1:19
  • \$\begingroup\$ Okay. Thanks for letting me know. \$\endgroup\$
    – Riker
    Commented Jan 1, 2016 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\$ Commented Jan 1, 2016 at 0:07
  • \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ That's the same length? \$\endgroup\$ Commented Jan 1, 2016 at 0:08
  • \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ thanks, I used - instead of eval which saved even more bytes \$\endgroup\$
    – Downgoat
    Commented Jan 1, 2016 at 0:09
  • \$\begingroup\$ Oh, forgot about auto type casting :D \$\endgroup\$ Commented Jan 1, 2016 at 0:09
  • \$\begingroup\$ Try it with the input #FFFFFF. Returns #0. \$\endgroup\$ Commented Jan 1, 2016 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
    Commented Jan 1, 2016 at 8:06
  • \$\begingroup\$ @nder exactly, thought about it already :) Thanks. Was going to use it, but with "eval". \$\endgroup\$
    – nicael
    Commented Jan 1, 2016 at 8:09
  • 1
    \$\begingroup\$ ah, I've got one more -6: c=>c.replace(/\w/g,x=>"fedcba9876543210"[+('0x'+x)]) \$\endgroup\$
    – nderscore
    Commented Jan 1, 2016 at 8:13
  • \$\begingroup\$ c=>c.replace(/\w/g,x=>('0x'+x^15).toString(16)) \$\endgroup\$
    – l4m2
    Commented Aug 17, 2023 at 19:41

Vyxal, 4 bytes


Try it Online!

k6   # Hex chars - "012356789abcdef"
  Ḃ  # Bifurcate - push its reverse without popping
   Ŀ # Transliterate the input from the hex chars to the reverse hex chars

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.)


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\$ Commented Jan 1, 2016 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 '#'

x86-16 machine code, IBM PC DOS, 60 41 32 bytes


00000000: be82 00ac cd29 b106 ac3c 397e 022c 272c  .....)...<9~.,',
00000010: 30f6 d03c f976 0204 2704 40cd 29e2 e9c3  0..<.v..'.@.)...

Unassembled listing:

BE 0082     MOV  SI, 82H            ; command line input set to string 
AC          LODSB                   ; load first '#' char 
CD 29       INT  29H                ; write it to console 
B1 06       MOV  CL, 6              ; loop 6 chars 
AC          LODSB                   ; load next ASCII hex char into AL 
3C 39       CMP  AL, '9'            ; is char an alpha (a-f)? 
7E 02       JLE  NOHEX_IN           ; if not, is numeric 
2C 27       SUB  AL, 'f'-'9'-6      ; otherwise ASCII adjust a-f 
2C 30       SUB  AL, '0'            ; ASCII to decimal convert 
F6 D0       NOT  AL                 ; complement the color (magic happens here) 
3C F9       CMP  AL, 0F9H           ; is char <= 9? 
76 02       JBE  NOHEX_OUT          ; if not, is numeric 
04 27       ADD  AL, 'f'-'9'-6      ; otherwise ASCII adjust a-f 
04 40       ADD  AL, '0'+10H        ; decimal convert to ASCII 
CD 29       INT  29H                ; write char to console 
E2 E9       LOOP INLOOP             ; keep looping 
C3          RET                     ; return to DOS

So nearly all of this code is brute-force conversion from an ASCII hex string into a numeric value and then back again. No CPU instructions on x86-16 to help with that I'm afraid!


Standalone PC DOS executable. Input via command line, output to STDOUT.

enter image description here


Japt v2.0a0, 9 bytes


Try it

r\wÈnG'nF     :Implicit input of string
r             :Replace
 \w           :  RegEx /[0-9a-z]/gi
   È          :  Pass each match through the following function
    n         :    Toggle base
     G        :      16
      'n      :    Subtracting intermediate result from
        F     :      15

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
    Commented Jan 1, 2016 at 0:14

Java, 95 90 bytes

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

Bitwise XOR.


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
    Commented Jan 3, 2016 at 11:44
  • \$\begingroup\$ To reply to other users' comments, type @username. \$\endgroup\$
    – wizzwizz4
    Commented Jan 3, 2016 at 11:49
  • \$\begingroup\$ @wizzwizz4 Many thanks for your assistance. \$\endgroup\$
    – lpapez
    Commented Jan 3, 2016 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
    Commented Jan 3, 2016 at 12:05
  • \$\begingroup\$ @wizzwizz4 Certainly, I'll see what I can do. \$\endgroup\$
    – lpapez
    Commented Jan 3, 2016 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
    Commented Jun 12, 2016 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
    Commented Jan 5, 2016 at 22:36
  • \$\begingroup\$ Sure. See new edit. \$\endgroup\$ Commented Jan 5, 2016 at 23:24
  • \$\begingroup\$ Nice use of the APL symbol . \$\endgroup\$
    – lirtosiast
    Commented Jan 6, 2016 at 0:01
  • \$\begingroup\$ Hehe, that symbol was the one that made the most sense for /g. \$\endgroup\$ Commented Jan 6, 2016 at 0:39

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\$ Commented Jan 2, 2016 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\$ Commented Jan 2, 2016 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
    Commented Jan 2, 2016 at 19:00
  • \$\begingroup\$ The question says you can choose which case your input and output are. \$\endgroup\$
    – lirtosiast
    Commented Jan 5, 2016 at 4:22

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\$
    – Riker
    Commented Jun 13, 2016 at 0:33
  • \$\begingroup\$ int("ff", 16) can be replaced with just 255. \$\endgroup\$
    – Doorknob
    Commented Jun 13, 2016 at 1:54
  • \$\begingroup\$ Do you need the parentheses inside the brackets after each x? \$\endgroup\$
    – Blue
    Commented Jun 13, 2016 at 2:08

Befunge-98, 37 bytes

~f<^ s
>,^ >-:9w>'0+
^+W'    ]^

This takes input in lowercase, outputs the answer in lowercase, then enters an infinite loop.

Try it online!

40 byte version that terminates



This gets the first character of the input (which is always #), outputs it unchanged, and enters the loop.

~f<  s

At the start of the loop, f pushes 15 onto the stack, ~ gets the next character of the input, and s puts that character one space to its left in the source code (without executing it).


(I'm using I to represent the character from the input that is now in the source code.) This section loops back around to execute that character. Every character in the range 0-9 or a-f pushes the corresponding hex value on the stack.


The hex value is subtracted from 15 (the previous top of the stack) to get its complement. :9w creates a copy of the complement, and compares the copy with 9, with the IP going down, up or right depending on whether it is greater, less than, or equal.


If the complement is greater than 9, the IP goes down and hits the ] ("turn right") from above, turning to move left. If the copy was less than 9, the IP goes up and hits the ] from below, turning to move right. In that case, it gets redirected back to the third line. If the copy was exactly 9, the IP goes right and hits the > immediately.


If the complement is less than or equal to 9 (i.e. a digit), it is added to ASCII 48 (0) to produce the correct digit.


If the complement is greater than 9, it is added to ASCII 87 (W) to produce the correct character (in the range a-f).


In all cases, the character corresponding to the complement is printed and the IP moves back to the start of the loop.

The loop repeats, outputting the complement of every hex digit in the input.

After we print the answer, the input has been completely used up. Once EOF is reached, ~ reflects the IP. This means that ~f< is now an infinite loop that pushes 15 on the stack forever. This doesn't produce any effects (except for possibly crashing), so we don't care about it.


C (clang), 53 49 bytes


Try it online!

-4 thanks to @jdt

  • \$\begingroup\$ suggest using wide chars for -4 bytes \$\endgroup\$
    – jdt
    Commented Nov 22, 2022 at 16:34

Julia 1.0, 46 bytes


Try it online!

Julia 0.6, 40 bytes


Try it online!

Simply a bitwise not (~) on the full number, in hex, the rest around is to parse and print the numbers in hexadecimal and keeping the last 6 numbers


Nibbles, 8 bytes (16 nibbles)

`($+.$hex- 15hex
`($+.$hex- 15hex
`($                 # output the first element of input (the '#') 
                    # and save the rest;
    .$              # now, map over the rest
             hex    #   convert from hex,
         - 15       #   subtract from 15,
      hex           #   and convert back to hex
   +                # and convert the resulting list of characters to a string
                    # (which is implicitly output after the '#')

enter image description here


Go, 111 bytes

func f(s string)string{n,_:=ParseInt(s[1:],16,64)
return Sprintf("#%06x",0xffffff-n)}

Attempt This Online!


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.


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
    Commented Jun 12, 2016 at 16:52

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.