Dogway's grading shader (slang)

Just updated Grade and Glass to official repo as PR, should be merged soon I guess.

Meanwhile this is my final comparison between RAW Sonic colors and final Grade adjusted colors. Settings are the same from the last time.


Grade 2023


This looks slighty warm.

This looks more than a bit cool. Contrast and saturation seem slightly muted and the red in the banner doesn’t look red. I’m not even sure what colour that is.

Overall the image is less vibrant in both brightness and colour departments.

Are sure this is where you want to leave things? Can you post a comparison image to old Grade as well?

Sure, that is with CRT Beam Red at 0.75 to roughly match my old grade 2020 preset look. Set it back to 1.0 and you will get roughly a neutral look with overall higher brightness (Red will not subtract to the overall brightness).

To make it pop more also set U and V Multiplier from my preset 0.95 to 1.0 but I believe saturation was always lost in analog systems.

The red was always like that (orangey) for NTSC-J, that hasn’t changed.

That’s basically my preset for NTSC-J but feel free to experiment and add your own CRT color artifacts.

This is RAW with luma fix:

This is Grade 2023 without color artifacts (only phosphor gamut and temperature -and vignette-):

And this is my old Grade 2020 preset (repost):

1 Like

Thanks for this. I’m not used to NTSC-J colours at least for Sega Genesis. I mostly use the 0 setting for TV-Standard because I was told that the different settings assumed that one was using a LUT on a professionally calibrated display. Someone (cough @Nesguy) can correct me if I’m wrong.

I hope I’m not confusing this with the colour space setting because I also remember something about sRGB should be used in most cases since that’s what the vast majority of users’ displays might support.

1 Like

Well, if you aim for a PVM with RGB interface sure, set signal type to RGB, phosphor to SMPTE or anything that suits you and temperature to 9200K. I simply like a consumer CRT look with red phosphor decay.

And the games were highly saturated in my opinion for the same reasons than for GBA games, because saturation is lost through the signal. Yeah, miss @Nesguy and @Syh, they were highly active back then.

The output color space is straight forward, I use 709 because my display is calibrated to 709 primaries and an EOTF of 2.4. If yours is calibrated to sRGB (EOTF of roughly a power function of 2.222) then set that to sRGB. But sRGB was never meant to be used at home, but offices and well lit rooms.


I updated Grade with some ideas on my repo and while at it noticed some people would get confused with the plethora of settings so I made a brief guide below.

The changelog:

  • Fix signal_type not being parsed
  • Fix a typo on SLANG no-LUT
  • Fix GLSL fallback defaults
  • Add headers for parameters and move up White Point on the stack
  • Remap CRT Brightness and CRT Contrast values to more CRT-like (also go negative)
  • Add CRT flare given a CRT reflection percentage and a Surround Luminance in nits (at simulated 2700K)
  • Quantize model conversion to 8-bits to replicate CRT (and console) circuit arithmetics (not sure about this one so please test)
Signal Type (0:RGB 1:Composite)                           = 0:RGB disables CRT color controls (Hue, U and V)
Phosphor (-2:CRT-95s -1:P22-80s 1:P22-90s 2:NTSC-J 3:PAL) = Emulated CRT phosphor gamut
Diplay Color Space (-1:709 0:sRGB 1:DCI 2:2020 3:Adobe)   = Host display calibrated color space
Dark to Dim adaptation                                    = Dark to Dim OOTF fpr 709 and 2020 color spaces

// ANALOG CONTROLS //    = This block is for CRT related controls so they behave like a CRT would do
White Point              = Emulated CRT white point temperature. Typically blue voltage was increased to increase the perceptual brightness of the image also as typically red phosphors would decay quick
CRT Hue                  = HUE drift
CRT U Shift              = Color drift in the U channel
CRT V Shift              = Color drift in the V channel
CRT U Multiplier         = Color burst to control saturation in U channel
CRT V Multiplier         = Color burst to control saturation in V channel
CRT Gamma                = CRT output gamma (EOTF)
CRT Brightness           = CRT brightness control (misnomer for Offset)
CRT Contrast             = CRT contrast   control (misnomer for Gain)
CRT Beam Red             = Controls the summed average of beam voltage current and phosphor decay for red
CRT Beam Green           = Controls the summed average of beam voltage current and phosphor decay for green 
CRT Beam Blue            = Controls the summed average of beam voltage current and phosphor decay for blue 
CRT Lambert Refl. in %   = Percetange of flare/glare lambertian reflectance (F0) of CRT display
Surround Luminance -nits-= Surround luminance in nits (cd/m2) for emulated room viewing environment at 2700K surround illuminant
Vignette Toggle          = Toggles vignetting effect around the borders
Vignette Strength        = Controls the vignette strength of the effect
Vignette Power           = Controls the decay rate for the vignette

// DIGITAL CONTROLS //   = Digital color grading controls for artistic purposes
Sega Luma Fix            = Fix white level for Retro Sega Systems; Genesis, 32x, CD and Saturn 2D had color palettes designed in TV levels to save on transformations
Brightness               = Increases brightness (read "Gain") in a roll-off fashion (aka no clipping)
Contrast                 = Applies a sigmoidal (aka no clipping) contrast
Contrast Pivot           = Sets the pivot for the sigmoidal contrast, therefore allowing going from an S shaped curve to a more power like (think gamma) curve
Saturation               = Increase or lower the global saturation
Dullness/Vibrance        = Interacts with "Saturation" to bias its weight over most saturated areas vs least saturated areas
Hue vs Sat Red           = Increase or lower saturation in reds
Hue vs Sat Green         = Increase or lower saturation in greens
Hue vs Sat Blue          = Increase or lower saturation in blues
Black Level              = Raise the black level in a "Lift" fashion
Black-Red Tint           = Red   tint for the black floor (noop if blacks are not raised)
Black-Green Tint         = Green tint for the black floor (noop if blacks are not raised)
Black-Blue Tint          = Blue  tint for the black floor (noop if blacks are not raised)
White-Red Tint           = Red   tint for the whites (similar to CRT Beam Red but keeping brightness)
White-Green Tint         = Green tint for the whites (similar to CRT Beam Red but keeping brightness)
White-Blue Tint          = Blue  tint for the whites (similar to CRT Beam Red but keeping brightness)
Red-Green Tint           = Green contribution to Reds
Red-Blue Tint            = Blue  contribution to Reds
Green-Red Tint           = Red   contribution to Greens
Green-Blue Tint          = Blue  contribution to Greens
Blue-Red Tint            = Red   contribution to Blues
Blue-Green Tint          = Green contribution to Blues
LUT 1 Toggle             = Enable LUT 1 (look LUT)
LUT Size 1               = Size of LUT 1 (8, 16, 32, 48, 64)
LUT 2 Toggle             = Enable LUT 2 (technical LUT)
LUT Size 2               = Size of LUT 2 (8, 16, 32, 48, 64)

Very interesting. Did the old implementation also do this with no clipping?

1 Like

Yes, the function rolled_gain() hasn’t changed in my updates.

Overall I think the shader could be squeeze-optimized a little bit further, but I don’t know how to profile the shader or whether it’s slow or fast.

1 Like

I didn’t know this and I used to be very concerned about clipping when trying to get back lost brightness due to strong mask and scanline settings in my presets.

Perhaps you can benchmark the same games using different versions to see if there are any differences.

There seems to be an issue with the new grade which is causing some blurring for some users.


With ‘new grade’ you mean my last update from yesterday?

If that’s so my first thought could be the quantization to 8-bit, that’s why it works when the new grade is placed first in the stack as it should, otherwise it will mess with float pipelines. You can also try if setting signal type to RGB which bypasses quantization fixes the blurriness to confirm that’s the issue.


I’ll give this a try later when I get home. Also wasn’t aware you updated grade one more time. My shader presets are set up in a way where grade is placed about 3 passes down in the guest shader chain. I’m not sure if having it first will cause some other issue with the other passes in the guest shader, I’ll report back after testing though.

1 Like

Yes, updated yesterday in this post. Didn’t commit to libretro yet because I wanted you to test whether quantization wasn’t creating unwanted posterization. Also I’m researching on gamut compression and wanted to see if I can implement that before.

Grade should always be the first in the stack, it’s output shouldn’t conflict with anything down below because I don’t do any geometric distortion nor texture scaling.


So I likely need to stick this in the middle of my chain where I may have the passes in float format, will this work if I have Grade in Float format? Or will this cause problems?


I set the pass to float to reduce rounding errors but internally I do a quantization to 8-bit when using ‘composite’ signal type. Later float pipeline is restored for the digital grade controls.


Isn’t this accurate? Main difference is that it uses 9300k instead of 6500k, and blank/black are the same but I guess you know that already.

1 Like

Those are different topics, the paper simply defines the conversion from the RGB model to the YUV model while at the same time saturating the whole video range. I do the normalization separately and my model conversion is full->full. And here you find the range definitions for each region.

The temperatures also varies from spec to real world units as current TVs do. None were 6500K but more towards 7100K, and 9300K was only for master monitors in Japan, consumer units were more in the ~8000K, typically 8600K.


@Dogway seem to have done quite a bit of research into NTSC-J color temperatures. I was amazed.
Older CRTs don’t know the exact Kelvin, so for example Panasonic 4K LCD VIERA is 8830K as standard. Therefore, I think that 8600K on a cathode ray tube is almost close. kk32
6500K is often used as a cinema mode in Japan.

It’s wonderful that the white text doesn’t turn blue even though the sky blue of Super Mario Bros. (FC/NES) is light blue.


I decided that I should post solid source information, so I will post two articles.
The Panasonic 4K LCD VIERA and the SONY projector.
This is an article written by a Japan AV-related writer. Of course, it’s in Japanese, so if you’re interested, I recommend translating it into English.
Both references to color temperature are in the middle or bottom of the page.

Sony projector is 8000K, but since the projector projects without directly emitting light, it is probably slightly darker, so I personally think it will be higher than 8000K.

1 Like

Thanks for the sources. It’s always interesting to see other POVs, but as far I could read (still can read some Japanese), it adheres too much on standards.

For instance the recommendation of D65 on a black box, where D60 would make more sense. Or the misconception to match white point with room luminary. There’s an optical effect called “Simultaneous Color Assimilation”, and it’s a non-linear surround visual leak into our perception of temperature. You can see a graph about it here. In other words, if you can’t control your surround illuminance and you’re stuck to 2700K or 3200K surround, the ideal white point for the display to be perceived white at standard 100nits is around 4100K. Given that eyes can’t white adapt to such temperature, it’s ok to set to D50, or simply turn off the lights and use D60. Translating that to a display with fixed D65 white point, surround illuminant should be around 6700K and 100nits.