Examining the Minecraft source reveals the exact algorithm for determining the color of potions. Here's the function that seems to do it:
public static int getPotionColor(Collection collection)
int waterColor = 0x385dc6;
if (collection == null || collection.isEmpty())
float r = 0.0F;
float g = 0.0F;
float b = 0.0F;
float numColors = 0.0F;
for (Iterator iterator = collection.iterator(); iterator.hasNext();)
PotionEffect potioneffect = (PotionEffect)iterator.next();
int potionColor = Potion.potionTypes[potioneffect.getPotionID()].getLiquidColor();
int i = 0;
while (i <= potioneffect.getAmplifier())
r += (float)(potionColor >> 16 & 0xff) / 255F;
g += (float)(potionColor >> 8 & 0xff) / 255F;
b += (float)(potionColor >> 0 & 0xff) / 255F;
r = (hBit / numColors) * 255F;
g = (mBit / numColors) * 255F;
b = (lBit / numColors) * 255F;
return (int)r << 16 | (int)g << 8 | (int)b;
This large, complex piece of code actually does something quite simple. It does basically just take all the different colors and average them together. Even so, it has a few extra quirks. First of all, this code checks to see if there are any potion effects to begin with. If not, it just uses the water color. I don't think that's ever used in the game, though.
It then creates a bunch of variables. Colors in Minecraft are stored in hexadecimal RGB format, so it creates a variable for red, green, and blue. It then loops through all the potions that are currently active. It then gets the amplifier, or potion level, and weights each color based on its level. Higher level potions have more influence on the resulting color.
Next is a bunch of bitwise math to average all the values, and in the end, all the values are divided by the total (to actually get an average) and spliced back into a single number, which is then returned as the particle color.