5

I have a colour picker where the user can specify a hex colour.

I also want a saturation slider where the user can adjust the saturation, and get the new hex colour as an output.

Is there a way I can, in JavaScript, convert a saturation value, and a hex colour, into a new hex colour?

So, say for example I have a value #FF0000 and a saturation of 50 (out of 100) how would I ascertain the new hex colour from this?

I can't use any libraries for it because I'm creating it as a plugin for my website, and I'm trying to keep it as light as possible.

Dan Hanly
  • 7,829
  • 13
  • 73
  • 134
  • 1
    `FF == 255` What's 50% of 255? – Shmiddty Nov 12 '12 at 17:11
  • Check this website, it has a javascript colour picker with a saturation slider, and also has a link to the source code: http://hslpicker.com/#9C639B – Kayo Nov 12 '12 at 17:13
  • That's great, but it makes heavy use of libraries. I'm trying to keep it as light as possible. – Dan Hanly Nov 12 '12 at 17:14
  • I've user a JS library called pixastic for dynamic desaturation of images using canvas before, maybe look into how the library does the calculations: http://www.pixastic.com – Kayo Nov 12 '12 at 17:18

5 Answers5

9

http://jsfiddle.net/5sfDQ/

$("#color, #saturation").change(function(){
    updateColor();
});

function updateColor(){
    var col = hexToRgb($("#color").val());
    var sat = Number($('#saturation').val())/100;
    var gray = col.r * 0.3086 + col.g * 0.6094 + col.b * 0.0820;

    col.r = Math.round(col.r * sat + gray * (1-sat));
    col.g = Math.round(col.g * sat + gray * (1-sat));
    col.b = Math.round(col.b * sat + gray * (1-sat));

    var out = rgbToHex(col.r,col.g,col.b);

    $('#output').val(out);

    $('body').css("background",out);
}

function componentToHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
}

function rgbToHex(r, g, b) {
    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}

function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}
Shmiddty
  • 13,847
  • 1
  • 35
  • 52
  • If you view this demo in Chrome, you even get a color picker and a slider out of it. – Shmiddty Nov 12 '12 at 17:52
  • 2
    I converted this not to use jQuery, so this works for me now: http://jsfiddle.net/danhanly/5sfDQ/4/ – Dan Hanly Nov 13 '12 at 09:30
  • Could you explain the use of the constants 0.3, 0.6, 0.08? How does this always produce a gray? – Hugheth Jun 13 '18 at 10:53
  • 1
    @Hugheth they may not produce a perfect gray for everyone. They are based on average human vision. More info here: https://en.m.wikipedia.org/wiki/Grayscale#Luma_coding_in_video_systems – Shmiddty Jun 13 '18 at 14:36
2
function applySat(sat, hex) {
    var hash = hex.substring(0, 1) === "#";

    hex = (hash ? hex.substring(1) : hex).split("");

    var long = hex.length > 3,
        rgb = [],
        i = 0,
        len = 3;

    rgb.push( hex.shift() + (long ? hex.shift() : "") );
    rgb.push( hex.shift() + (long ? hex.shift() : "") );
    rgb.push( hex.shift() + (long ? hex.shift() : "") );

    for( ; i < len; i++ ) {
        if ( !long ) {
            rgb[i] += rgb[i];
        }

        rgb[i] = Math.round( parseInt(rgb[i], 16)/100*sat).toString(16);

      rgb[i] += rgb[i].length === 1 ? rgb[i] : "";
    }

    return (hash ? "#" : "") + rgb.join("");
}

console.log(applySat(50, "#ff0000")); // "#7f0000";
console.log(applySat(50, "ff0000")); // "7f0000";
console.log(applySat(50, "#fed")); // "#7f776f"
console.log(applySat(50, "fed")); // "7f776f"
console.log(applySat(20, "#addfaa")); // "#232d22"
Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
  • 2
    This isn't desaturating, it's darkening. – Shmiddty Nov 12 '12 at 17:45
  • 1
    @Shmiddty hmm.. thought that was it ? . So it's the other way around? – Andreas Louv Nov 12 '12 at 17:46
  • 1
    You're bringing the colors to black. You should be bringing them to gray. There are a couple of different ways to do this. You can use 128 as your gray, or you can calculate a gray based on the original color (more useful when desaturating images). – Shmiddty Nov 12 '12 at 17:47
  • Your solution is simple, but unfortunately it doesn't achieve what I'm looking for. This isn't saturation, it's just turning it to black. It needs to go to grey. – Dan Hanly Nov 13 '12 at 08:57
1

If you really don't want to use a library, see mjijackson's RGB to HSL conversion page.

Copy the code to do RGB hex to HSL (or HSV) conversions. As the slider is moved, you'll need to use these to convert between the color models to get the saturation value, modify it, and then get the resulting rgb color back.

Note: HSL and HSV are standard color models. A few of the other answers are proposing definitions of "saturation" that do not correspond to these standard color models. Users will be confused as the alternate definitions will not give results consistent with what they'd expect from GIMP, Photoshop or other common graphics applications.

Ben
  • 494
  • 3
  • 9
1

Real simple and easy function, input your color, and add how much you want to desaturate it by. it does a pretty good job, but its not perfectly accurate.

    function addSaturation(color, amount){
        var color = color.replace('#', '').split('');
        var letters = '0123456789ABCDEF'.split('');
        for(var i = 0; i < color.length; i++){
            var newSaturation = 0;
            if(letters.indexOf(color[i]) + amount > 15) newSaturation = 15;
            else if(letters.indexOf(color[i]) + amount < 0) newSaturation = 0;
            else newSaturation = letters.indexOf(color[i]) + amount;
            color[i] = letters[newSaturation];
        }
        return "#" + color.join('');
    }

you can use positive or negative amounts as well.

Shadetheartist
  • 438
  • 3
  • 11
0

You could utilize the Javascript provided in this solution to match your needs

To change the saturation of an element, shift each of the three HEX values simultaneously, bringing the values closer to 128 (half of 256).

background-color: #FF0000; // rgb(255, 0, 0)

to this...

background-color: #BF4040; // rgb(191, 64, 64)
Community
  • 1
  • 1
RekindledPhoenix
  • 447
  • 8
  • 17