Reading the hue of a color variable gives strange results


#1

I found out that when you set a hue value to a color object, you don’t get the same result back when you use the hue() function on that color. For example: the following code outputs the value 67.166664 instead of 67

colorMode(HSB); // same as colorMode(HSB, 255);
color c = color(67, 255, 255);
println(hue(c));

So I thought this had something to do with the fact that maybe the hue() function always returns values between 0 and 100, no matter what the second argument of the colorMode() function. But

colorMode(HSB, 100);
color c = color(67, 255, 255);
println(hue(c));

returns 66.99347, which is also a little bit off.

The return value of the hue() function also depends on the saturation and the brightness of the color. For example:

colorMode(HSB, 100);
color c = color(67, 10, 10);
println(hue(c));

returns 72.22223

Can someone explain these strange results? Because I want to read the hue values of a array of color variables, then increase the hue by 1 and do this over and over again in a loop. But the hue values never make neat steps of 1 because of the problem I descibed above.


#2

Fascinating :slight_smile:

This program gives a bit more of insight:

colorMode(HSB);
for(int i=0; i<256; i++) {
  float h = hue(color(i, 255, 255));
  println("hue", i, "becomes", h, "which is off by", h-i);
}

Which shows

hue 0 becomes 0.0 which is off by 0.0
hue 1 becomes 0.83333325 which is off by -0.16666675
hue 2 becomes 1.8333333 which is off by -0.16666675
hue 3 becomes 2.8333333 which is off by -0.16666675
hue 4 becomes 3.833333 which is off by -0.16666698
hue 5 becomes 4.833333 which is off by -0.16666698
hue 6 becomes 5.833333 which is off by -0.16666698
hue 7 becomes 6.833333 which is off by -0.16666698
hue 8 becomes 7.833333 which is off by -0.16666698
hue 9 becomes 8.833333 which is off by -0.16666698
hue 10 becomes 9.833333 which is off by -0.16666698
hue 11 becomes 10.833333 which is off by -0.16666698
hue 12 becomes 11.833333 which is off by -0.16666698
hue 13 becomes 12.833333 which is off by -0.16666698
hue 14 becomes 13.833332 which is off by -0.16666794
hue 15 becomes 14.833332 which is off by -0.16666794
hue 16 becomes 15.999999 which is off by -9.536743E-7
hue 17 becomes 16.999998 which is off by -1.9073486E-6
hue 18 becomes 17.999998 which is off by -1.9073486E-6
hue 19 becomes 18.999998 which is off by -1.9073486E-6
hue 20 becomes 19.999998 which is off by -1.9073486E-6
hue 21 becomes 20.999998 which is off by -1.9073486E-6
hue 22 becomes 21.999998 which is off by -1.9073486E-6
hue 23 becomes 22.999998 which is off by -1.9073486E-6
hue 24 becomes 23.999998 which is off by -1.9073486E-6
hue 25 becomes 24.999998 which is off by -1.9073486E-6
hue 26 becomes 25.999998 which is off by -1.9073486E-6
hue 27 becomes 26.999998 which is off by -1.9073486E-6
hue 28 becomes 27.999998 which is off by -1.9073486E-6
hue 29 becomes 28.999998 which is off by -1.9073486E-6
hue 30 becomes 29.999998 which is off by -1.9073486E-6
hue 31 becomes 30.999998 which is off by -1.9073486E-6
hue 32 becomes 32.0 which is off by 0.0
hue 33 becomes 33.0 which is off by 0.0
hue 34 becomes 34.0 which is off by 0.0
hue 35 becomes 35.0 which is off by 0.0
hue 36 becomes 36.0 which is off by 0.0
hue 37 becomes 37.0 which is off by 0.0
hue 38 becomes 38.0 which is off by 0.0
hue 39 becomes 39.0 which is off by 0.0
hue 40 becomes 40.0 which is off by 0.0
hue 41 becomes 41.0 which is off by 0.0
hue 42 becomes 42.0 which is off by 0.0
hue 43 becomes 43.166668 which is off by 0.16666794
hue 44 becomes 44.16667 which is off by 0.16667175
hue 45 becomes 45.166668 which is off by 0.16666794
hue 46 becomes 46.16667 which is off by 0.16667175
hue 47 becomes 47.166668 which is off by 0.16666794
hue 48 becomes 48.16667 which is off by 0.16667175
hue 49 becomes 49.166668 which is off by 0.16666794
hue 50 becomes 50.16667 which is off by 0.16667175
hue 51 becomes 51.166668 which is off by 0.16666794
hue 52 becomes 52.16667 which is off by 0.16667175
hue 53 becomes 53.166668 which is off by 0.16666794
hue 54 becomes 54.16667 which is off by 0.16667175
hue 55 becomes 55.166668 which is off by 0.16666794
hue 56 becomes 56.16667 which is off by 0.16667175
hue 57 becomes 57.166668 which is off by 0.16666794
hue 58 becomes 58.16667 which is off by 0.16667175
hue 59 becomes 59.166668 which is off by 0.16666794
hue 60 becomes 60.16667 which is off by 0.16667175
hue 61 becomes 61.166668 which is off by 0.16666794
hue 62 becomes 62.16667 which is off by 0.16667175
hue 63 becomes 63.166668 which is off by 0.16666794
hue 64 becomes 64.16667 which is off by 0.16667175
hue 65 becomes 65.166664 which is off by 0.16666412
hue 66 becomes 66.16667 which is off by 0.16667175
hue 67 becomes 67.166664 which is off by 0.16666412
hue 68 becomes 68.16667 which is off by 0.16667175
hue 69 becomes 69.166664 which is off by 0.16666412
hue 70 becomes 70.16667 which is off by 0.16667175
hue 71 becomes 71.166664 which is off by 0.16666412
hue 72 becomes 72.16667 which is off by 0.16667175
hue 73 becomes 73.166664 which is off by 0.16666412
hue 74 becomes 74.16667 which is off by 0.16667175
hue 75 becomes 75.166664 which is off by 0.16666412
hue 76 becomes 76.16667 which is off by 0.16667175
hue 77 becomes 77.166664 which is off by 0.16666412
hue 78 becomes 78.16667 which is off by 0.16667175
hue 79 becomes 79.166664 which is off by 0.16666412
hue 80 becomes 80.16667 which is off by 0.16667175
hue 81 becomes 81.166664 which is off by 0.16666412
hue 82 becomes 82.16667 which is off by 0.16667175
hue 83 becomes 83.166664 which is off by 0.16666412
hue 84 becomes 84.16667 which is off by 0.16667175
hue 85 becomes 85.0 which is off by 0.0
hue 86 becomes 86.0 which is off by 0.0
hue 87 becomes 86.833336 which is off by -0.16666412
hue 88 becomes 87.99999 which is off by -7.6293945E-6
hue 89 becomes 88.833336 which is off by -0.16666412
hue 90 becomes 90.0 which is off by 0.0
hue 91 becomes 90.833336 which is off by -0.16666412
hue 92 becomes 91.99999 which is off by -7.6293945E-6
hue 93 becomes 92.833336 which is off by -0.16666412
hue 94 becomes 94.0 which is off by 0.0
hue 95 becomes 94.833336 which is off by -0.16666412
hue 96 becomes 95.99999 which is off by -7.6293945E-6
hue 97 becomes 96.833336 which is off by -0.16666412
hue 98 becomes 98.0 which is off by 0.0
hue 99 becomes 98.833336 which is off by -0.16666412
hue 100 becomes 99.99999 which is off by -7.6293945E-6
hue 101 becomes 100.833336 which is off by -0.16666412
hue 102 becomes 102.0 which is off by 0.0
hue 103 becomes 102.833336 which is off by -0.16666412
hue 104 becomes 103.99999 which is off by -7.6293945E-6
hue 105 becomes 104.833336 which is off by -0.16666412
hue 106 becomes 106.0 which is off by 0.0
hue 107 becomes 106.833336 which is off by -0.16666412
hue 108 becomes 108.0 which is off by 0.0
hue 109 becomes 108.833336 which is off by -0.16666412
hue 110 becomes 110.0 which is off by 0.0
hue 111 becomes 110.833336 which is off by -0.16666412
hue 112 becomes 112.0 which is off by 0.0
hue 113 becomes 112.833336 which is off by -0.16666412
hue 114 becomes 114.0 which is off by 0.0
hue 115 becomes 114.833336 which is off by -0.16666412
hue 116 becomes 116.0 which is off by 0.0
hue 117 becomes 116.833336 which is off by -0.16666412
hue 118 becomes 118.0 which is off by 0.0
hue 119 becomes 118.833336 which is off by -0.16666412
hue 120 becomes 120.0 which is off by 0.0
hue 121 becomes 120.833336 which is off by -0.16666412
hue 122 becomes 122.0 which is off by 0.0
hue 123 becomes 122.833336 which is off by -0.16666412
hue 124 becomes 124.0 which is off by 0.0
hue 125 becomes 124.833336 which is off by -0.16666412
hue 126 becomes 126.0 which is off by 0.0
hue 127 becomes 126.833336 which is off by -0.16666412
hue 128 becomes 128.16669 which is off by 0.16668701
hue 129 becomes 129.16667 which is off by 0.16667175
hue 130 becomes 130.16667 which is off by 0.16667175
hue 131 becomes 131.16667 which is off by 0.16667175
hue 132 becomes 132.16669 which is off by 0.16668701
hue 133 becomes 133.16667 which is off by 0.16667175
hue 134 becomes 134.16667 which is off by 0.16667175
hue 135 becomes 135.16667 which is off by 0.16667175
hue 136 becomes 136.16669 which is off by 0.16668701
hue 137 becomes 137.16667 which is off by 0.16667175
hue 138 becomes 138.16667 which is off by 0.16667175
hue 139 becomes 139.16667 which is off by 0.16667175
hue 140 becomes 140.16669 which is off by 0.16668701
hue 141 becomes 141.16667 which is off by 0.16667175
hue 142 becomes 142.16667 which is off by 0.16667175
hue 143 becomes 143.16667 which is off by 0.16667175
hue 144 becomes 144.16669 which is off by 0.16668701
hue 145 becomes 145.16667 which is off by 0.16667175
hue 146 becomes 146.16667 which is off by 0.16667175
hue 147 becomes 147.16667 which is off by 0.16667175
hue 148 becomes 148.16669 which is off by 0.16668701
hue 149 becomes 149.16667 which is off by 0.16667175
hue 150 becomes 150.16667 which is off by 0.16667175
hue 151 becomes 151.16667 which is off by 0.16667175
hue 152 becomes 152.16669 which is off by 0.16668701
hue 153 becomes 153.16667 which is off by 0.16667175
hue 154 becomes 154.16667 which is off by 0.16667175
hue 155 becomes 155.16667 which is off by 0.16667175
hue 156 becomes 156.16669 which is off by 0.16668701
hue 157 becomes 157.16667 which is off by 0.16667175
hue 158 becomes 158.16667 which is off by 0.16667175
hue 159 becomes 159.16667 which is off by 0.16667175
hue 160 becomes 160.16669 which is off by 0.16668701
hue 161 becomes 161.16667 which is off by 0.16667175
hue 162 becomes 162.16667 which is off by 0.16667175
hue 163 becomes 163.16667 which is off by 0.16667175
hue 164 becomes 164.16669 which is off by 0.16668701
hue 165 becomes 165.16667 which is off by 0.16667175
hue 166 becomes 166.16667 which is off by 0.16667175
hue 167 becomes 167.16667 which is off by 0.16667175
hue 168 becomes 168.16669 which is off by 0.16668701
hue 169 becomes 169.16667 which is off by 0.16667175
hue 170 becomes 170.0 which is off by 0.0
hue 171 becomes 171.0 which is off by 0.0
hue 172 becomes 172.0 which is off by 0.0
hue 173 becomes 172.83333 which is off by -0.16667175
hue 174 becomes 173.83333 which is off by -0.16667175
hue 175 becomes 175.0 which is off by 0.0
hue 176 becomes 175.99998 which is off by -1.5258789E-5
hue 177 becomes 176.99998 which is off by -1.5258789E-5
hue 178 becomes 177.83333 which is off by -0.16667175
hue 179 becomes 179.0 which is off by 0.0
hue 180 becomes 180.0 which is off by 0.0
hue 181 becomes 180.83333 which is off by -0.16667175
hue 182 becomes 181.83333 which is off by -0.16667175
hue 183 becomes 183.0 which is off by 0.0
hue 184 becomes 183.99998 which is off by -1.5258789E-5
hue 185 becomes 184.99998 which is off by -1.5258789E-5
hue 186 becomes 185.83333 which is off by -0.16667175
hue 187 becomes 187.0 which is off by 0.0
hue 188 becomes 188.0 which is off by 0.0
hue 189 becomes 188.83333 which is off by -0.16667175
hue 190 becomes 189.83333 which is off by -0.16667175
hue 191 becomes 191.0 which is off by 0.0
hue 192 becomes 192.0 which is off by 0.0
hue 193 becomes 192.99998 which is off by -1.5258789E-5
hue 194 becomes 193.83333 which is off by -0.16667175
hue 195 becomes 195.0 which is off by 0.0
hue 196 becomes 196.0 which is off by 0.0
hue 197 becomes 196.83333 which is off by -0.16667175
hue 198 becomes 197.83333 which is off by -0.16667175
hue 199 becomes 199.0 which is off by 0.0
hue 200 becomes 200.0 which is off by 0.0
hue 201 becomes 200.99998 which is off by -1.5258789E-5
hue 202 becomes 201.83333 which is off by -0.16667175
hue 203 becomes 203.0 which is off by 0.0
hue 204 becomes 204.0 which is off by 0.0
hue 205 becomes 204.83333 which is off by -0.16667175
hue 206 becomes 205.83333 which is off by -0.16667175
hue 207 becomes 207.0 which is off by 0.0
hue 208 becomes 208.0 which is off by 0.0
hue 209 becomes 208.99998 which is off by -1.5258789E-5
hue 210 becomes 209.83333 which is off by -0.16667175
hue 211 becomes 211.0 which is off by 0.0
hue 212 becomes 212.0 which is off by 0.0
hue 213 becomes 213.0 which is off by 0.0
hue 214 becomes 214.0 which is off by 0.0
hue 215 becomes 215.16666 which is off by 0.1666565
hue 216 becomes 216.16666 which is off by 0.1666565
hue 217 becomes 217.16666 which is off by 0.1666565
hue 218 becomes 218.0 which is off by 0.0
hue 219 becomes 219.16666 which is off by 0.1666565
hue 220 becomes 220.16666 which is off by 0.1666565
hue 221 becomes 221.0 which is off by 0.0
hue 222 becomes 222.0 which is off by 0.0
hue 223 becomes 223.16666 which is off by 0.1666565
hue 224 becomes 224.16666 which is off by 0.1666565
hue 225 becomes 225.16666 which is off by 0.1666565
hue 226 becomes 226.0 which is off by 0.0
hue 227 becomes 227.16666 which is off by 0.1666565
hue 228 becomes 228.16666 which is off by 0.1666565
hue 229 becomes 229.0 which is off by 0.0
hue 230 becomes 230.0 which is off by 0.0
hue 231 becomes 231.16666 which is off by 0.1666565
hue 232 becomes 232.16666 which is off by 0.1666565
hue 233 becomes 233.16666 which is off by 0.1666565
hue 234 becomes 234.0 which is off by 0.0
hue 235 becomes 235.16667 which is off by 0.16667175
hue 236 becomes 236.16666 which is off by 0.1666565
hue 237 becomes 237.0 which is off by 0.0
hue 238 becomes 238.0 which is off by 0.0
hue 239 becomes 239.16667 which is off by 0.16667175
hue 240 becomes 240.16666 which is off by 0.1666565
hue 241 becomes 241.16667 which is off by 0.16667175
hue 242 becomes 242.0 which is off by 0.0
hue 243 becomes 243.16667 which is off by 0.16667175
hue 244 becomes 244.16666 which is off by 0.1666565
hue 245 becomes 245.0 which is off by 0.0
hue 246 becomes 246.0 which is off by 0.0
hue 247 becomes 247.16667 which is off by 0.16667175
hue 248 becomes 248.16666 which is off by 0.1666565
hue 249 becomes 249.16667 which is off by 0.16667175
hue 250 becomes 250.0 which is off by 0.0
hue 251 becomes 251.16667 which is off by 0.16667175
hue 252 becomes 252.16666 which is off by 0.1666565
hue 253 becomes 253.0 which is off by 0.0
hue 254 becomes 254.0 which is off by 0.0
hue 255 becomes 0.0 which is off by -255.0

I guess after looking at the source code it will have a perfectly reasonable explanation, but at this point is super interesting: some are exact, some are above, and some below. Very nice bug :slight_smile:


#3

I had to visualize the error :slight_smile:

void setup() {
  size(512, 512);
  colorMode(HSB);
  translate(0, height/2);
  background(255);
  noStroke();
  fill(0);
  for (int i=0; i<256; i++) {
    float h = hue(color(i, 255, 255));
    float diff = h-i;
    println("hue", i, "becomes", h, "which is off by", diff);
    fill(abs(diff) < 0.01 ? #008800 : #880000 );
    rect(i*2, diff*200, 2, 30);
  }
}

2018-11-13-213225_512x512_scrot

The green entries are correct, some are above, and some are below.


#4

Thanks :smile:
I think that the color type internally works with RGB values and there are some rounding errors when converting a HSB value to a RGB value and back.


#5

Btw, if you use other saturation and brightness values than 255, you get other, more irregular differences.


#6

Sonifying it would be very nice. It has rhythm :slight_smile:


#7

Well, you’re the artist (I checked out your site - wow!), so go ahead. :+1:


#8

It‘s more of an unintentional rounding error. It is cause by calculations that result in numbers that have too many numbers after 0. and this get „rounded“ unintentionally, because a float/double only goes to so many values until it just „rounds“ it. And as a result, if you do the reverse calculation, then this small error results in an error in the calculation from RGB to HSB. This is pretty much unavoidable, unless you use infinte space for your floats… But you could just round your result (Even to a decimal point, not just int) and solve this Problem that way.


#9

@Lexyth Being off by 0.166 in a the range 0~255 is quite a large error and using floats does not account for such an error. I believe it’s caused by conversions between int and float.

It’s also not unavoidable, as if you do the same using TColor from ToxicLibs the error is 0.0. TColor stores the color components as float instead of int, so you can do subtler manipulations to colors which loose their precision only when converting back to ARGB.

import toxi.color.*;
import toxi.geom.*;

void setup() {
  size(512, 512);
  translate(0, height/2);
  background(255);
  noStroke();
  fill(0);
  for (int i=0; i<256; i++) {
    TColor c = TColor.newHSV(i/256.0, 1, 1);
    float h = c.hue() * 256;
    float diff = h-i;
    println("hue", i, "becomes", h, "which is off by", diff);
    
    fill(abs(diff) < 0.01 ? #008800 : #880000 );
    rect(i*2, diff*200, 2, 30);
    
    fill(c.toARGB());
    rect(i*2, -200, 2, 30);
    
  }
}
void draw() {}

But you are right, since the error in every case was below 0.5 in magnitude, round() would fix it.


#10

Well, by unavoidable i didn’t mean that the problem with color conversion can’t be fixed, but that the problem with conversions in general, that the most precise value that is used is double, but a conversion could result in numbers with more point values than a double can handle, and thus be rounded (floored for ints). If you now do the reverse calculation you don’t get the same value as you had, even though you did the exact opposite. This can be seen very clearly with ints. If you do :

int a = 3/2; //the result is not 1.5, but 1, because the .5 is cut off.
//with double it is similar, just after the 16 point number, so : 
float a = 1/3; //becomes 0.3333333432674408, while 
float b = a*3; //becomes 1.0000000298023224

And round() in general wouldn’t fix it, if he wants to use floats. He has to use
round(x*10)/10
to round the first point value, and 100 for second, and so on. Just to be clear :sweat_smile:

Also, just to point it out. The conversion from rgb to hsb mixes different values together, so if you change saturation, then the resulting color will change, leading to the difference that hue() gets from it by calculating from slightly different values. Just having 1 instead of 0 in saturation will cause the resulting double to have a different ending value and thus is rounded differently, which causes a butterfly effect further down the calculation.


#11

Yes, I agree that this happens in some cases. But I still think that this is not what is happening here. Notice how in your example the error is of 0.0000000298023224, but in the case of the hue() it’s 0.1666, 5.5 million times larger.

If all the color calculations where done in floats or doubles, the error would be insignificant, but I think that converting ints to floats and back with multiplications and divisions might have this effect.

When calling hue(), Processing uses java.awt.Color.RGBtoHSB to do the transformation.
But when calling color() Processing calculates the value itself.

I decompiled java.awt.Color and this is the source of that function:

    public static int HSBtoRGB(float hue, float saturation, float brightness) {
        int r = 0;
        int g = 0;
        int b = 0;
        if (saturation == 0.0F) {
            r = g = b = (int)(brightness * 255.0F + 0.5F);
        } else {
            float h = (hue - (float)Math.floor((double)hue)) * 6.0F;
            float f = h - (float)Math.floor((double)h);
            float p = brightness * (1.0F - saturation);
            float q = brightness * (1.0F - saturation * f);
            float t = brightness * (1.0F - saturation * (1.0F - f));
            switch((int)h) {
            case 0:
                r = (int)(brightness * 255.0F + 0.5F);
                g = (int)(t * 255.0F + 0.5F);
                b = (int)(p * 255.0F + 0.5F);
                break;
            case 1:
                r = (int)(q * 255.0F + 0.5F);
                g = (int)(brightness * 255.0F + 0.5F);
                b = (int)(p * 255.0F + 0.5F);
                break;
            case 2:
                r = (int)(p * 255.0F + 0.5F);
                g = (int)(brightness * 255.0F + 0.5F);
                b = (int)(t * 255.0F + 0.5F);
                break;
            case 3:
                r = (int)(p * 255.0F + 0.5F);
                g = (int)(q * 255.0F + 0.5F);
                b = (int)(brightness * 255.0F + 0.5F);
                break;
            case 4:
                r = (int)(t * 255.0F + 0.5F);
                g = (int)(p * 255.0F + 0.5F);
                b = (int)(brightness * 255.0F + 0.5F);
                break;
            case 5:
                r = (int)(brightness * 255.0F + 0.5F);
                g = (int)(p * 255.0F + 0.5F);
                b = (int)(q * 255.0F + 0.5F);
            }
        }

        return -16777216 | r << 16 | g << 8 | b << 0;
    }

And this is from the Processing color() function:

    case HSB:
      x /= colorModeX; // h
      y /= colorModeY; // s
      z /= colorModeZ; // b

      calcA = colorModeScale ? (a/colorModeA) : a;

      if (y == 0) {  // saturation == 0
        calcR = calcG = calcB = z;

      } else {
        float which = (x - (int)x) * 6.0f;
        float f = which - (int)which;
        float p = z * (1.0f - y);
        float q = z * (1.0f - y * f);
        float t = z * (1.0f - (y * (1.0f - f)));

        switch ((int)which) {
        case 0: calcR = z; calcG = t; calcB = p; break;
        case 1: calcR = q; calcG = z; calcB = p; break;
        case 2: calcR = p; calcG = z; calcB = t; break;
        case 3: calcR = p; calcG = q; calcB = z; break;
        case 4: calcR = t; calcG = p; calcB = z; break;
        case 5: calcR = z; calcG = p; calcB = q; break;
        }
      }
break;

AWT adds a 0.5f offset in many cases but Processing doesn’t do that. Is that maybe part of the reason for this discrepancy?

Update: I can confirm that the issue is the different way color() calculates the color when in HSB mode, compared to AWT. If you replace color() by a call to java.awt.Color.HSBtoRGB() the unexpected values are gone and you only get the minor float rounding errors @Lexyth was mentioning above :

import java.awt.*;

void setup() {
  for (int i=0; i<256; i++) {
    float h = hue(Color.HSBtoRGB(i/255.0, 1.0, 1.0));
    println("hue", i, "becomes", h);
  }
}
void draw() {}
...
hue 57 becomes 57.0
hue 58 becomes 57.999996
hue 59 becomes 59.0
hue 60 becomes 59.999996
hue 61 becomes 61.0
hue 62 becomes 61.999996
hue 63 becomes 63.0
hue 64 becomes 63.999996
hue 65 becomes 65.0
hue 66 becomes 66.0
hue 67 becomes 67.0
hue 68 becomes 67.99999
hue 69 becomes 69.0
hue 70 becomes 70.0
...

#12

@hamoid Yeah, the discrepancy was pretty small, but even that small discrepancy can add up if there are enough calculations involved… though apparently there aren’t enough that this could really become an issue and cause such large discrepancies as color does. :sweat_smile:


#13

Nice find, we should fix this.

A bigger problem though is that as saturation and brightness approach zero the precission gradually drops, because the hues and saturations become too subtle for RGB. (And when brightness is zero, you lose hue and brightness values completely.)

Several HSB colors fall between two RGB colors, and after you round to the nearest RGB color, you can’t recover the original HSB color anymore. In other words: compared to RGB, HSB has a higher number of darker and less saturated colors and there is not enough RGB colors to represent them all.

If you need precise HSB calculations, store the H, S and B in floats. Give them to Processing right before you need to draw something :slight_smile: