0

I have looked around different sources:

However, none of these answers is what I am searching for.

What I am trying to do is to get a randomized color in range of 2 colors, let's say for example purple and pink. With purple being #6A0DAD and pink being #FFC0CB, I want to grab a color in the range of these 2 colors, but a random one. So I'd get for example #D982B5, which is magenta-pink.

I have no clue so far where to get started other than making a randomizer.

akuzminykh
  • 4,522
  • 4
  • 15
  • 36
King Reload
  • 2,780
  • 1
  • 17
  • 42
  • 3
    #checked none of the links# basically treat your colors as their were numbers, meaning convert your hex values to ints and then generate a randint inbetween these boundaries, finally convert your result back to hex; HOWEVER you may want to have a look into this: [HSB](https://learnui.design/blog/the-hsb-color-system-practicioners-primer.html) – gkhaos Jan 02 '21 at 23:42
  • 5
    Frame challenge: Color is a 3-dimensional space with axes Hue, Saturation and Value (aka Brightness or Lightness). There are other such models like R/G/B or L\*a\*b. What does it mean for one color to be "between" two others? – Jim Garrison Jan 02 '21 at 23:44
  • 1
    A few good pointers here: https://stackoverflow.com/q/22607043/829571 – assylias Jan 03 '21 at 00:22

3 Answers3

6

Tl;dr:

It's not that simple. Color is what you perceive when you see visible light. What makes a color are different wavelengths of light. To represent color by numbers, we use different color spaces, e.g. RGB, CMYK, YCbCr and so on. All these color spaces do simplifications to represent a physical (or biological) property.

When you say "color between", it could mean many things: A wavelength between two wavelengths. A hue that is between two other hues in a specific color space. A color that you "expect" to be between two colors by your experience and perception. A color that results from mixing two colors. To mix colors, a good approach is to use additive mixing, but in a color space. However, in terms of "a color between", this could be flawed, depending on what you want.

Here are some related posts that are worth reading:


The longer version:

The first thing that comes to mind is to simply take each channel in RGB of both colors and to generate a random value in-between the values of the corresponding channel: E.g. you take the colors #6A0DAD and #FFC0CB. The values for the red channels are 106 and 255. So the value of the red channel for the new color would be a number between 106 and 255. This naive approach has a problem: The result for the number between #6A0DAD, purple, and #FFC0CB, pink, could be #6AC0AD, which is petrol color. Petrol is definitely not a color that you would perceive as a color "between purple and pink".

The cause of it is that the RGB color space has the hue of a color represented by all three channels: it's the balance of the channels that makes up the hue. When we generate a random number within the corresponding range for each channel for the new color, the hue of the resulting color might be something completely different, because it might result from a completely different balance of the channels.

Another approach would be to change the color space we are working in to one that represents hue by a single channel. One such color space is HSL/HSV. What we do is: Convert both numbers to the HSB equivalent. Then generate random numbers for each channel like we did in RGB. Notice that the balance of the channels doesn't matter as the hue is represented by a single channel. Then take the result and convert it back to RGB.

Here a simple demonstration in Java:

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;

class Main {
    
    
    static int[] purple = {106, 13, 173};
    static int[] pink = {255, 192, 203};
    
    
    static float randomBetween(float a, float b) {
        float min = Math.min(a, b);
        float max = Math.max(a, b);
        return min + (max - min) * (float) Math.random();
    }
    
    
    static Color colorBetween(int[] a, int[] b) {
        float[] a_hsb = Color.RGBtoHSB(a[0], a[1], a[2], null);
        float[] b_hsb = Color.RGBtoHSB(b[0], b[1], b[2], null);
        float[] between_hsb = {
                randomBetween(a_hsb[0], b_hsb[0]),
                randomBetween(a_hsb[1], b_hsb[1]),
                randomBetween(a_hsb[2], b_hsb[2])
        };
        return new Color(
                Color.HSBtoRGB(
                        between_hsb[0],
                        between_hsb[1],
                        between_hsb[2]));
    }
    
    
    public static void main(String args[]) {
        Color purple_color = new Color(purple[0], purple[1], purple[2]);
        Color pink_color = new Color(pink[0], pink[1], pink[2]);
        Color between_color = colorBetween(purple, pink);
        
        JFrame frame = new JFrame();
        GridLayout layout = new GridLayout(2, 2);
        frame.setLayout(layout);
        
        JLabel purple_label = new JLabel();
        purple_label.setBackground(purple_color);
        purple_label.setOpaque(true);
        frame.add(purple_label);
        
        JLabel pink_label = new JLabel();
        pink_label.setBackground(pink_color);
        pink_label.setOpaque(true);
        frame.add(pink_label);
        
        JLabel between_label = new JLabel();
        between_label.setBackground(between_color);
        between_label.setOpaque(true);
        frame.add(between_label);
        
        frame.addMouseListener(new MouseAdapter() {  
            public void mouseClicked(MouseEvent e) {  
               between_label.setBackground(colorBetween(purple, pink));
            }  
        }); 
        
        frame.setSize(1000, 1000);
        frame.setVisible(true);
    }
}

Click on the frame to recreate the new color. In the upper left is the purple, in the upper right the pink and the result is bottom left.

a b

As you should see, the result is definitely a color that you would call at least "in the same color palette". But this has still an issue: We are also adjusting the brightness and saturation in HSB. The pink color that we have initially has a very low saturation but the purple a very high one. The issue is that the result could be the same pink but highly saturated, which looks very reddish. It doesn't "seem" right.

Another approach could be to come back to RGB and to respect the balance of the channels. And actually, this is nothing new. It's called blending, which is a common technique in rendering to display transparency.

Here the same demonstration but with an adjusted colorBetween method:

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;

class Main {
    
    
    static int[] purple = {106, 13, 173};
    static int[] pink = {255, 192, 203};
    
    
    static Color colorBetween(int[] a, int[] b) {
        double c_a = Math.random();
        double c_b = 1.0 - c_a;
        
        int[] blend = {
                (int) (c_a * a[0] + c_b * b[0]),
                (int) (c_a * a[1] + c_b * b[1]),
                (int) (c_a * a[2] + c_b * b[2]),
        };
        
        return new Color(blend[0], blend[1], blend[2]);
    }
    
    
    public static void main(String args[]) {
        Color purple_color = new Color(purple[0], purple[1], purple[2]);
        Color pink_color = new Color(pink[0], pink[1], pink[2]);
        Color between_color = colorBetween(purple, pink);
        
        JFrame frame = new JFrame();
        GridLayout layout = new GridLayout(2, 2);
        frame.setLayout(layout);
        
        JLabel purple_label = new JLabel();
        purple_label.setBackground(purple_color);
        purple_label.setOpaque(true);
        frame.add(purple_label);
        
        JLabel pink_label = new JLabel();
        pink_label.setBackground(pink_color);
        pink_label.setOpaque(true);
        frame.add(pink_label);
        
        JLabel between_label = new JLabel();
        between_label.setBackground(between_color);
        between_label.setOpaque(true);
        frame.add(between_label);
        
        frame.addMouseListener(new MouseAdapter() {  
            public void mouseClicked(MouseEvent e) {  
               between_label.setBackground(colorBetween(purple, pink));
            }  
        }); 
        
        frame.setSize(1000, 1000);
        frame.setVisible(true);
    }
}

c

Notice that the result is never a highly saturated pink anymore that "seems wrong". What you do is simply using a random factor c_a that is between 0.0 and 1.0 and a factor c_b that is 1.0 - c_a. You multiply each channel of color a by c_a, do the same with b and c_b and add the results. This is simple scaling of a color by a constant factor, which has the effect of the balance of the channels being unchanged. After adding the results, you have a new color balance that is a blend of a and b.

Another method is a subtractive approach: Multiple each channel of color a by the corresponding channels of color b and divide by 255.

Or an another additive approach: Add the channels together and take it as the result if it is <= 255, otherwise take 255.

... and so on and so on ...

Notice that all these methods work relatively well when the initial two colors are close to another. It's getting difficult in some other cases: The colors red, (255, 0, 0), and cyan, (0, 255, 255), are complementary colors. An additive approach will results in white. A subtractive approach will result in black. For a color "in-between", let's look at hue (the HSB/HSL image from Wikipedia):

hue

You can see that the color red is on the left and on the right. The simplification made by the color space is now troublesome when we want to pick a hue in-between. Do we pick a color between 0° and 180° or do we pick a color between 180° and 360°? The first example that I've shown will pick a color from the lower half. You can see well why two colors that are close to another will yield a good result and colors that are far away will yield a bad result.

A perfect example for that: (255, 0, 0) and (255, 0, 50), which are red and pink, will result in any color: green, blue, yellow and so on. What you can do to solve it is to look at the distance and to add 360° if needed. E.g. color a with the hue 10° and color b with the hue 350° would become color a with 370° and b with still 350°. The randomly picked hue in this range modulo 360 is the hue of the new color.

My suggestion is to use the blending method, as it yields a result from an additive approach, which is closer to how visible light works, and does not increase the brightness unproportionally like some other methods would.

akuzminykh
  • 4,522
  • 4
  • 15
  • 36
  • The first solution where you used randomBetween and betweenColor worked like a charm! The saturation in the end didn't seem to be a problem :) – King Reload Jan 03 '21 at 14:48
3

As I commented, "between" is not well-defined when it comes to color.

However, one possible but probably useless approach is to take each component (R, G and B) and generate a random value "between" the values of the two colors.

Note that the result has a high probability of not being something you perceive as "between" unless the colors are relatively close to each other on an HSV color wheel. What is "between" 0xff0000 and 0x00ffff? I'd be very surprised if you could come with anything that any 3 people would agree is "between" Red and Cyan.

For your example

#6A0DAD
#FFC0CB

Generate a new color with the R component between 0x6A and 0xFF (106..255), the Green component between 0x0D and 0xC0 (13..192), and the Blue component between 0xAD and 0xCB (173..203).

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • I tried your answer as well, sadly its not the answer I'm looking for. Thanks for your contribution either way Jim :) – King Reload Jan 03 '21 at 00:02
  • If you can define what you're looking for better, maybe we can suggest a better solution. Please update your question and describe in detail what you mean by "between", and provide some examples. Look at a color wheel, pick some pairs of random colors, and decide what it means to be "between" those 2 colors. I think you will find that this is not generally possible. For example, the line on a color wheel between Red and Cyan goes right through the center (white) so any "between" color will be red or cyan of varying saturation levels. – Jim Garrison Jan 03 '21 at 00:05
  • This approach is terrible. A result could be `0x6AC0AD`, which is petrol color. This is definitely not what you expect from a color between pink and purple. And I think it's clear what [the OP asks for](https://en.wikipedia.org/wiki/Hue). – akuzminykh Jan 03 '21 at 00:06
  • @akuzminykh Go for it. Please define a color "between" `0xFF0000` and `0x00FFFF`. Explain how you arrived at your choice and how you'd code that in an algorithm. – Jim Garrison Jan 03 '21 at 00:08
  • I changed "between" to "in the range of", what I meant is get a random color pallet that is similar to the purple and pink colors. – King Reload Jan 03 '21 at 00:12
  • That's a big change to your question. You still have to specify what you want when someone gives you Red and Cyan as the starting colors. – Jim Garrison Jan 03 '21 at 00:15
  • 1
    This is turning more into a color theory problem than a programming problem, but perhaps you could calculate the difference between the two colors for each component and pick a random point in that range for all components. So for 5,5,5 and 0,6,10 we would get a difference of -5,1,5. Multiply that by a random number between 0 and 1 and add to the first color, e.g. random value of 0.5 -> add -2.5,0.5,2.5 to 5,5,5 = 2.5,5.5,7.5. – timsmelik Jan 03 '21 at 00:15
  • @JimGarrison so you're suggesting to hard-code the colors to respond with when someone sends 2 different colors? – King Reload Jan 03 '21 at 00:17
  • @timsmelik I will try it out :) – King Reload Jan 03 '21 at 00:17
  • 1
    @KingReload _"...hard-code the colors ..."_ -- not at all, just pointing out that no matter which algorithm you use there will be results that cannot be perceived as "between". Color just doesn't work that way, so your initial premise, that "between" is always meaningful, is false. – Jim Garrison Jan 03 '21 at 00:45
0

It requires a subjective definition of how hexadecimal colors should be sorted.

See this page for hexadecimal colors categorized as perceive them.

Basically, you cannot categorize the hexadecimals based on their numeric values, because numerically close hexadecimal numbers can be wildly different in color.

Just another episode where humans think differently than computers.

I created a fully functioning java program that fulfills your request. Would you like it attached here? I don't want to overwhelm you with unrequested code.

It can take input of either hexadecimal codes OR color names, and can return a pseudorandom hexadecimal code, a color name, or a renderable color that is between the colors as we perceive it (not, as akuzminykh aptly put it "petrol between purple and pink").

Cole Henrich
  • 155
  • 3
  • 17