35

I want to be able to take two RGB-256 vectors and calculate the result of their mixture. Also I want to be able to give each vector a different weight. I've experimented with it using the Word color plate, and I've seen that while some colors do mix according to a weighted average:

0.5*red(255,0,0) + 0.5*yellow(255,255,0) = orange(255,127,0)

others don't:

0.5*yellow(255,255,0) + 0.5*blue(0,0,255) = gray (127,127,127) and not green (0,255,0)

Is there an algorithm for accurate calculation for all colors or am I forced to do it using a look up table?

Cody Gray
  • 222,280
  • 47
  • 466
  • 543
SIMEL
  • 8,246
  • 23
  • 76
  • 115

12 Answers12

33

The best explanation is that the RGB color model is somewhat unintuitive for us humans.
It's perfectly logical for a video display device (such as a monitor or television) that only knows how to display colors by mixing varying amounts of 3 pre-defined colors: red, green, and blue. But that's not how you and I learned to mix colors in grade school.

In RGB, the color white is represented as (255, 255, 255), which equates to "all on." The full value for each of the red, green, and blue color components is displayed, which creates such a high light intensity that we perceive the color as white. Conversely, the color black (0, 0, 0) is the "default" state of the display device—when no color light is displayed ("0"), the result is black, or the absence of color.

When you fully mix yellow (255, 255, 0) and blue (0, 0, 255), you get the color represented by (255, 255, 255), or white. When you multiply white by 0.5, you get a gray color, because 255 * 0.5 = 127.

It's probably more of an anomaly than anything that you get the expected result when mixing red and yellow. Red + yellow is (510, 255, 0), so when you multiply that by 0.5, you get orange (255, 127, 0).

It turns out that what you and I learned in grade school is really more accurately known as a subtractive color model, versus the additive color model used by RGB. Looking at the two diagrams at the bottom, you'll see the RGB color model (additive) on the left, versus the CMYK color model (subtractive) on the right. You should be able to immediately see the problem. Using a subtractive color model instead will produce the results that you want.

         RGB Color Model                                 CMYK Color Model

EDIT: Of course, that's easier said than done. That is, even if you adopt the CMYK color model instead of RGB (which is already difficult, because conversions from RGB to CMYK are heavily device-dependent and far from straightforward), this will probably still not satisfy the urge to mix pure blue (0, 0, 255) with pure yellow (255, 255, 0) and get green. CMYK uses cyan, magenta, and yellow as the 3 primary colors, instead of red, yellow, and blue. And you have a lot of work ahead of you if you want to go about implementing the RYB color model in the language of your choice.

I don't know of any such algorithm to combine RGB colors realistically. I was attempting in my answer to explain why it would be impossible, or extremely difficult at the very least. You could leave this question open a few more days and see if anyone else has any ideas, but I'd go ahead and start on that look-up table if you need to stick with the RGB model. ;-)

Cody Gray
  • 222,280
  • 47
  • 466
  • 543
  • 1
    I'm implementing it in hardware, and I don't want to change the color model, since my output and input are in the RGB model. Is there some Algorithm that knows to combine RGB colors realistically or should I use a look up table that deals with all anomalies? – SIMEL Nov 23 '10 at 13:15
  • 1
    @Ilya: I don't know of any such algorithm. I was attempting in my answer to explain why it would be impossible, or *extremely* difficult at the very least. You could leave this question open a few more days and see if anyone else has any ideas, but I'd go ahead and start on that look-up table if you need to stick with the RGB model. ;-) – Cody Gray Nov 23 '10 at 13:22
  • 1
    @cody-gray @ilya-melamed `I don't know of any such algorithm to combine RGB colors realistically.` I once investigated about this problem, and found this paper: [Modeling Pigmented Materials for Realistic Image Synthesis](http://www.heathershrewsbury.com/dreu2010/wp-content/uploads/2010/07/ModelingPigmentedMaterialsForRealisticImageSynthesis.pdf). I didn't read it though... – wip Apr 08 '14 at 07:45
  • 1
    Indeed @wip. It seems that Kubelka-Munk theory is the only decent theoretical framework for solving the paint mixing issue. – Davor Josipovic Sep 30 '20 at 18:16
  • Relatedly I recently stumbled upon [this ShaderToy implementation](https://www.shadertoy.com/view/Ws3cD7) of [2015 paper "RYB Color Compositing"](http://nishitalab.org/user/UEI/publication/Sugita_IWAIT2015.pdf). – wip Oct 12 '20 at 04:39
4

Based on this documented response and this alghoritm aware response, I have tryed a simple interface to mix colors using additive and substractive aproach.

You must confirm that primary colors of RGB and CMYK gives you the secondary colors in the first response diagrams:

  • Red + Blue = Magenta (on additive)
  • Yellow + Cyan = Green (on substractive)
  • and so on ...
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.CompoundBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Hashtable;
import java.util.Vector;

/**
 * Color Mixing alghoritms
 * User: alberto
 * Date: 29/01/13
 * Time: 21:28
 */
public class ColorMix {

    Vector<JLabel> firstMixColors;
    Vector<JLabel> secondMixColors;
    JComboBox/*<Mixer>*/ comboBox;
    JLabel firstMixColor;
    JLabel firstSel;
    JLabel secondSel;
    JLabel finalColor;

    public ColorMix() {
        firstMixColors = new Vector<JLabel>();
        Vector<Mixer> mixers = new Vector<Mixer>();
        mixers.add(new AdditiveMixer());
        mixers.add(new SustractiveMixer());
        mixers.add(new TertiaryMixer());
        mixers.add(new DilutingSustractiveMixer());

        comboBox = new JComboBox(new DefaultComboBoxModel(mixers));
        firstMixColor = buildColorLabel();
        firstSel = buildColorLabel();
        secondSel = buildColorLabel();
        secondMixColors = new Vector<JLabel>();
        secondMixColors.add(firstSel);
        secondMixColors.add(secondSel);
        finalColor = buildColorLabel();
        comboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                calculateMixes();
            }
        });
        buildGUI();
    }

    private JLabel buildColorLabel() {
        JLabel label = new JLabel();
        label.setOpaque(true);
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setHorizontalTextPosition(SwingConstants.CENTER);
        label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        label.setPreferredSize(new Dimension(100,25));
        return label;
    }

    public void buildGUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setTitle("Mixing colors");

        frame.setLayout(new GridBagLayout());
        GridBagConstraints cc = new GridBagConstraints();
        cc.fill = GridBagConstraints.BOTH;
        cc.insets = new Insets(5, 5, 5, 5);
        cc.weightx = .2;
        cc.weighty = 1;
        frame.getContentPane().add(buildColorPanel(0), cc);
        frame.getContentPane().add(buildColorPanel(1), cc);
        cc.gridy = 1;
        JPanel firstMix = new JPanel(new GridBagLayout());
        GridBagConstraints ccCol = new GridBagConstraints();
        ccCol.fill = GridBagConstraints.BOTH;
        ccCol.insets = new Insets(5, 5, 5, 5);
        ccCol.weightx = 1;
        ccCol.weighty = 1;

        ccCol.gridx = 0;
        ccCol.gridy = 0;
        ccCol.gridheight = 2;
        firstMix.add(firstMixColor, ccCol);
        ccCol.fill = GridBagConstraints.HORIZONTAL;
        ccCol.weightx = 0.2;
        ccCol.weighty = 0.5;
        ccCol.gridx = 1;
        ccCol.gridy = 0;
        ccCol.gridheight = 1;
        ccCol.gridwidth = 1;
        firstMix.add(new JButton(new AbstractAction("Set First") {
            @Override
            public void actionPerformed(ActionEvent e) {
                setBackgroundToLabel(firstSel, firstMixColor.getBackground());
                calculateMixes();
            }
        }), ccCol);
        ccCol.gridx = 1;
        ccCol.gridy = 1;
        firstMix.add(new JButton(new AbstractAction("Set Second") {
            @Override
            public void actionPerformed(ActionEvent e) {
                setBackgroundToLabel(secondSel, firstMixColor.getBackground());
                calculateMixes();
            }
        }), ccCol);
        firstMix.setBorder(BorderFactory.createTitledBorder("Secondary Colors"));
        frame.getContentPane().add(firstMix, cc);
        cc.weightx = .6;

        JPanel panel = new JPanel(new GridBagLayout());
        GridBagConstraints ccColor = new GridBagConstraints();
        ccColor.fill = GridBagConstraints.BOTH;
        ccColor.insets = new Insets(5, 5, 5, 5);
        ccColor.weightx = 1;
        ccColor.weighty = 1;
        panel.add(firstSel, ccColor);
        ccColor.gridx = 1;
        panel.add(secondSel, ccColor);
        ccColor.gridx = 0;
        ccColor.gridy = 1;
        ccColor.weighty = 0;
        ccColor.gridwidth = 2;
        panel.add(finalColor, ccColor);
        ccColor.gridy = 2;
        panel.add(comboBox, ccColor);
        panel.setBorder(BorderFactory.createTitledBorder("Tertiary Colors"));
        frame.getContentPane().add(panel, cc);
        frame.pack();
        frame.setVisible(true);
    }


    public static void main(String[] args) {
        new ColorMix();
    }

    private JComponent buildColorPanel(int selectedIndex) {
        final JLabel pColor = buildColorLabel();
        firstMixColors.add(pColor);
        JPanel pSelectColor = new JPanel(new GridBagLayout());
        GridBagConstraints cc = new GridBagConstraints();
        cc.fill = GridBagConstraints.BOTH;
        cc.insets = new Insets(5, 5, 5, 5);
        cc.weightx = 1;
        cc.weighty = 1;
        final JSlider slidRed = buildSlider(pSelectColor, cc);
        final JSlider slidGreen = buildSlider(pSelectColor, cc);
        final JSlider slidBlue = buildSlider(pSelectColor, cc);
        pSelectColor.add(pColor, cc);
        final JComboBox comboColores = buildColorCombo();
        comboColores.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Color color = (Color) comboColores.getSelectedItem();
                slidRed.setValue(color.getRed());
                slidGreen.setValue(color.getGreen());
                slidBlue.setValue(color.getBlue());
            }
        });
        comboColores.setSelectedIndex(selectedIndex);
        cc.gridy = 1;
        cc.gridwidth = 4;
        cc.weighty = 0;
        pSelectColor.add(comboColores, cc);
        ChangeListener changeListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                setBackgroundToLabel(pColor, new Color(slidRed.getValue(), slidGreen.getValue(), slidBlue.getValue()));
                calculateMixes();
            }
        };
        slidRed.addChangeListener(changeListener);
        slidGreen.addChangeListener(changeListener);
        slidBlue.addChangeListener(changeListener);
        pSelectColor.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        changeListener.stateChanged(null);
        return pSelectColor;
    }

    private JComboBox buildColorCombo() {
        Color TRANSPARENT = new Color(0, 0, 0, 0);

        Vector<Color> colors = new Vector<Color>();

        colors.add(new NamedColor(Color.RED, "Red"));
        colors.add(new NamedColor(Color.GREEN, "Green"));
        colors.add(new NamedColor(Color.BLUE, "Blue"));

        colors.add(new NamedColor(Color.YELLOW, "Yellow"));
        colors.add(new NamedColor(Color.MAGENTA, "Magenta"));
        colors.add(new NamedColor(Color.CYAN, "Cyan"));

        colors.add(new NamedColor(Color.WHITE, "White"));
        colors.add(new NamedColor(Color.LIGHT_GRAY, "Light Gray"));
        colors.add(new NamedColor(Color.GRAY, "Gray"));
        colors.add(new NamedColor(Color.DARK_GRAY, "Dark Gray"));
        colors.add(new NamedColor(Color.BLACK, "Black"));
        colors.add(new NamedColor(Color.PINK, "Pink"));
        colors.add(new NamedColor(Color.ORANGE, "Orange"));

        colors.add(new NamedColor(TRANSPARENT, "transparent"));
        //http://www.w3schools.com/css/css_colornames.asp
        colors.add(new NamedColor(new Color(0xf0f8ff), "aliceblue"));
        colors.add(new NamedColor(new Color(0xfaebd7), "antiquewhite"));
        colors.add(new NamedColor(new Color(0x00ffff), "aqua"));
        colors.add(new NamedColor(new Color(0x7fffd4), "aquamarine"));
        colors.add(new NamedColor(new Color(0xf0ffff), "azure"));
        colors.add(new NamedColor(new Color(0xf5f5dc), "beige"));
        colors.add(new NamedColor(new Color(0xffe4c4), "bisque"));
        colors.add(new NamedColor(new Color(0x000000), "black"));
        colors.add(new NamedColor(new Color(0xffebcd), "blanchedalmond"));
        colors.add(new NamedColor(new Color(0x0000ff), "blue"));
        colors.add(new NamedColor(new Color(0x8a2be2), "blueviolet"));
        colors.add(new NamedColor(new Color(0xa52a2a), "brown"));
        colors.add(new NamedColor(new Color(0xdeb887), "burlywood"));
        colors.add(new NamedColor(new Color(0x5f9ea0), "cadetblue"));
        colors.add(new NamedColor(new Color(0x7fff00), "chartreuse"));
        colors.add(new NamedColor(new Color(0xd2691e), "chocolate"));
        colors.add(new NamedColor(new Color(0xff7f50), "coral"));
        colors.add(new NamedColor(new Color(0x6495ed), "cornflowerblue"));
        colors.add(new NamedColor(new Color(0xfff8dc), "cornsilk"));
        colors.add(new NamedColor(new Color(0xdc143c), "crimson"));
        colors.add(new NamedColor(new Color(0x00ffff), "cyan"));
        colors.add(new NamedColor(new Color(0x00008b), "darkblue"));
        colors.add(new NamedColor(new Color(0x008b8b), "darkcyan"));
        colors.add(new NamedColor(new Color(0xb8860b), "darkgoldenrod"));
        colors.add(new NamedColor(new Color(0xa9a9a9), "darkgray"));
        colors.add(new NamedColor(new Color(0xa9a9a9), "darkgrey"));
        colors.add(new NamedColor(new Color(0x006400), "darkgreen"));
        colors.add(new NamedColor(new Color(0xbdb76b), "darkkhaki"));
        colors.add(new NamedColor(new Color(0x8b008b), "darkmagenta"));
        colors.add(new NamedColor(new Color(0x556b2f), "darkolivegreen"));
        colors.add(new NamedColor(new Color(0xff8c00), "darkorange"));
        colors.add(new NamedColor(new Color(0x9932cc), "darkorchid"));
        colors.add(new NamedColor(new Color(0x8b0000), "darkred"));
        colors.add(new NamedColor(new Color(0xe9967a), "darksalmon"));
        colors.add(new NamedColor(new Color(0x8fbc8f), "darkseagreen"));
        colors.add(new NamedColor(new Color(0x483d8b), "darkslateblue"));
        colors.add(new NamedColor(new Color(0x2f4f4f), "darkslategray"));
        colors.add(new NamedColor(new Color(0x2f4f4f), "darkslategrey"));
        colors.add(new NamedColor(new Color(0x00ced1), "darkturquoise"));
        colors.add(new NamedColor(new Color(0x9400d3), "darkviolet"));
        colors.add(new NamedColor(new Color(0xff1493), "deeppink"));
        colors.add(new NamedColor(new Color(0x00bfff), "deepskyblue"));
        colors.add(new NamedColor(new Color(0x696969), "dimgray"));
        colors.add(new NamedColor(new Color(0x696969), "dimgrey"));
        colors.add(new NamedColor(new Color(0x1e90ff), "dodgerblue"));
        colors.add(new NamedColor(new Color(0xb22222), "firebrick"));
        colors.add(new NamedColor(new Color(0xfffaf0), "floralwhite"));
        colors.add(new NamedColor(new Color(0x228b22), "forestgreen"));
        colors.add(new NamedColor(new Color(0xff00ff), "fuchsia"));
        colors.add(new NamedColor(new Color(0xdcdcdc), "gainsboro"));
        colors.add(new NamedColor(new Color(0xf8f8ff), "ghostwhite"));
        colors.add(new NamedColor(new Color(0xffd700), "gold"));
        colors.add(new NamedColor(new Color(0xdaa520), "goldenrod"));
        colors.add(new NamedColor(new Color(0x808080), "gray"));
        colors.add(new NamedColor(new Color(0x808080), "grey"));
        colors.add(new NamedColor(new Color(0x008000), "green"));
        colors.add(new NamedColor(new Color(0xadff2f), "greenyellow"));
        colors.add(new NamedColor(new Color(0xf0fff0), "honeydew"));
        colors.add(new NamedColor(new Color(0xff69b4), "hotpink"));
        colors.add(new NamedColor(new Color(0xcd5c5c), "indianred"));
        colors.add(new NamedColor(new Color(0x4b0082), "indigo"));
        colors.add(new NamedColor(new Color(0xfffff0), "ivory"));
        colors.add(new NamedColor(new Color(0xf0e68c), "khaki"));
        colors.add(new NamedColor(new Color(0xe6e6fa), "lavender"));
        colors.add(new NamedColor(new Color(0xfff0f5), "lavenderblush"));
        colors.add(new NamedColor(new Color(0x7cfc00), "lawngreen"));
        colors.add(new NamedColor(new Color(0xfffacd), "lemonchiffon"));
        colors.add(new NamedColor(new Color(0xadd8e6), "lightblue"));
        colors.add(new NamedColor(new Color(0xf08080), "lightcoral"));
        colors.add(new NamedColor(new Color(0xe0ffff), "lightcyan"));
        colors.add(new NamedColor(new Color(0xfafad2), "lightgoldenrodyellow"));
        colors.add(new NamedColor(new Color(0xd3d3d3), "lightgray"));
        colors.add(new NamedColor(new Color(0xd3d3d3), "lightgrey"));
        colors.add(new NamedColor(new Color(0x90ee90), "lightgreen"));
        colors.add(new NamedColor(new Color(0xffb6c1), "lightpink"));
        colors.add(new NamedColor(new Color(0xffa07a), "lightsalmon"));
        colors.add(new NamedColor(new Color(0x20b2aa), "lightseagreen"));
        colors.add(new NamedColor(new Color(0x87cefa), "lightskyblue"));
        colors.add(new NamedColor(new Color(0x778899), "lightslategray"));
        colors.add(new NamedColor(new Color(0x778899), "lightslategrey"));
        colors.add(new NamedColor(new Color(0xb0c4de), "lightsteelblue"));
        colors.add(new NamedColor(new Color(0xffffe0), "lightyellow"));
        colors.add(new NamedColor(new Color(0x00ff00), "lime"));
        colors.add(new NamedColor(new Color(0x32cd32), "limegreen"));
        colors.add(new NamedColor(new Color(0xfaf0e6), "linen"));
        colors.add(new NamedColor(new Color(0xff00ff), "magenta"));
        colors.add(new NamedColor(new Color(0x800000), "maroon"));
        colors.add(new NamedColor(new Color(0x66cdaa), "mediumaquamarine"));
        colors.add(new NamedColor(new Color(0x0000cd), "mediumblue"));
        colors.add(new NamedColor(new Color(0xba55d3), "mediumorchid"));
        colors.add(new NamedColor(new Color(0x9370d8), "mediumpurple"));
        colors.add(new NamedColor(new Color(0x3cb371), "mediumseagreen"));
        colors.add(new NamedColor(new Color(0x7b68ee), "mediumslateblue"));
        colors.add(new NamedColor(new Color(0x00fa9a), "mediumspringgreen"));
        colors.add(new NamedColor(new Color(0x48d1cc), "mediumturquoise"));
        colors.add(new NamedColor(new Color(0xc71585), "mediumvioletred"));
        colors.add(new NamedColor(new Color(0x191970), "midnightblue"));
        colors.add(new NamedColor(new Color(0xf5fffa), "mintcream"));
        colors.add(new NamedColor(new Color(0xffe4e1), "mistyrose"));
        colors.add(new NamedColor(new Color(0xffe4b5), "moccasin"));
        colors.add(new NamedColor(new Color(0xffdead), "navajowhite"));
        colors.add(new NamedColor(new Color(0x000080), "navy"));
        colors.add(new NamedColor(new Color(0xfdf5e6), "oldlace"));
        colors.add(new NamedColor(new Color(0x808000), "olive"));
        colors.add(new NamedColor(new Color(0x6b8e23), "olivedrab"));
        colors.add(new NamedColor(new Color(0xffa500), "orange"));
        colors.add(new NamedColor(new Color(0xff4500), "orangered"));
        colors.add(new NamedColor(new Color(0xda70d6), "orchid"));
        colors.add(new NamedColor(new Color(0xeee8aa), "palegoldenrod"));
        colors.add(new NamedColor(new Color(0x98fb98), "palegreen"));
        colors.add(new NamedColor(new Color(0xafeeee), "paleturquoise"));
        colors.add(new NamedColor(new Color(0xd87093), "palevioletred"));
        colors.add(new NamedColor(new Color(0xffefd5), "papayawhip"));
        colors.add(new NamedColor(new Color(0xffdab9), "peachpuff"));
        colors.add(new NamedColor(new Color(0xcd853f), "peru"));
        colors.add(new NamedColor(new Color(0xffc0cb), "pink"));
        colors.add(new NamedColor(new Color(0xdda0dd), "plum"));
        colors.add(new NamedColor(new Color(0xb0e0e6), "powderblue"));
        colors.add(new NamedColor(new Color(0x800080), "purple"));
        colors.add(new NamedColor(new Color(0xff0000), "red"));
        colors.add(new NamedColor(new Color(0xbc8f8f), "rosybrown"));
        colors.add(new NamedColor(new Color(0x4169e1), "royalblue"));
        colors.add(new NamedColor(new Color(0x8b4513), "saddlebrown"));
        colors.add(new NamedColor(new Color(0xfa8072), "salmon"));
        colors.add(new NamedColor(new Color(0xf4a460), "sandybrown"));
        colors.add(new NamedColor(new Color(0x2e8b57), "seagreen"));
        colors.add(new NamedColor(new Color(0xfff5ee), "seashell"));
        colors.add(new NamedColor(new Color(0xa0522d), "sienna"));
        colors.add(new NamedColor(new Color(0xc0c0c0), "silver"));
        colors.add(new NamedColor(new Color(0x87ceeb), "skyblue"));
        colors.add(new NamedColor(new Color(0x6a5acd), "slateblue"));
        colors.add(new NamedColor(new Color(0x708090), "slategray"));
        colors.add(new NamedColor(new Color(0x708090), "slategrey"));
        colors.add(new NamedColor(new Color(0xfffafa), "snow"));
        colors.add(new NamedColor(new Color(0x00ff7f), "springgreen"));
        colors.add(new NamedColor(new Color(0x4682b4), "steelblue"));
        colors.add(new NamedColor(new Color(0xd2b48c), "tan"));
        colors.add(new NamedColor(new Color(0x008080), "teal"));
        colors.add(new NamedColor(new Color(0xd8bfd8), "thistle"));
        colors.add(new NamedColor(new Color(0xff6347), "tomato"));
        colors.add(new NamedColor(new Color(0x40e0d0), "turquoise"));
        colors.add(new NamedColor(new Color(0xee82ee), "violet"));
        colors.add(new NamedColor(new Color(0xf5deb3), "wheat"));
        colors.add(new NamedColor(new Color(0xffffff), "white"));
        colors.add(new NamedColor(new Color(0xf5f5f5), "whitesmoke"));
        colors.add(new NamedColor(new Color(0xffff00), "yellow"));
        colors.add(new NamedColor(new Color(0x9acd32), "yellowgreen"));

        JComboBox comboBox = new JComboBox(new DefaultComboBoxModel(colors));
        comboBox.setRenderer(new DefaultListCellRenderer() {
            protected Color backgroundColor = Color.BLACK;

            {
                setBorder(new CompoundBorder(
                        new MatteBorder(2, 5, 2, 5, Color.white)
                        , new LineBorder(Color.black)));
            }

            public Component getListCellRendererComponent(JList list, Object obj,
                                                          int row, boolean sel, boolean hasFocus) {
                if (obj instanceof Color)
                    backgroundColor = (Color) obj;
                setText(obj.toString());
                return this;
            }

            public void paint(Graphics g) {
                setBackground(backgroundColor);
                super.paint(g);
            }
        });


        return comboBox;
    }

    class NamedColor extends Color {
        private String name;

        NamedColor(Color color, String name) {
            super(color.getRed(), color.getGreen(), color.getBlue());
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    private void calculateMixes() {
        calculateFirstMix();
        calculateSecondMix();
    }

    private void calculateFirstMix() {
        calculateMix(firstMixColors, firstMixColor);
    }

    private void calculateSecondMix() {
        calculateMix(secondMixColors, finalColor);
    }

    private void calculateMix(Vector<JLabel> mixColors, JLabel finalColor) {
        Color bg = ((Mixer) comboBox.getSelectedItem()).calculateMix(mixColors);
        setBackgroundToLabel(finalColor, bg);
    }

    private void setBackgroundToLabel(JLabel label, Color color) {
        label.setBackground(color);
        label.setText(color.getRed() + "," + color.getGreen() + "," + color.getBlue());
    }

    interface Mixer {
        Color calculateMix(Vector<JLabel> colores);
    }

    /**
     * Implement a additive mix of colors
     */
    static class AdditiveMixer implements Mixer {
        public Color calculateMix(Vector<JLabel> colores) {
            int red = 0;
            int green = 0;
            int blue = 0;
            for (int i = 0; i < colores.size(); i++) {
                Color background = colores.get(i).getBackground();
                red += background.getRed();
                green += background.getGreen();
                blue += background.getBlue();
            }
            return new Color(Math.min(255, red), Math.min(255, green), Math.min(255, blue));
        }

        @Override
        public String toString() {
            return "Additive";
        }
    }

    /**
     * Implement a sustractive mix of colors
     */
    static class SustractiveMixer implements Mixer {
        public Color calculateMix(Vector<JLabel> colores) {
            int red = 1;
            int green = 1;
            int blue = 1;
            for (int i = 0; i < colores.size(); i++) {
                Color background = colores.get(i).getBackground();
                red *= background.getRed();
                green *= background.getGreen();
                blue *= background.getBlue();
            }
            return new Color(Math.min(255, red / 255), Math.min(255, green / 255), Math.min(255, blue / 255));
        }

        @Override
        public String toString() {
            return "Sustractive";
        }
    }

    /**
     * Implement a diluting/sustractive mix of colors
     */
    static class DilutingSustractiveMixer implements Mixer {
        public Color calculateMix(Vector<JLabel> colores) {
            int red = 0;
            int green = 0;
            int blue = 0;
            for (int i = 0; i < colores.size(); i++) {
                Color background = colores.get(i).getBackground();
                red += Math.pow(255 - background.getRed(), 2);
                green += Math.pow(255 - background.getGreen(), 2);
                blue += Math.pow(255 - background.getBlue(), 2);
            }
            return new Color(Math.min(255, (int)Math.sqrt(red / colores.size())), Math.min(255, (int)Math.sqrt(green / colores.size())), Math.min(255, (int)Math.sqrt(blue / colores.size())));
        }

        @Override
        public String toString() {
            return "Diluting/Sustractive";
        }
    }

    /**
     * Implement a diluting/sustractive mix of colors
     */
    static class TertiaryMixer implements Mixer {
        public Color calculateMix(Vector<JLabel> colores) {
            Color background1 = colores.get(0).getBackground();
            int red = background1.getRed();
            int green = background1.getGreen();
            int blue = background1.getBlue();
            Color background2 = colores.get(1).getBackground();
            red -= background2.getRed();
            green -= background2.getGreen();
            blue -= background2.getBlue();
            return new Color(Math.min(255, background1.getRed() - (red/2)), Math.min(255, background1.getGreen() - (green/2)), background1.getBlue() - (blue/2));
        }

        @Override
        public String toString() {
            return "Tertiary";
        }
    }

    private JSlider buildSlider(JPanel container, GridBagConstraints upperCC) {
        JPanel panel = new JPanel(new GridBagLayout());
        GridBagConstraints cc = new GridBagConstraints();
        cc.fill = GridBagConstraints.BOTH;
        cc.insets = new Insets(5, 5, 5, 5);
        cc.weightx = 1;
        cc.weighty = 0.7;

        final JSlider slider = new JSlider(JSlider.VERTICAL, 0, 255, 0);
        slider.setFont(new Font("Serif", Font.PLAIN, 4));

        Hashtable<Integer, JLabel> labels = new Hashtable<Integer, JLabel>();
        labels.put(0, new JLabel("0"));
        labels.put(128, new JLabel("128"));
        labels.put(255, new JLabel("255"));
        panel.add(slider, cc);
        final JTextField field = new JTextField();
        field.setEditable(false);
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                field.setText(String.valueOf(slider.getValue()));
            }
        });
        cc.gridx = 0;
        cc.gridy = 1;
        cc.weighty = 0;

        panel.add(field, cc);
        slider.setLabelTable(labels);
        slider.setPaintLabels(true);

        container.add(panel, upperCC);

        return slider;
    }
}

Color Mixing Interface

Basically, This operations are like logic AND and logic OR. (Well not exactly)

  • On additive alghoritm, the colors overlap, but for every primary it can only be 255 (Logic OR operation)
  • On Substractive alghoritm, if some primary is not present, the result will not have that (CYAN has no RED, YELLOW has no BLUE: you get GREEN) (Logic AND Operation)

Based on this you can obtain tertiary colors from mix of primary and secundary colors, so I make a tiny implementation which works pretty well:

NewColor.R = Color1.R - (Color1.R - Color2.R)/2
NewColor.G = Color1.G - (Color1.G - Color2.G)/2
NewColor.B = Color1.B - (Color1.B - Color2.B)/2

There is also an diluting/substractive alghoritm mentioned on second cited response, but I cannot say what It is based on, just added to test.

Community
  • 1
  • 1
albfan
  • 10,988
  • 2
  • 49
  • 74
  • 3
    Is all the code you posted necessary to give a good answer to the question? If not, please reduce to the max and if necessary post a link to an extending gist. – markus Jan 30 '13 at 12:21
  • Yes, I think image is enough if you want to try the code, I will do as soon as possible. – albfan Feb 01 '13 at 03:43
4

The REAL answer is to change RGB color vector space into one that's additive, then change it back to RGB. In this new vector space, when you add two light vectors it takes into account the additive properties of light and our perception of color to produce an additive color.

It turns out the CIE XYZ vector space works well for that purpose.

The XYZ vectors are additive in this space, and mix as light sources do.

See this paper on color mixing by Cree:

X_mix = X1 + X2 + ...

Y_mix = Y1 + Y2 + ...

Z_mix = Z1 + Z2 + ...

You can then change basis back to RGB. There are many libraries available for changing color between vector spaces and CIEXYZ is standardized and widely supported.

This method produces realistic results that suit most of my purposes.

More information about CIE 1931 color space.

nh43de
  • 731
  • 7
  • 11
2

Here is a Java implementation of the Kubelka-Munk Theory of Reflectance for mixing RGB colors. This implementation uses a simplified version of the Kubelka-Munk model that assumes all colors have the same concentration when blending and that all colors are opaque.

https://github.com/benjholla/ColorMixer

Ben Holland
  • 2,046
  • 4
  • 30
  • 49
  • I'm a big fan of your project. But still as others pointed (red+yellow=red), the color mixing is not implemented properly yet. – Tom Pažourek Nov 11 '13 at 14:33
  • Hi, sorry to respond so slow. You are right it looks like my implementation has some issues, especially with reds. I'm not sure whats wrong with it or how to fix it unfortunately. I've put a note on the repo to warn others. – Ben Holland Dec 26 '13 at 15:55
2

I just wanted to point out why you are getting grey when you add Blue. It's because you are adding Blue, and not Cyan:

enter image description here

  • If you add Cyan to Yellow, you get Green
  • If you add Blue (i.e. cyan+magents) to Yellow, you get Grey.

Or, more mathematically:

Yellow + (Cyan          ) = Green  
Yellow + (Cyan + Magenta) = Gray
Yellow + (Blue)           = Gray

You are adding Blue, when you meant to be adding Cyan.

0.5*Yellow(255,255,0) + 0.5*Cyan(0,255,255) = VeryLightLimeGreen(128,255,128)
Ian Boyd
  • 220,884
  • 228
  • 805
  • 1,125
  • This happens if you work with `CMYK` color model, in `RYB` color model [blue + yellow is green](https://danielhaim.com/research/downloads/Computational%20RYB%20Color%20Model%20and%20its%20Applications.pdf). – ElChiniNet Apr 04 '21 at 11:21
2

Looking at Cody Gray's answer, I think I can suggest how to combine colors. Converting color wheel to RGB:

                cyan(0, 255, 255)
        blue(0, 0, 255) green(0, 255, 0)
magenta(255, 0, 255) red(255, 0, 0) yellow(255, 255, 0)

Without extra complications, colors can be combined like this: invert both colors, add them together and invert the result (ActionScript):

sum(0, 255, 255,   255, 0, 255, "cyan + magenta =");
sum(255, 0, 0,     0, 255, 0,   "red + green =");
sum(0, 0, 0,       0, 0, 0,     "black + black =");
sum(0, 0, 0,       255, 255, 255, "black + white =");

function sum(c1:int, c2:int, c3:int, b1:int, b2:int, b3:int, m:String):void {
    c1 = 255 - c1; c2 = 255 - c2; c3 = 255 - c3;
    b1 = 255 - b1; b2 = 255 - b2; b3 = 255 - b3;
    var d1:int = c1 + b1;
    var d2:int = c2 + b2;
    var d3:int = c3 + b3;
    d1 = 255 - d1; d2 = 255 - d2; d3 = 255 - d3;
    d1 = clamp(d1); d2 = clamp(d2); d3 = clamp(d3);
    trace(m, d1, d2, d3);
}

function clamp(value:int):int {
    if (value < 0) return 0;
    if (value > 255) return 255;
    return value;
}

Output:

cyan + magenta = 0 0 255
red + green = 0 0 0
black + black = 0 0 0
black + white = 0 0 0

See if that fit for you.

Edit: I'm not pretending this is physically correct, it's just an attempt to approximate. The idea of lookup table sounds crazy for me for two reasons: it depends on two arguments, so its size will be very large; and laws of nature are usually continuous with no or rare corner cases. If you can fill lookup table, you should know the algorithm - so just write a function for it.

alxx
  • 9,882
  • 4
  • 24
  • 40
  • I want it to be this simple, but I don't think it is. What am I doing wrong here? Assume I have blue `(0, 0, 255)` + yellow `(255, 255, 0)`. Invert both colors: `(255, 255, 0)` + `(0, 0, 255)`. Add them together: `(255, 255, 255)`. Invert the result: `(0, 0, 0`). I end up with **black**, instead of the expected result of green. – Cody Gray Nov 24 '10 at 02:02
  • (0, 0, 255) and (255, 255, 0) cannot give green - they have no common components. Think of it like this - two layers of paint obscuring white light, no common spectrum lines - result is black. In your color wheel (right) cyan is clearly (0, 255, 255), not (0, 0, 255). – alxx Nov 24 '10 at 11:43
  • 1
    Cyan + yellow will give green, not blue + yellow. – alxx Nov 24 '10 at 11:48
  • 1
    Sure, I agree that's how it works. My point is just that your algorithm still doesn't satisfy the original request to mix colors as you would paint in an art class. I wouldn't try mixing cyan and yellow to get green. – Cody Gray Nov 24 '10 at 13:01
  • @alxx - Would this algorithm work with 4 color mix: A+B(=X), C+D(=Y) and then those results together X+Y to get final color? Or with 3 color mix: A+B(=X) and then C+X? – Ωmega Jun 17 '12 at 12:53
  • If three or four colors have common components, why not... otherwise results will get close to black with each iteration. – alxx Jun 18 '12 at 06:38
1

I don't think the answers above give adequate mixing results.

I've been working on this problem with both RGB and RYB (after converting from RGB). The conversion for RGB to RYB here is good: http://www.insanit.net/tag/rgb-to-ryb/ (I will share my code upon request).

If mixing as light, it's not too bad (see below). If you want to mix as physical materials such as paint it's a little trickier - I'm working on an app to explore it.

Back to the original question - here's my code for RGB mixing. RgbColor is a custom class but I think this makes sense on its own:

-(RgbColor*)mixWith:(RgbColor *)aColor {
    int r1, g1, b1, r2, g2, b2, r3, g3, b3, m1, m2, w1, w2, w3; //colors and maxes, white
    float br; // brightness of resulting color

r1 = self.redVal;
g1 = self.greenVal;
b1 = self.blueVal;
r2 = aColor.redVal;
g2 = aColor.greenVal;
b2 = aColor.blueVal;

w1 = MIN(r1, MIN(g1, b1));
w2 = MIN(r2, MIN(g2, b2));

// remove white before mixing
r1 -= w1;
g1 -= w1;
b1 -= w1;
r2 -= w2;
g2 -= w2;
b2 -= w2;

m1 = MAX(r1, MAX(g1, b1));
m2 = MAX(r2, MAX(g2, b2));

br = (m1+m2)/(2*255.0);

r3 = (r1+r2)*br;
g3 = (g1+g2)*br;
b3 = (b1+b2)*br;

// average whiteness and add into final color
w3 = (w1+w2)/2;

r3 += w3;
g3 += w3;
b3 += w3;

[self setRedVal:[[NSNumber numberWithFloat:r3] intValue]];
[self setGreenVal:[[NSNumber numberWithFloat:g3] intValue]];
[self setBlueVal:[[NSNumber numberWithFloat:b3] intValue]];
return self;
}
Damien Del Russo
  • 1,030
  • 8
  • 19
  • Mixing `127,255,0` and `0,255,0` I got `127,510,0` - what is wrong? – Ωmega Jun 17 '12 at 13:51
  • The color space only goes up to 255, so your 510 isn't really registering. Since originally posting this I have actually shared all my color mixing code - please have a look & enjoy. https://github.com/ddelruss/UIColor-Mixing – Damien Del Russo Jun 18 '12 at 15:27
  • By the way, where the code above mixes 2 colors, the code in my github project mixes n colors. The sample project app mixes combinations of 4 colors, and my app store app QColor mixes as many as you want. Good luck. – Damien Del Russo Jun 18 '12 at 15:34
  • Looking at github... how can `c1 = c1Total/sqrt((sqrt(count)));` work, when I will mix 4 colors or red (all 4 same 255,0,0), then your result will be `1020/sqrt((sqrt(4)))` which is `721.2489168102785`, way above 255..!? – Ωmega Jun 21 '12 at 16:47
  • Any color above the max is set back to the max, so yeah you can't have higher than 255 (or 1.0 if using regularized numbers). This happens often with colors that only have 1 component, but try mixing something like 155 red with 155 red, and look at the differences between RGB, RYB, and CMYK, and you start to see the subtlety of the mixing. The reason for using square roots is to allow mixing of many more colors without going straight to max values - on the github project, tap the corners to change those colors, which then changes the overlapping rectangles. Enjoy. – Damien Del Russo Jun 23 '12 at 15:42
  • Program at http://www.insanit.net/tag/rgb-to-ryb/ gives for 0xfff100 color (yellow) complement 0xf100ff (magenta/fuchsia), but in "The Non-Designers Design Book" for the same color complement is 0x5c2c91 (purple). So that program is not suitable for design. – DSblizzard Sep 10 '12 at 10:29
  • @Damien Del Russo: Hello. Your site seems to be down. I am very interesed in you rgb-to-ryb code. I think I saw a color mixer on your site a few days ago. It seemed to do exactly what I'm looking for. Could I get in contact with you somehow? – Christoffer Oct 12 '13 at 21:05
  • My code is still on github - though I don't know how good it compares now that iOS has new apis and all that. https://github.com/ddelruss/UIColor-Mixing – Damien Del Russo Oct 25 '13 at 16:29
0

Having the same issue with mixing colors in either a dark or ligth theme in my app, I was looking for a quick and easy solution.

In dark theme, I simply or'd the RGB colors and the result was pretty ok, however in light theme this resulted in very light colors becoming invisible.

I simply tried and'd colors and that worked pretty well. blue + yellow gave me green, magenta + cyan gave me blue at least.

So, in dark theme (back background) I do:

mixed_color = color1 | color2; // Dark theme, like RGB mixing

In light theme:

mixed_color = color1 & color2; // Light theme, like CMY mixing

There's unlikely any realism in the mix, but for my need (far away from photography software) that was highly satisfactory.

3c71
  • 3,574
  • 21
  • 42
0

A lot has already been said on the matter, but I just wanted to share a way of mixing colors (considering working with light: addition scheme).

I tried the example code from Damien Del Russo which seemed a nice approach in my own taste using white average to calculate rgb mix. I wanted to compare the results against my own (basic & linear) code.

Former code unfortunately can return values which exceeds 255 in some cases... Yet in these cases, the result is the same for me if a ratio is applied to get back to 0-255 range.

For other cases (mostly with a white component), I can see some differences in the results. The difference is more about luminosity than chromaticity. If I try to scale down the values I obtain, I get very close to the former code results...

I'm not sure yet which way of calculating it gives the closer results in terms of luminosity, but if I get an answer, I'll update this post to share the results.

Knowing that, here's my unfinished code (in python, as it's my principles test bench) to mix n tuples of rgb colors:

def rgb_mix_colors(rgb_scale, *colors):
    """ color mix
    :param rgb_scale: scale of values
    :param colors: list of colors (tuple of rgb values)
    :return: relative mix of rgb colors """
    r = g = b = 0

    for item in colors:
        try:
            if not isinstance(item, tuple):
                raise TypeError
            if item[0] > rgb_scale or item[1] > rgb_scale or item[2] > rgb_scale:
                raise ValueError
        except (TypeError, ValueError):
            print "WARNING: Value is outside range or unhandled parameter given as function argument!"
        else:
            r += item[0]    # add red value from item
            g += item[1]    # add green value from item
            b += item[2]    # add blue value from item

    ratio = max(r, g, b)
    if ratio > rgb_scale:
        ratio = float(rgb_scale) / ratio
        r *= ratio
        g *= ratio
        b *= ratio

    return int(r), int(g), int(b)


if __name__ == "__main__":
    col_list = [(512, 10, 256),
                (30, 120, 50),
                (50, 40, 512),
                "exception",        # should raise TypeError when mixing
                (3800, 20, 50),     # should raise ValueError when mixing
                (512, 10, 512)]

    # example with a scale defined at 1024 instead of default, providing list of tuples as params already packed as list
    print "2 warning messages should be displayed on the next line:"
    print rgb_mix_colors(1024, *col_list)
    print rgb_mix_colors(255, (0, 255, 0), (0, 32, 255))

SMFSW
  • 286
  • 1
  • 2
  • 10
0

Mixing yellow (= red+green) and blue does give white color as per physics, see http://en.wikipedia.org/wiki/Additive_color .

user502515
  • 4,229
  • 22
  • 20
  • But I want something that will know that green+red=yellow, since yellow is a natural basic color and green is not. I want to imitate nature by being able to create the color green from a mixture of other colors. – SIMEL Nov 23 '10 at 12:47
  • Then use the substractive model. – user502515 Nov 23 '10 at 12:49
0

The "Yellow" in RGB model is not the same as the Yellow in RYB model which when mixed with Blue is supposed to give Green.

As an example: (255, 255, 0) is (approximately) twice as "intense" as (0, 0, 255) in RGB model, while in RYB model, EQUAL amounts of Yellow and Blue are supposed to give Green. Similarly, Red and Blue in the two models are different.

Think of them as vector spaces RGB and R'Y'B'.

If some kind of relation like:

R = i1*R' + j1*Y' + k1*B';
G = i2*R' + j2*Y' + k2*B';
B = i3*R' + j3*Y' + k3*B';

holds, you can do your algebra by first converting the individual colors (operands) from RGB to R'Y'B' space.

There are 9 unknowns (i,j,k variables), so you need 9 equations (3 color equalities in these 2 spaces).

Unfortunately, I think that the models don't scale linearly, and hence for precision you might have to use lookup tables.

Another good idea may be to convert to HSV or YCbCr space, because the color information is more clearly abstracted out in those space. (If an RGB to RYB conversion does exist, an RGB->YCbCr->RYB route might be easier to find).

  • Kye thing to note is:: RGB(255, 0, 0) may not be equal to RYB(255, 0, 0). RGB(255, 255, 0) may not be equal to RYB(0, 255, 0), etc. Perhaps the 2 sure points of equality are (0, 0, 0) and (255, 255, 255). Others necessary to solve the equations (which are valid ONLY IF the relation between the color spaces is approximately linear) perhaps need to be found by observation. –  Nov 23 '10 at 15:37
0

I needed to do the same for one of my libraries. I applied the suggested approach of this paper, and the result is pretty acceptable.

The process is pretty straightforward. It requires converting the colors to the RYB color space, makes the mix, and converting back the result to RGB. They propose a conversion equation in agreement with the next table:

RGB Color RGB values RYB values
Black R: 0 | G: 0 | B: 0 R: 255 | Y: 255 | B: 255
Red R: 255 | G: 0 | B: 0 R: 255 | Y: 0 | B: 0
Green R: 0 | G: 255 | B: 0 R: 0 | Y: 255 | B: 255
Blue R: 0 | G: 0 | B: 255 R: 0 | Y: 0 | B: 255
Yellow R: 255 | G: 255 | B: 0 R: 0 | Y: 255 | B: 0
Magenta R: 255 | G: 0 | B: 255 R: 255 | Y: 0 | B: 127.5
Cyan R: 0 | G: 255 | B: 255 R: 0 | Y: 127.5 | B: 255
White R: 255 | G: 255 | B: 255 R: 0 | Y: 0 | B: 0

Here are the proposed formulas (take the TypeScript code as pseudocode):

type RYBObject = {
    r: number;
    y: number;
    b: number;
};

export const rgbToRYB = (r: number, g: number, b: number): RYBObject => {
    const Iw = Math.min(r, g, b);
    const Ib = Math.min(255 - r, 255 - g, 255 - b);
    const rRGB = r - Iw;
    const gRGB = g - Iw;
    const bRGB = b - Iw;
    const minRG = Math.min(rRGB, gRGB);
    const rRYB = rRGB - minRG;
    const yRYB = (gRGB + minRG) / 2;
    const bRYB = (bRGB + gRGB - minRG) / 2;
    const n = Math.max(rRYB, yRYB, bRYB, 1) / Math.max(rRGB, gRGB, bRGB, 1);
    return {
        r: rRYB / n + Ib,
        y: yRYB / n + Ib,
        b: bRYB / n + Ib
    };
};

type RGBObject = {
    r: number;
    g: number;
    b: number;
};

export const rybToRGB = (r: number, y: number, b: number): RGBObject => {
    const Iw = Math.min(r, y, b);
    const Ib = Math.min(255 - r, 255 - y, 255 - b);
    const rRYB = r - Iw;
    const yRYB = y - Iw;
    const bRYB = b - Iw;
    const minYB = Math.min(yRYB, bRYB);
    const rRGB = rRYB + yRYB - minYB;
    const gRGB = yRYB + 2 * minYB;
    const bRGB = 2 * (bRYB - minYB);
    const n = Math.max(rRGB, gRGB, bRGB, 1) / Math.max(rRYB, yRYB, bRYB, 1);
    return {
        r: rRGB / n + Ib,
        g: gRGB / n + Ib,
        b: bRGB / n + Ib
    };
};

And you can use this method to mix the colors:

const mixColors = (c1: RYBObject, c2: RYBObject): RYBObject => ({
    r: Math.min(255, c1.r + c2.r),
    y: Math.min(255, c1.y + c2.y),
    b: Math.min(255, c1.b + c2.b)
});

// color1 and color2 are the input colors in the shape of RGBObject
const mixedColor = rybToRGB(
    mixColors(
        rgbToRYB(color1),
        rgbToRYB(color2)
    )
);

Here you can see the result using the library that I built which uses the above methods to achieve the mix:

const mixes = [
  '#FF0000',
  '#FFFF00',
  '#0000FF',
  [1, 3],
  [1, 2],
  [2, 3],
  [1, 2, 3]
];

const paths = document.querySelectorAll('#planes path');

paths.forEach((path, index) => {
    let color = '#CCCCCC';
    if (mixes[index]) {
      if (typeof mixes[index] === 'string') {
        color = mixes[index];
      } else {
        const colors = mixes[index].map((i) => mixes[i - 1]);
        color = mixes[index] = ColorTranslator.getMixHEX(colors, Mix.SUBTRACTIVE);           
      }
    }  
    path.setAttribute('fill', color);
});
body, html {
  height: 100%;
}

svg {
  height: 100%;
  width: 100%;
}
<script src="https://unpkg.com/colortranslator@1.7.0/dist/colortranslator.web.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
    <g id="planes">
        <path d="M250,210.9929A144.2657,144.2657,0,0,1,392.9611,192.67a144.125,144.125,0,1,0-287.0788-18.3437,145.5121,145.5121,0,0,0,1.1566,18.3437A144.2657,144.2657,0,0,1,250,210.9929Z"/>
        <path d="M409.4255,200.84a145.5235,145.5235,0,0,0-16.4644-8.17,144.2654,144.2654,0,0,1-87.3487,114.6464A144.2658,144.2658,0,0,1,250,440.2858,144.125,144.125,0,1,0,409.4255,200.84Z"/>
        <path d="M194.3876,307.3163A144.2654,144.2654,0,0,1,107.0389,192.67,144.1177,144.1177,0,1,0,250,440.2858,144.2658,144.2658,0,0,1,194.3876,307.3163Z"/>
        <path d="M194.3876,307.3163A144.5321,144.5321,0,0,1,250,210.9929,144.2657,144.2657,0,0,0,107.0389,192.67,144.2654,144.2654,0,0,0,194.3876,307.3163Z"/>
        <path d="M250,210.9929a144.5321,144.5321,0,0,1,55.6124,96.3234A144.2654,144.2654,0,0,0,392.9611,192.67,144.2657,144.2657,0,0,0,250,210.9929Z"/>
        <path d="M305.6124,307.3163a144.532,144.532,0,0,1-111.2248,0A144.2658,144.2658,0,0,0,250,440.2858,144.2658,144.2658,0,0,0,305.6124,307.3163Z"/>
        <path d="M250,210.9929a144.5321,144.5321,0,0,0-55.6124,96.3234,144.532,144.532,0,0,0,111.2248,0A144.5321,144.5321,0,0,0,250,210.9929Z"/>
    </g>
    <g id="letters">
        <path d="M249.5908,115.1084h-.04l-2.2608,1.22-.34-1.34,2.8408-1.52h1.5v13.0029h-1.7Z"/>
        <path d="M254.2119,265.5859v1.16l-5.6611,11.8428h-1.82l5.6406-11.503v-.04H246.01v-1.46Z"/>
        <path d="M377.5254,347.1016v-1.08l1.38-1.34c3.32-3.1611,4.8213-4.8418,4.8408-6.8017a2.32,2.32,0,0,0-2.58-2.541,4.427,4.427,0,0,0-2.7607,1.1006l-.5606-1.24a5.7088,5.7088,0,0,1,3.6807-1.32,3.6594,3.6594,0,0,1,3.9814,3.7813c0,2.4-1.74,4.3408-4.4814,6.9814l-1.04.96v.04h5.8417v1.46Z"/>
        <path d="M109.436,345.08a5.88,5.88,0,0,0,2.88.82c2.2607,0,2.9609-1.44,2.9409-2.5195-.02-1.82-1.6606-2.6016-3.3608-2.6016h-.98v-1.32h.98c1.28,0,2.9009-.66,2.9009-2.2,0-1.04-.66-1.96-2.2808-1.96a4.7821,4.7821,0,0,0-2.6006.8594l-.46-1.2793a6.1819,6.1819,0,0,1,3.4008-1c2.5606,0,3.7207,1.5195,3.7207,3.1a3.2286,3.2286,0,0,1-2.4,3.0606v.041a3.3856,3.3856,0,0,1,2.9,3.34c0,2.0811-1.62,3.9014-4.7412,3.9014a6.6111,6.6111,0,0,1-3.38-.88Z"/>
        <path d="M169.7305,235.2827v-3.5405H163.69v-1.1607L169.49,222.28h1.9v8.082h1.8208v1.38h-1.8208v3.5405Zm0-4.9209v-4.3413q0-1.02.06-2.04h-.06c-.4.76-.72,1.32-1.08,1.92l-3.1807,4.4209v.04Z"/>
        <path d="M325.0127,223.76h-4.9609l-.5,3.3408a6.9313,6.9313,0,0,1,1.06-.08,5.4945,5.4945,0,0,1,2.8008.7,3.7056,3.7056,0,0,1,1.86,3.3408,4.511,4.511,0,0,1-4.8409,4.4409,6.3659,6.3659,0,0,1-3.24-.8l.44-1.34a5.8208,5.8208,0,0,0,2.7812.72,2.8686,2.8686,0,0,0,3.08-2.8208c-.02-1.68-1.14-2.88-3.74-2.88a13.4421,13.4421,0,0,0-1.8008.14l.84-6.2417h6.2217Z"/>
        <path d="M253.1924,362.9092a6.3162,6.3162,0,0,0-1.32.08,5.2845,5.2845,0,0,0-4.5215,4.6211h.06a3.9634,3.9634,0,0,1,7.0621,2.7,4.3479,4.3479,0,0,1-4.3213,4.5811c-2.7808,0-4.6011-2.16-4.6011-5.541a8.1752,8.1752,0,0,1,2.2-5.8613,7.08,7.08,0,0,1,4.1616-1.9209,8.557,8.557,0,0,1,1.28-.1Zm-.5,7.5019c0-1.86-1.0606-2.9814-2.6812-2.9814a2.8967,2.8967,0,0,0-2.52,1.6006,1.4976,1.4976,0,0,0-.2.78c.04,2.1407,1.02,3.7207,2.8609,3.7207C251.6719,373.5312,252.6924,372.2715,252.6924,370.4111Z"/>
     </g>
 </svg>

And in this CodePen you can check the differences between mixing the colors in additive mode vs subtractive mode.

ElChiniNet
  • 2,587
  • 2
  • 17
  • 25