Forums > Jitter


July 22, 2013 | 10:41 am

so, i am trying my luck again, this time in the jitter forums.

i need to convert sRGB (0 1) values to HCL values, but i aready fail at the rgb to xyz conversion.

the formulas i can find on the net are either java or some pseudo code, which i dont fully understand. do i need all the scaling stuff or do i need only the matrix transform?

from what most examples are telling me, rgb to xyz conversion would look about that:

vexpr (($f1>0.04045)*(srqt((($f1+0.055)/1.055)\,2.4)) + ($f1<=0.04045)*($f2/12.92))*100.
unpack 0. 0. 0.
expr ($f1*0.4124)+($f2*0.3576)+($f3*0.1805) X
expr ($f1*0.2126)+($f2*0.7152)+($f3*0.0722) Y
expr ($f1*0.0193)+($f2*0.1192)+($f3*0.9505) Z

there should be a working example for srgb-xyz-hcl in the processing language, maybe some of you would be so kind and explain the code so that it can be written in simple max. my current OSX doesnt not allow to run me processing due to the java version.


July 22, 2013 | 11:55 am

I can’t comment on your code, but last year I released a suite of shaders that perform various colorspace conversions. RGB to CIE-XYZ and CIE-XYZ to CIE-L*CH are included.

July 22, 2013 | 12:14 pm


– Pasted Max Patch, click to expand. –


July 22, 2013 | 12:21 pm

i made up that max code from the same source you were using (, but as it seems i have a serious error in it … srqt() instead of pow() … which is a returning error here … hence the weird output.

but so i was right using everything and not only the matrix transform, and i also understood the method how to interpret that quasi code right. at least.

can you explain what rectify() means in your shader and why you are using this where the source said *100?

July 22, 2013 | 12:26 pm

ah you have the *100. in, i didnt see it at first. thanks so far.

July 23, 2013 | 5:54 am

I just got done doing this. Here is an accurate RGB to XYZ converter. Couldn’t figure out how to get my [if] expressions more efficient, but it works like a charm.

– Pasted Max Patch, click to expand. –

July 23, 2013 | 6:05 am is also an excellent resource.

RGB XYZ matrices can be found here:

Can one of you gentlemen tell me how to make use of those .jxs shader files? Also, what is the best way to perform standard multiplication of matrices? I tried to multiply matrices using jit.op. only to discover that I suck at jitter.

July 23, 2013 | 7:35 am

you can get [if] more efficient when you use [expr] instead. :)

later when i am at max i will compare the results to see if my code is right finally.

i have yet to work out lab and luv – and if these are really getting me closer to what i was calling "hcl" above. my first aim is to get a perfect luminance parameter, and check out how big of a difference it makes compared to HSV.

July 23, 2013 | 7:41 am

oh, and i just saw that other thread. i find it interesting that you ponted out that rgb isnt a colorspace. but from what you say this also would mean that you can not "convert" rgb to srgb, isnt it?

i wonder in this context how one can be on the safe side when trying to convert R,G,B channels to a colorspace.
is it wrong to assume that a photoshop picture (which did not make use of custom palettes) or a quicktime-encoded movie file from after effects or u&i software provides or "contains" srgb (or at least something similar, like apple rgb) when used in max or jitter?

July 23, 2013 | 8:33 am

Yeah, you don’t really convert RGB to a colorspace as much as you specify RGB within a certain color space.. which is simply a matter of defining how the primaries mix together to form other colors …which all depends on how "white" is defined and how much red, green and blue is used. You can think of this ‘space’ as a way of scaling and interpolating primary color values to generate a spectrum.

sRGB is usually the safest bet, but it’s not ideal because it’s limited.. and lacks certain colors. Other spaces can be too big.. and go beyond the range of what we can see. That’s why XYZ and L*ab spaces are so useful. They both emphasize the range of what we can actually see.

A good general rule is that unless it’s specified otherwise, stick with sRGB.

July 23, 2013 | 8:36 am

This is the best calculator to use for checking your results..

July 23, 2013 | 8:44 am

i wonder if photoshop really jumps between colorspaces al the time, or if it not uses something like a 10-bit long table for scaling the "luminance" or "saturation" parameters before applying them directly to the individual R’G'B channels. (in max we could use a signal buffer for storing these curves)

July 25, 2013 | 4:32 pm

normal people would probably just stay with photoshop and after effects, but well. i had an hour left today, fired up max, and got that lch stuff finished.

if you put the RGB 01 to XYZ, XYZ to L*a*b*, and L*a*b* to L*CH° conversions all in one patch, that will really look a bit weird in max/msp.

dont forget that when you use this data now to apply a change on the luma parameter as a part of a luma-depending blend mode, then you will have to add another bunch of cryptic math to convert that back to RGB.


  1. direct-lch-forum

July 25, 2013 | 5:08 pm

and here are the individual patches.

the code is optimized for speed (which might sound like a joke when doing things like that on this platform) and the outlets follow a strict right to left order.

i would be really interested in the results of a clocker speed test between my versions and the ones from metamax.


July 25, 2013 | 7:42 pm

hey Roman I think in this expression:

vexpr (($f1/$f2)>0.008856)*(pow(($f1/$f2)\,(1./3.))) + (($f1/$f2)<=0.008856)*((($f1/$f2)*7.787)+(16/116))

the 16/116 is going to return a zero as it's an int/int
I think it should be 16./116.
or even better just use a literal (0.13793…)

July 25, 2013 | 11:30 pm

Roman, those are great. But given that my patch takes three separate ints and yours takes a list of floats.. it’s not clear how to design the best speed test.

In terms of accuracy (or at least getting similar output values).. both rgb-xyz patches output identical values (to six decimal places) about 7500/100000 calculations for red, 8000/100000 for green and 5600/100000 for blue. The mean difference in non-identical output values is 0.000002 for red, 0.000003 for green and 0.000004 for blue, respectively. Those numbers were stable over 100,000+ calculations. So accuracy isn’t the issue.

In terms of speed, depending on how things are tweaked so that both patches take the same kind of values, the results can vary quite a bit. It seems that processing a single list with a vector expressions is a lot more efficient than processing three separate inputs, but the time it takes to make a list (if needed) slows things down. iow, depending on the original source of data, your patch goes from about 70% faster to negligibly faster. If the source data is three ints, then you seem to lose much of the speed advantage processing them to a list of floats… Whereas, I don’t have much advantage either way because of the lack of vector processing in my patch. Or something like that?

It’s all interesting either way. Thanks for the input.

July 26, 2013 | 5:34 am

speed test:
well, in a real life situation i would of course use [110.rgb2rgb01] – or [pak 0. 0. 0.]-[vexpr $f1/255.] – before going into the XYZ transform. feel free to add this at the beginning.
normally mine should be way faster because i saved tons of connections compared to using + – * / objects (except for the right to left triggers, which i am very anal about), but one never know, in some cases when you pack everything into expressions you calculate things two or three or even four times (like the atan2 variable in the 2HCL tranform), and expr is 64 bit internally (though am never sure if that makes a difference for atan() or log() calculation time, i only know it does for + and -.)
but doing all in float ("110.rgb01 objects") seemed just practically. this 3-stage conversion from rgb to hcl is the best example why you should not output int, int, int from _any color abstraction.

"lack of vector processing … or something like that" … the main issue in my version is the higher accuracy or expr, and the main issue of your version is that you are sending so many data via max connections at the patcher level. integrating [+ 0.] and [* 0.] into [epxr ($f1+$f2)*$f3] save you one time "a float is sent from + to *" which needs like 10 times more CPU compared to what happens inside expr.

btw., i am very proud of the elegant solution i found with that loadbanged list of constants. :D

thanks for checking out! expr should normally help to get more accuracy, too.
but when i was asking myself why they show you only 4 digits after the comma at, i noticed that you will never need more accuracy than that, not for calculating something which ends up in a rounded 3×8 bit format.
last but not least, we´re not even going to use something like log() or a 8 decimalplaces shifting or range mapping here, where a high precision during such a calculation would be interesting.
in a C++ app you would probably try to do all these calculations in a 12-bit floating point space, that would be just fine for colors. well, or use translation tables for certain effects/parameters.

no, an int in an expr which contains a float variable just treats it as float. the symbol "16/116" is already parsed into the objects as floats. (not sure if i am using the right terminology here)
expr gets it mode from the $ variables only (if there is one. in [expr 3+3.] the presence of at least one float sets the mode to float).
sometimes it would be cool if expr also take ints when it is in float mode, but that it cannot has something to do with its general design.

i should still change that to 16./116., for better readability, and because i dont want to be inconsequent when it comes to style.

or i use 0.13793, which of course would be faster.


July 31, 2013 | 5:00 pm

sorry Roman, but i tested the 16/116 and it returns a zero (0.00000) in an expr object– looks like it has to be typed explicitly with a point

but I made a speed test comparing your method and the jit.colorspace method (which seems to return spurious values anyway), and your patch is nearly twice as fast (about 75% faster) on my machine:


– Pasted Max Patch, click to expand. –


converting the list into a matrix and then back again takes some time, but your method is still 35% faster than converting a single-cell rgb matrix to lab without converting to and from a list

August 3, 2013 | 1:50 pm

wow, you are actually right.

can it be that i have never run into it during the last 10 years and during the making of almost 1000 abstractions using expressions? it seems so.

the second factor which my wrong assumption is based on i also interesting: not only that i was probably using 16/116 in an expr for the very first time, as it seems, i was testing my patch against the calculator only for ($f1/$f2)>0.008856 but not for ($f1/$f2)<=0.008856

that is a classic! as soon as you leave the path of proper scientific methods to build you knowledge and make assumptions … you are wrong. :D

but it is a pretty weird behaviour, dont you think?

[expr $f1+16/116] returns 0, but [expr $f1/116] is performed in float.

this is a bit inconsequent, and it is not even reproducing the look and feel of other max objects. i wold understand when the divisor is determining float or int calculation, but [expr $f1+16./116] will also work.

now guess what happens with [expr $i1+16./116] … and then explain me the logic behind this.

i´ve just checked some older patches of mine (where i was using things like "1/3." … normally i always used one dot .. so luckily i at least dont patch based on my wrong assumptions.


Viewing 20 posts - 1 through 20 (of 20 total)