86

I have two colors:

#15293E
#012549

How can I find the color that is half way in between them? Is there some way to do this calculation?

Zoltan Toth
  • 46,981
  • 12
  • 120
  • 134
  • the jquery animate colors plugin does this, as does raphael js. I am sure the answer lies in the source of those. – Billy Moon Jan 23 '13 at 14:38
  • 12
    Sure, they're made up of 3 hex numbers - 15 29 3E and 01 25 49. Convert each of these numbers to decimal, average them and convert back to hex. Result: 0B2743 or 0B2744, however you want to round off. – Mr Lister Jan 23 '13 at 14:39
  • Yes, with JavaScript. But not with HTML or CSS. – David Thomas Jan 23 '13 at 14:41
  • 4
    Typically you would want to get the midpoint in HSV space and not in RGB space. In practice for nearby colors it might not matter. I wouldn't be surprised if this is what is done by ColorBlender. – Dov Grobgeld Jan 23 '13 at 14:44

12 Answers12

110

As Mr Lister just said, it is easy to automate the calculation with any programming language :

  1. Separate your two colors into their 3 color numbers for Red, Green, Blue : (r1,g1,b1) and (r2,g2,b2).
    • For example #15293E, #012549 become ("15", "29", "3E"), ("01", "25", "49")

  2. Convert each color string into an integer, specifying explicitly that you are parsing a hexadecimal-based representation of a number.
    • For example ("15", "29", "3E") becomes (21, 41, 62)

  3. Calculate the average (r',g',b') = ( (r1+r2)/2, (g1+g2)/2, (b1+b2)/2 ).
    • For example ( (21+1)/2, (41+37)/2, (62+73)/2) = (11, 39, 67)

  4. Convert them again to strings , specifying explicitly that you are generating two-digit hexadecimal representations (pad with a zero when necessary).
    • For example (11, 39, 67) -> ("0B", "27", "43")

  5. Concatenate a hash character followed by the 3 strings
    • For example ("0B", "27", "43") -> "#0B2743"

Edit : Implementation is not "very easy" as I initially stated. I took the time to write the code in several languages on Programming-Idioms .

2540625
  • 11,022
  • 8
  • 52
  • 58
Deleplace
  • 6,812
  • 5
  • 28
  • 41
67

I use this website to do this task for me: ColorBlender.

The mid-color will be #0B2744.

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
25

With LESS

If you use the latest LESS CSS preprocessor then you'll notice there is a function (mix()) that does this:

mix(#15293E, #012549, 50%)

Outputs: #0b2744.

Andy Hoffman
  • 18,436
  • 4
  • 42
  • 61
ScottS
  • 71,703
  • 13
  • 126
  • 146
12

If you need to do this generically, and expect the middle colour to be visually accurate in more cases (i.e. the visual colour and tone of the mid point should "look right" to a human viewer), then as suggested above you may want to convert from RGB to HSV or HSL before calculating the mid point, and then convert back afterwards. This may differ significantly from averaging RGB values directly.

Here is some JavaScript code for the conversion to/from HSL that I found on a brief search, and that on a brief check appears to do the right thing:

github.com/mjackson/mjijackson.github.com/blob/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.txt

https://web.archive.org/web/20170919064926/https://github.com/mjackson/mjijackson.github.com/blob/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.txt

Just apply the rgbToHsl function to your two r,g,b colour vectors, average the two resulting vectors, and apply hslToRgb to that . . .

2540625
  • 11,022
  • 8
  • 52
  • 58
Neil Slater
  • 26,512
  • 6
  • 76
  • 94
8

Handy-Dandy Function

function padToTwo(numberString) {
    if (numberString.length < 2) {
        numberString = '0' + numberString;
    }
    return numberString;
}

function hexAverage() {
    var args = Array.prototype.slice.call(arguments);
    return args.reduce(function (previousValue, currentValue) {
        return currentValue
            .replace(/^#/, '')
            .match(/.{2}/g)
            .map(function (value, index) {
                return previousValue[index] + parseInt(value, 16);
            });
    }, [0, 0, 0])
    .reduce(function (previousValue, currentValue) {
        return previousValue + padToTwo(Math.floor(currentValue / args.length).toString(16));
    }, '#');
}

console.log(hexAverage('#111111', '#333333')); // => #222222
console.log(hexAverage('#111111', '#222222')); // => #191919
console.log(hexAverage('#111111', '#222222', '#333333')); // => #222222
console.log(hexAverage('#000483', '#004B39')); // => #00275e
Jackson
  • 9,188
  • 6
  • 52
  • 77
  • `hexAverage('#000483', '#004B39')` has this has result **"#0275e"** , not really working fine – axel Apr 10 '15 at 14:47
  • 1
    The function works nicely, but there's bug in the live demo: `#0088CE, #00B8CF` produces `#0a0ce` instead of `#00a0ce`. – Pekka May 18 '15 at 16:25
6

Like this:

function colourGradientor(p, rgb_beginning, rgb_end){

    var w = p * 2 - 1;


    var w1 = (w + 1) / 2.0;
    var w2 = 1 - w1;

    var rgb = [parseInt(rgb_beginning[0] * w1 + rgb_end[0] * w2),
        parseInt(rgb_beginning[1] * w1 + rgb_end[1] * w2),
            parseInt(rgb_beginning[2] * w1 + rgb_end[2] * w2)];
    return rgb;
};

where p is a value between 0 and 1 specifying how far through the gradient the colour should be and rgb_beginning is the from colour and rgb_end is the to colour. Both are [r,g,b] arrays so you'll have to convert from hex first. This is a simplified version of LESS's mix function which I think is from SASS. For the poster p would be 0.5

Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
Rested
  • 187
  • 2
  • 9
3

i just wrote a function that calculates colors between two colors so here it is in case somebody needs it, i think it's quite short and readable, it accepts two colors in hex strings, and the number of colors to calculate in between, returns the colors in an array of hex strings

i took the rgbToHex and hexToRgb functions from here

Hope this helps!

function rgbToHex(r, g, b) {
  return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}

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;
}

// returns an array of startColor, colors between according to steps, and endColor
function getRamp(startColor, endColor, steps) {
  var ramp = [];

  ramp.push(startColor);

  var startColorRgb = hexToRgb(startColor);
  var endColorRgb = hexToRgb(endColor);

  var rInc = Math.round((endColorRgb.r - startColorRgb.r) / (steps+1));
  var gInc = Math.round((endColorRgb.g - startColorRgb.g) / (steps+1));
  var bInc = Math.round((endColorRgb.b - startColorRgb.b) / (steps+1));

  for (var i = 0; i < steps; i++) {
    startColorRgb.r += rInc;
    startColorRgb.g += gInc;
    startColorRgb.b += bInc;

    ramp.push(rgbToHex(startColorRgb.r, startColorRgb.g, startColorRgb.b));
  }
  ramp.push(endColor);

  return ramp;
}
Dan Levin
  • 718
  • 7
  • 16
3

I found an npm module that does this: https://www.npmjs.com/package/color-between

Here's some example usage:

const colorBetween = require('color-between');

// rgb format
colorBetween('rgb(255, 255, 255)', 'rgb(0, 0, 0)', 0.5, 'rgb');
// output: 'rgb(128, 128, 128)'

// rgba format
colorBetween('rgba(255, 255, 255, .2)', 'rgba(0, 0, 0, .8)', 0.5, 'rgb');
// output: 'rgba(128, 128, 128, 0.5)

// hex format
colorBetween('#fff', '#000', 0.5, 'hex');
// output: '#808080'

// mixed format
colorBetween('#fff', 'rgb(0, 0, 0)', 0.5, 'hsl');
output: 'hsl(0, 0%, 50%)'
Eric Johnson
  • 1,084
  • 13
  • 24
2

Here's a Python version.

# -*- coding: utf-8 -*-
"""jcolor_split.py splits 2 hex color values, HEX_COLOR_1 and HEX_COLOR_2, 
    printing the color halfway between the two colors
Usage:     jcolor_split.py HEX_COLOR_1 HEX_COLOR_2
Example: ./jcolor_split.py 3E599C      2E4892
"""
import sys

def jcolor_split(hex_color_1, hex_color_2):

    print()
    print (hex_color_1)
    r1s = hex_color_1[0:2]
    g1s = hex_color_1[2:4]
    b1s = hex_color_1[4:6]
    print(r1s,g1s,b1s)

    print()
    print (hex_color_2)
    r2s = hex_color_2[0:2]
    g2s = hex_color_2[2:4]
    b2s = hex_color_2[4:6]
    print(r2s,g2s,b2s)

    # convert rgb's to ints
    r1 = int(r1s, 16); g1 = int(g1s, 16); b1 = int(b1s, 16);
    r2 = int(r2s, 16); g2 = int(g2s, 16); b2 = int(b2s, 16);
    print()
    print(r1,g1,b1)
    print(r2,g2,b2)
   
    # get the average
    ra = int(round(float(r1+r2)/2))
    ga = int(round(float(g1+g2)/2))
    ba = int(round(float(b1+b2)/2))

    print()
    print(format(ra, 'x')+ format(ga, 'x')+ format(ba, 'x') )

    
 
NUM_ARGS = 2
def main():
    args = sys.argv[1:]
    if len(args) != NUM_ARGS or "-h" in args or "--help" in args:
        print (__doc__)
        sys.exit(2)
    jcolor_split(args[0], args[1])
 
if __name__ == '__main__':
    main()


sample_run = '''
PS C:\1d\JoeCodeswellHomePage> ./jcolor_split.py 3E599C      2E4892

3E599C
3E 59 9C

2E4892
2E 48 92

62 89 156
46 72 146

365097
PS C:\1d\JoeCodeswellHomePage> 
'''

Dharman
  • 30,962
  • 25
  • 85
  • 135
1

If you so wished you could do it yourself with windows calculator.

  1. Open Windows Calculator > View > Programmer
  2. Choose the Hex option
  3. Enter the Hex value
  4. Switch to Dec and write down the value given
  5. Repeat for steps 2-4 for the second hex value
  6. Calculate the average by adding the two Dec numbers and dividing by 2
  7. Enter this value into calculator with the Dec option selected then switch to the hex option and voila

Example:

  • 15293E (HEX) = 1386814 (DEC)
  • 012549 (HEX) = 75081 (DEC)
  • Mid Point = (1386814+75081)/2
  • Mid Point = 730947 (DEC)
  • 730947 (DEC) = 0B2743 (HEX)
  • #0B2743

or use ColorBlender as mentioned above ;)

Dave Haigh
  • 4,369
  • 5
  • 34
  • 56
1

Here I leave the code that I use in my typescript app

function mixHexColors(color1: string, color2: string) {
    const valuesColor1 = color1.replace('#', '').match(/.{2}/g).map((value) =>
        parseInt(value, 16)
    )
    const valuesColor2 = color2.replace('#', '').match(/.{2}/g).map((value) =>
        parseInt(value, 16)
    )
    const mixedValues = valuesColor1.map((value, index) =>
        ((value + valuesColor2[index]) / 2).toString(16).padStart(2, '')
    )
    return `#${mixedValues.join('')}`
}
Lucas Vazquez
  • 1,456
  • 16
  • 20
1

I know this thread is about 10 years old, but this answer might be interesting for someone who is searching for a CSS-only alternative. Therefore you can just create a CSS gradient between two colors where you set an huge size, e.g. 10000vw and set the position to center:

background: linear-gradient(90deg, rgb(255,255,0) 0%, rgb(0,0,255) 100%);
background-size: 10000vw;
background-position: center;

Based on the percentage of the gradient positions you can also set a ratio to one direction of color.

Ramazan Gevrek
  • 151
  • 1
  • 6