How to fade color

There are a bunch of ways to do this. How you choose to do it will depend on whether you value speed and simplicity or perceptual uniformity. If you need it to be truly uniform you will need to define you RGB colors with a color profile and you'll need the primaries of the profile so you can convert to XYZ and then to LAB where you can manipulate the L channel.

Most of the time you don't need to do that and you can instead use a simple HSB model like Photoshop does in the info palette.

To do this you simply imagine a line between your RGB point and the white point in 3D space and move your color along that line. In practical terms you can just create a parametric equation for that line and move the parameter.

import numpy as np

def lighter(color, percent):
    '''assumes color is rgb between (0, 0, 0) and (255, 255, 255)'''
    color = np.array(color)
    white = np.array([255, 255, 255])
    vector = white-color
    return color + vector * percent

A percentage of 0.0 will return the same color and 1.0 will return white. Everything between will be a lighter shade of the same hue. This should give you results that agree with Photoshop's HSB implementation, but will be device dependent and may not be perfectly uniform.

If you have RGB [200, 100, 50] and put in a percentage of .50 it should return RGB[ 227.5 177.5 152.5] Photoshop reports both as a hue of 20º.

It is not hard to do this without numpy, but the element wise operations are convenient.

Edit based on comment:

I'm not suggesting you do this unless you know you really need to do it the hard way. But if you want to convert to LAB you can without too much trouble. The most important thing is that you need to know what color space your RGB numbers are in to begin with or you need to make some assumptions about their meaning. Since sRGB is pretty standard on the web, I'll assume that here.

The conversions aren't that difficult, but it's easy to make mistakes. Happily, there's a pretty nice colormath module with good documentation: https://github.com/gtaylor/python-colormath

Using that you can convert between sRGB and LAB like this:

from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color

sRGB = sRGBColor(126, 126, 126, is_upscaled=True) # or between [0, 1] with out is_upscaled
lab =  convert_color(sRGB, LabColor)

lab now is a color with a Luminance channel lab.lab_l which you can move up or down between black(0) and white(100). This should be more perceptually uniform than HSB (but, depending on your application, maybe not enough to warrant the work).

You can simply change lab_l and then convert back:

lab.lab_l = 80
new_sRGB = convert_color(lab, color_objects.sRGBColor).get_upscaled_value_tuple()

new_sRGB is now [198, 198, 198]. colormath took care of the illuminant and gamma issues for you.


Simply linearly interpolate between your color and white:

def lerp(a, b, t):
    return a*(1 - t) + b*t

import numpy as np
white = np.array([255, 255, 255])
my_color = np.array([...])
lightened25 = lerp(my_color, white, 0.25)

Or without numpy:

lightened25 = [lerp(c, w, 0.25) for c, w in zip(my_color, white)]

I prefer to use HSV color mode.

To grayer your color you have to decrease Saturation factor.

Standard colorsys module can help in RGB <-> HSV conversions, but please keep in mind: colorsys operates with channel values in range [0, 1), not [0, 256).

There is full code example:

>>> from colorsys import hsv_to_rgb, rgb_to_hsv
>>> color = (200, 120, 40)
>>> normalized_color = (color[0]/256., color[1]/256., color[2]/256.)
>>> normalized_color
(0.78125, 0.46875, 0.15625)
>>> hsv_color = rgb_to_hsv(*normalized_color)
>>> hsv_color
(0.08333333333333333, 0.8, 0.78125)
>>> grayed_hsv_color = (hsv_color[0], 0.6, hsv_color[2])
>>> grayed_rgb_color = hsv_to_rgb(*grayed_hsv_color)
>>> grayed_rgb_color
(0.78125, 0.546875, 0.3125)
>>> denormalized_rgb_color = (int(grayed_rgb_color[0]*256), int(grayed_rgb_color[1]*256), int(grayed_rgb_color[2]*256))
>>> denormalized_rgb_color
(200, 140, 80)