Converting an RGB Color To Hex With JavaScript

code 0 comments suggest edit

UPDATE: 12/30 I had transposed the rgb colors. I corrected the function.

I’ve been distracted by a new jQuery plugin that I’m writing. The plugin has certain situations where it sets various background and foreground colors. You can have it set those styles explicitly or you can have it set a CSS class, and let the CSS stylesheet do the work.

color-wheelI’m writing some unit tests to test the former behavior and ran into an annoying quirk. When testing the color value in IE, I’ll get something like #e0e0e0, but when testing it in FireFox, I get rgb(224, 224, 224).

Here’s a function I wrote that normalizes colors to the hex format. Thus if the specified color string is already hex, it returns the string. If it’s in rgb format, it converts it to hex.

function colorToHex(color) {
    if (color.substr(0, 1) === '#') {
        return color;
    }
    var digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color);
    
    var red = parseInt(digits[2]);
    var green = parseInt(digits[3]);
    var blue = parseInt(digits[4]);
    
    var rgb = blue | (green << 8) | (red << 16);
    return digits[1] + '#' + rgb.toString(16);
};

Now, I can compare colors like so.

equals(colorToHex('rgb(120, 120, 240)'), '#7878f0');

I hope you find this useful. :)

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

49 responses

  1. Avatar for James Fuller
    James Fuller December 29th, 2009

    I just wanted to point out, it looks like this code is switching the red and blue value, if you use the rgb values(120,120,240) your code returns #F07878 which is dominantly red, rather than dominantly blue, a fix to this is to remove the multiplier in front of the blue and multiply the red value by 65536. Just a quick analysis. Useful code otherwise.

  2. Avatar for Sergio Pereira
    Sergio Pereira December 29th, 2009

    If you want to normalize colors beyond the specific IE vs Firefox problem, you'll need to consider a few other color representations, like "rgb(10%, 30%, 30%)", "#aaa", "rgba(12, 34, 56, 20)".
    Also, my JavaScript scars make me call out the missing radix parameter in parseInt.
    If you download jquery-ui (full) you'll find a function called getRGB that normalizes colors into RGB arrays. Too bad the function is private.

  3. Avatar for Peter Obiefuna
    Peter Obiefuna December 29th, 2009

    Kinda sounds like the same pain we know too well -- a pain deliberately inflicted on the world by MSFT when they chose to make IE non standard.
    Let me guess: google chrome and safari handled the rgb thing just like Firefox didn't they?
    Anyway, just to say, welcome to my world.

  4. Avatar for Artiom Chilaru
    Artiom Chilaru December 29th, 2009

    I will have to agree with James Fuller. It seems to me as well that you have to swap the code to:
    var dec = 65536 * red + 256 * green + blue

  5. Avatar for David Fowler
    David Fowler December 29th, 2009


    var rgb = b | (g << 8) | (r << 16)

    :)

  6. Avatar for "Cowboy" Ben Alman
    "Cowboy" Ben Alman December 29th, 2009


    function hex( c ) {
    var m = /rgba?\((\d+), (\d+), (\d+)/.exec( c );
    return m
    ? '#' + ( m[1] << 16 | m[2] << 8 | m[3] ).toString(16)
    : c;
    };
    hex( 'rgba(255, 136, 17, 0.5)' ); // '#ff8811'
    hex( 'rgb(255, 136, 17)' ); // '#ff8811'
    hex( '#ff8811' ); // '#ff8811'
    hex( '#f81' ); // '#f81'

  7. Avatar for Justin
    Justin December 29th, 2009

    hehe, I ran into this same thing a few months back. Came up with a similar solution but for the rgb (0, 0, 0) route I ended up getting the sub-string of what's in the '()' and splitting on the commas for the parts.
    Nice solution, thanks :)

  8. Avatar for partha
    partha December 30th, 2009

    Can you explain this in more detail?
    equals(colorToHex($('foo').css('color')), '#e0e0e0');
    What's the $('foo')?
    Can you give a detailed example of using the function?

  9. Avatar for John
    John December 30th, 2009
  10. Avatar for Pita.O
    Pita.O December 30th, 2009

    Hi Partha:
    Please don't mind the sassiness.
    $('foo') is a j@uery capture expression for an arbitrary html element foo. An specific example would be #('div').
    So, colorToHex() function converts the css.color of that capture to Hexadesimal.
    Finally, the equals() function checks if that hex value is equal to the string '#e0e0e0'

  11. Avatar for Pita.O
    Pita.O December 30th, 2009

    Spelling corrections:
    jQuery: not j@uery
    A specific example would be $('div'): not "An ... #('div')

  12. Avatar for haacked
    haacked December 30th, 2009

    Serves me right, testing against a color where the rgb values are all the same was stupid stupid stupid.
    Thanks James for pointing out the error. Also, thanks to David fowler for pointing out the clever bit shifting approach.
    I've corrected the function.

  13. Avatar for Adam
    Adam December 30th, 2009

    Be careful with parseInt. It will return the wrong result for '01' so either use a + or parseInt("01",10)

  14. Avatar for Doeke
    Doeke January 1st, 2010

    I would also add some optional whitespace to the regex:
    /(.*?)rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/

  15. Avatar for NC
    NC January 3rd, 2010

    $('body').append('<div class="asdfghjkl"></div>');
    $('.asdfghjkl').hide();
    $('.asdfghjkl').css({ 'background': '#e0e0e0' });
    equals($('.somecontainer').css("background-color"), $('.asdfghjkl').css("background-color"));

  16. Avatar for waqas
    waqas January 3rd, 2010

    great job,
    keep it up

  17. Avatar for Loukas
    Loukas January 4th, 2010

    Great post!
    I hope you enjoyed Elmo's Potty Time...

  18. Avatar for Divxindirizle
    Divxindirizle January 8th, 2010

    Thank you for these useful informations. i will apply them right away

  19. Avatar for iPhoneK&#246;nig
    iPhoneK&#246;nig January 9th, 2010

    Thank you for these Great post!

  20. Avatar for Gerbus
    Gerbus February 11th, 2010

    "Cowboy" Ben's solution in original structure:
    function RGBtoHex(rgb_color) {
    if (rgb_color.substr(0, 1) === '#') {
    return rgb_color;
    }
    var digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(rgb_color);

    var red = parseInt(digits[2]);
    var green = parseInt(digits[3]);
    var blue = parseInt(digits[4]);

    var hex = (red << 16) | (green << 8) | blue;
    return digits[1] + '#' + hex.toString(16);
    }

  21. Avatar for Chris Hefley
    Chris Hefley April 12th, 2010

    Maybe I'm missing something, but it appears that none of these solutions work with RGB values where the first value is 0. Hex numbers that should be rendered as #00ff00 come back as #ff00
    Am I missing something? Please tell me I am, cause I'm tired of messing with this on my own. :)

  22. Avatar for Andrey
    Andrey April 12th, 2010

    Chris,
    following line produces such a behaviour:
    rgb = blue | (green << 8) | (red << 16)
    For color #00ff00 red is zero, 0 << 16 gives 0, so
    0 | (255 << 8) | 0 just equals to 0 | (255 << 8) = 65280 = FF00
    For such a cases leading zeros needed.

  23. Avatar for Andy K
    Andy K May 17th, 2010

    I use this function to compare in javascript for toggling color in HTML but didn't work. Any suggest ??
    ...
    ...
    <label style='color:#000000;' id='xx'>
    ...
    ...
    <script>
    function addtag(tag_)
    {
    if(document.getElementById('xx').style.color.toString(16) == rgb(0,0,0))
    {
    document.getElementById('xx').style.color = "#fff";
    }
    else
    {
    document.getElementById('xx').style.color = "#000";
    }
    </script>

  24. Avatar for Go2Store
    Go2Store June 5th, 2010

    Thank you for these Great post!
    it cool tip !!

  25. Avatar for Smithy
    Smithy June 24th, 2010

    Doeke is absolutely right: FF3.6 reports rgb colours with white space in them: rgb(34, 34, 34) - while IE8 has no spaces: rgb(34,34,34).
    Thanks for the solutions!

  26. Avatar for myEnemy
    myEnemy July 7th, 2010

    Hi, i've had the same problem comparing color terms like #00FFFF or #FF0FFF. My solution was to create leading zeros by brute force... Sorry for destroying your beautiful code :-)
    function fillZero(myString) {
    if (myString.length == 1) return "0" + myString;
    else return myString;
    }
    function colorToHex(color) {
    var digits = /(.*?)rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/.exec(color);
    if (digits == null) return "";
    var red = parseInt(digits[2]);
    var green = parseInt(digits[3]);
    var blue = parseInt(digits[4]);
    var hexcode = fillZero(red.toString(16)) + fillZero(green.toString(16)) + fillZero(blue.toString(16));
    return '#' + hexcode.toUpperCase();
    }

  27. Avatar for Rod Byrnes
    Rod Byrnes July 25th, 2010

    The following works. It's a modification of Cowboy Ben's elegant solution but takes into account variable whitespace and colors with leading zeros.

    function colorToHex(c) {
    var m = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(c);
    return m ? (1 << 24 | m[1] << 16 | m[2] << 8 | m[3]).toString(16).substr(1) : c;
    }

  28. Avatar for Dave Cook
    Dave Cook August 4th, 2010

    One more tiny convenience fix - add the leading '#' needed for sending the output string back into jQuery, Google Earth etc. APIs:

    function colorToHex(c) {
    var m = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(c);
    return m ? '#' + (1 << 24 | m[1] << 16 | m[2] << 8 | m[3]).toString(16).substr(1) : c;
    }

  29. Avatar for glass shelving systems
    glass shelving systems December 15th, 2010

    Good tutorials
    Thanks

  30. Avatar for zegarek84
    zegarek84 January 13th, 2011

    "rgb(11,22,33)", "rgb(10%, 30%, 30%)", "#aaa", "#aaaaaa", rgba(12, 34, 56, 1)"

    /*
    * @param {string} sColor
    * @return {integer|false}
    */
    function colorToInt(sColor){
    var sFirst = sColor[0], i, aFromReg, iR, iG, iB;
    if(!sFirst)
    return false;
    if(sFirst==='#') {
    if(!!sColor[6]) {
    sColor = sColor.substr(1, 6);
    } else {
    sColor = [sColor[1],sColor[1],sColor[2],sColor[2],sColor[3],sColor[3]].join('');
    }
    i = parseInt(sColor, 16);
    } else {
    /*
    * few other color representations, like "rgb(10%, 30%, 30%)", "rgba(12, 34, 56, 20)"
    */
    aFromReg = /rgba?\s*\(\s*(\d+)(\D)\D*(\d+)\D*(\d+)/i.exec(sColor);
    if(!aFromReg) return false;
    iR = parseInt(aFromReg[1], 10);
    iG = parseInt(aFromReg[3], 10);
    iB = parseInt(aFromReg[4], 10);
    if (aFromReg[2]==='%') {
    iR = Math.round(iR*2.55);
    iG = Math.round(iG*2.55);
    iB = Math.round(iB*2.55);
    }
    i = iR<<16|iG<<8|iB;
    // fix gdyby czasem liczby były większe niż 255 podał 16777215 = (1<<24) - 1;
    i&=16777215;
    aFromReg = iR = iG = iB = null;
    }
    sFirst = sColor = null;
    return i;
    }
    /*
    * @param {integer} integer
    * @return {string}
    */
    function intToHexColor(integer) {
    // to gdyby ktoś za dużą wartość podał 16777215 = (1<<24) - 1;
    integer&=16777215;
    // gdy są z przodu zera to rzutowanie do stringa jest nie poprawne
    // - lepiej o 1 znak więcej niech wygeneruje a potem przyciąć ;)
    integer|=1<<24;
    return ['#',integer.toString(16).substr(1, 6)].join('');
    }
    /*
    * @param {integer} integer
    * @return {string}
    */
    function intToRGBColor(integer) {
    return ['RGB(',integer>>16 & 255,', ',integer>>8 & 255,', ',integer & 255,')'].join('');
    }
    /*
    * @param {integer} integer
    * @return {string}
    */
    function intToRGBaColor(integer) {
    return ['RGB(',integer>>16 & 255,', ',integer>>8 & 255,', ',integer & 255,', 1)'].join('');
    }

  31. Avatar for Kenji Yamamoto
    Kenji Yamamoto November 8th, 2011

    I'm create a new function named "ZeroFill" to solve the problem with Hex starting by "0", take a look:
    zeroFill = function(number, limit) {
    for (var i = number.toString().length; i < limit; i++) {
    number = '0' + number;
    }
    return number;
    };
    convertColors = function( colors ){
    var m = /rgba?\((\d+), (\d+), (\d+)/.exec( colors );
    if( m ){
    return "#" + zeroFill( ( m[1] << 16 | m[2] << 8 | m[3] ).toString(16), 6 );
    }else{
    return colors;
    }
    };

  32. Avatar for sean
    sean December 20th, 2011

    I once wrote a script wherein I had to do something similar to this about 100,000 times (see the link). One thing that bothered me is that there was something about it that was rather slow. Just curious, any guess as to what it is? Mine didn't include the regex, so that's not it, perhaps toString()?
    I am just typing words, maybe I should go figure it out myself ;)

  33. Avatar for Sam
    Sam January 3rd, 2012

    Sean,
    It might be the bit shifting - Javascript keeps all numbers in floating point format, so it first converts to integer, then does the bit shift, then converts back to floating point again. On some JS implementations this could be slow.

  34. Avatar for Saeed Neamati
    Saeed Neamati January 22nd, 2012

    This function really helped me. I think the reverse function would also be a good utility. Something like HexToRgb('#73bb42') which returns rbg(x, y, z)

  35. Avatar for Rob
    Rob September 26th, 2012

    The method that Dave Cook posted is the most concise, and it returns leading zeros where needed. Thanks Dave!

  36. Avatar for Fernando
    Fernando October 27th, 2012

    You, dear sir, just saved my life. THANK YOU A LOT!

  37. Avatar for rak supermarket murah
    rak supermarket murah November 17th, 2012

    thank u for very usefull code

  38. Avatar for Jore
    Jore November 17th, 2012

    My solution:
    function getColor(color) {
    var newColor = '#000000';
    var red = parseInt(color.substr(1,2),16);
    var green = parseInt(color.substr(3,2),16);
    var blue = parseInt(color.substr(5,2),16);
    var rgb = blue | (green << 8) | (red << 16);
    var rgbs = rgb.toString(16);
    newColor = newColor.substr(0,7-rgbs.length)+rgbs;
    return newColor;
    }

  39. Avatar for chanpreet singh
    chanpreet singh December 1st, 2012

    hi, can you please tell me that how we could write a Function in C lAnguage in which we have to use a variable to store Color values RGB(8 bit each) using bit operations.?

  40. Avatar for Carson Powers
    Carson Powers March 4th, 2013

    function colorToHex(color) {
        if (color.substr(0, 1) === '#') {
            return color;
        }
        var digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color);

        var red = parseInt(digits[2]);
        var green = parseInt(digits[3]);
        var blue = parseInt(digits[4]);

        var rgb = blue | (green << 8) | (red << 16);

        switch (rgb.toString(16).length) {
            case 2:
                rgb = "0000" + rgb.toString(16)
            break;
            case 4:
                rgb = "00" + rgb.toString(16)
                break;
            default:
                rgb = rgb.toString(16)
        }
        return digits[1] + '#' + rgb
    };

    Isn't pretty but this fixes the bug that causes what Chris Hefley found.

  41. Avatar for Juscelino Tanaka
    Juscelino Tanaka March 11th, 2013

    Hi! I'm searching a function to "plot" a image like these on your post. Anything where I can give r,g,b . i.e: printDot(r,g,b [, x, y]); and it plots a image like these. I need a function because I'll plot these here, but after I'll need take the color based on screen position click. Could you help me?

  42. Avatar for aditya sharma
    aditya sharma March 22nd, 2013

    Great work man this the thing that I was looking for, it solved one of my major problems, keep up the good work, amazing function

  43. Avatar for Mateusz
    Mateusz July 25th, 2013

    If you change last two lines (except the parantheses) to:
    var rgb = blue | (green << 8) | (red << 16) | (1 << 24);
    return digits[1] + '#' + rgb.toString(16).substring(1,8);
    works much better. Otherwise it omits leading zeroes...

  44. Avatar for Ross
    Ross June 4th, 2014

    This doesn't work - I found at least leading '0's get stripped

  45. Avatar for Markus Prokott
    Markus Prokott August 6th, 2014

    Nice code. First I was going to use this. But considering the possible performance drawbacks mentioned here and the missing leading zero support, I produced this:

    if (match) {
    // match[1], match[2], match[3] contain strings of integers between 0 and 255
    var
    r = ("0" + parseInt(match[1]).toString(16)).slice(-2),
    g = ("0" + parseInt(match[2]).toString(16)).slice(-2),
    b = ("0" + parseInt(match[3]).toString(16)).slice(-2);

    return "#" + r + g + b;
    }

    This doesn't need any explicit arithmetic, neither bit-wise nor numeric.

  46. Avatar for Tim Erwin
    Tim Erwin October 6th, 2014

    this doesn't convert correctly: colorToHex('rgb(10, 0, 0)') results in #a0000

  47. Avatar for Daniel Faure
    Daniel Faure September 4th, 2015

    Very useful!

    I need to consider rgba on regex, and some spaces after the comma, so I used

    var digits = /(.*?)rgba?\((\d+),\s?(\d+),\s?(\d+),\s?(\d+)\)/.exec(color);

  48. Avatar for Luis
    Luis September 14th, 2015

    Thank you so much. You've help me.

  49. Avatar for Sam sandy
    Sam sandy February 13th, 2018

    RGB and hex are not different color spaces. Hex is just a compact way to represent a RGB color using three bytes, using a byte for each channel with their values written in hexadecimal.