Koko-aio shader discussions and updates

Hi guys, first post here. In the past I made a shader for FS-UAE (Amiga emulation), but since i found that retroarch has a pretty good support for whdload games, I ported it to slangp so that it can be used with other emulators too.

I’m not that expert with technical terms, but i’m fortunate enough to own an old 15khz slot mask crt monitor inside an arcade cab loaded with Advance mame and other old emulators, so I used it as a reference. Why using a shader, you may ask, instead of the almost “real thing™”?

Actually that thing is big, heavy and power hungry and since it is running on an old AthlonXP i expect it to break sooner or later and i’ll not have spare time to fix, that’s for sure. Also, i’ve finally got an awesome oled 4k tv and i think that with that amazing (low) pixel persistance and resolution, we can get pretty damn close to the “real thing™”

One of the main goal of this shader is to be usable on integrated GPUs.
Granted, don’t expect it to run on a Raspberry pi, but the actual target is
Intel Haswell, the one on which I develop it, where it perform with all the features
enabled, when dealing with 240p content, on 1080p at resolution ad about 85/90fps.

Actually, it provides emulation for:

  • Scanlines
  • Screenlines
  • RGB phosphors
  • RGB deconvergence
  • NTSC/PAL CVBS color bleeding
  • NTSC color artifacting (early stage)
  • Aperture grille and slot mask.
  • Input signal glowing
  • Output signal glowing
  • Blooming
  • Gamma, contrast, saturation, luminance, color temperature adjustments
  • Black frame insertions through alternate blanking
  • Interlace flickering, forcing and emulation
  • Antialiasing
  • Curvature
  • Ambient lights
  • Vignette and Spotlight
  • Bezel (Thank you HyperspaceMadness for allowing me to rip his automagically generated bezel!) https://github.com/HyperspaceMadness/
  • Background images
  • Full screen glowing

Version 3.9.01 is up on github and should be available via online updater in Retroarch.

Relevant changes over 3.5:


  • Provide a straight bezel as well
  • Better reflections blur
  • Allow to use ambient lights to colorize the bezel by a custom amount
  • Reflection blur can now be customized to depend on the distance from the tube
  • Allow to manually set alignment parameters for bezel reflections
  • Reflection in the corners can be tweaked via static config, not just in the texture.
  • Reflection strength can be freely configured


  • Far better look even at higher sizes with no performance cost.
  • Bloom may now depend on Zone brightness
  • Bloom modulation can be temporally smoothed
  • Bloom temporal modulation inertia can be configured
  • Bloom modulation can be “soloed” to better tweak it

Other Improvements, changes, additions

  • Allow to shift and zoom the whole image.
  • Adjusted global noise/deband code
  • Moved aspect ratio settings down in the option list
  • Add RF noise
  • Allow to specify a minimum and maximum scanline height.
  • Add optional support for backdrop images for BW games that use mirrors
  • Add a clean scanline preset and other “classic_take” presets.
  • Use NTSC conversion weights to get average luminance
  • Allow to change the default back/over image wrap_mode.
  • Luminosity dependant zoom is now temporal smoothed
  • Provide a way to selectively blur ntsc artifacts.


  • Reduced number of total passes
  • Performance optimizations in warp function
  • split config.inc in config.inc and config.globals.inc to easilly
  • Tune parameters to static and gain (a lot of) performance
  • Further reduced ambient light gpu use.
  • Disable bezel mipmap generation by default, gain performance.
  • Bit faster pixel_darklines
  • Better glow spread that depends on color brightness
  • (optionally it can completely drive blur sharpness)
  • glow and halo: use 2 passes (x,y)

Bugs fixed:

  • Fix composite bleed + deconvergence togheter
  • Get rid of another useless pass for ambilight, gains DX11 compatibility
  • Keep bloom size consistent across input resolutions
  • Keep bezel reflection size consistent across resolutions
  • Avoid negative pow exponents (fixes koko-aio under nvidia and probably amd)
  • Bezel reflections now are consistent with luminosity dependant zoom
  • Workaround graphical glitches when switching “temporal” options runtime.

monitor-slotmask-bloom-bezelwider - Arcade - Final fight

monitor-Commodore_1084S-wider - Amiga - ProjectX SE

tv-PAL-my-old - Master System - Trans Bot

tv-NTSC-1 - Genesis - Sonic 2

tv-NTSC-2 - Snes - Aladdin

tv-aperturegrille-bloom-bezel - Amiga - Leander

Even if I’ve had to look to multiple shaders to understand how glsl first and slang works, I re-used and modified blur(bloom) code from CRT - Guest - Dr.Venom (single pass bloom function) and completely included without modifications Nvidia’s FXAA for antialiasing into its own pass.

  • Main development done on this Github repo
  • GLcore or Vulkan are probably required.
  • Stable releases should be available through Retroarch’s online updater
    (shaders_slang/bezel/koko-aio/) or here.
  • Click here if you want to try the latest in-develompent shader and unpack the zip somewhere inside your -already in place- shader directory.
    I do not ensure you it will work as expected nor that your cat will be safe.
  • Make sure to read the docs!
  • A new repo is up: koko-aio-slang-presets-and-overlays
    to contain external submissions of new presets and overlays.
  • If you want to boost your fps and you don’t mind tinkering with text files, take a look to the file config.globals.inc in the shader folder and follow the instructions; on intel IGPs, I’ve seen fps boosted by 45%.

Please, tell me what you think, any comment is really appreciated.


Screenshots look great!

I tried testing it, but looks like you forgot to add curvature.slang to your repository. Retroarch gives that error.


Yep, sorry, and thanks! Just updated!


Thanks! It worked now! I like your slotmask-bloom preset, using scanlines. Good for my tastes. I’m not very technical about crt shaders, so I just gave a completely subjective opinion.

1 Like

Looks really good! We’re now at a point where we’re spoiled with choices for great looking CRT Shaders!

You can take a look at what I’ve been able to come up with here as well as some other cool CRT Shader projects:

CyberLab Mega Bezel Death To Pixels Shader Preset Pack

Sony Megatron Color Video Monitor

The Guest-Advanced NTSC thread

The Guest-Advance, 100% mask strength thread

Example videos:

This is what CyberLab Turbo Duo for Blargg + Blargg_NTSC_Turbo_Duo_SNES_PSX_S-Video_CyberLab_Special_Edition looks like today!

CyberLab Turbo Duo for Blargg + Blargg_NTSC_Turbo_Duo_SNES_PSX_S-Video_CyberLab_Special_Edition

This is what CyberLab SNES looks like today!

CyberLab SNES

This is what CyberLab Genesis for Blargg + Blargg_NTSC_Genesis_S-Video_CyberLab_Special_Edition looks like today!

CyberLab Genesis for Blargg + Blargg_NTSC_Genesis_S-Video_CyberLab_Special_Edition

This is what CyberLab NES for Blargg + Core Blargg NTSC S-Video looks like today!

CyberLab NES for Blargg + Core Blargg NTSC S-Video

CyberLab NES for Blargg + Core Blargg NTSC S-Video

CyberLab NES for Blargg + Core Blargg NTSC S-Video

You can use MPC-HC, MX Player or VLC Player to view the videos.

Here’s a look at my latest preset!

CyberLab Ultimate Virtual Slot Mask CRT-1P2RTA

New Preset and System Recommendations

TVch34 Overlay


TVch34 Overlay + Mega Bezel

Yeah, I’ve seen your presets, they are great. It seems to me they are targeting 4k resolutions, since you draw the triads horizontal gap too, right?

Do you have any comment or critic specific to work?

1 Like


My presets target 4K mainly but I’ve also tested and optimized presets for 1080p and to a lesser extent 1440p.

Most of them use RGB Mask including at 4K which uses the same mask with a size of 2 which would make it RRGGBB. This is mainly to remain compatible with displays that can’t do RGB Full 4:4:4 at 4K60Hz resolution, like one of my TVs.

Even after testing RRGGBBX masks initially, at 4K60Hz RGB Full 4:4:4, I still preferred RRGGBB because RRGGBBX TVL seemed lower.

However since I realized that in order to get “phosphor” triads to look properly on OLED TVs, I needed to use RRGGBBX mask with reversed Layout, I’ve started to include presets like that for use with OLED TVs.

I’m not one to be too critical, as I don’t even use a real CRT as a reference. I just tweak until stuff looks good to me and your work looks good!

I would just try to avoid making things even the slightest bit blurry especially when viewed from a distance because it’s tough on my eyes.

Keep up the great work!

1 Like

With the new update you can now:

  • Emulate monitor curvature
  • Emulate a light spot (in the screenshot it is in the top/right corner)
  • Emulate a vignette effect (see in the screenshot that the monitor uniformity is uneven)
  • Emulate led ambient lights; by now the fade speed is slow and costant, but i plan to add a scene change detection euristic somehow to make the fades instant when the scene changes:

This is a WIP, you need to set aspect ratio to full in retroarch for the ambient light border to work. Also, the aspect ratio may be wrong in some configurations.


Love what I’m looking at here , great stuff , I was wondering if it was possible to reduce the bloom radius less than 0.5 , Ideally to pixel perfect level ? the bloom can help restore some color but the “fog” bleeding can reduce the vibrance

The image below settings were adjusted to my display saturation ,it might look too saturated

1 Like

For that maybe you can just disable blooming and use ‘halo’ setting?

1 Like

In the latest commit i discovered the usefulness of mipmaps over retroarch downscaling :slight_smile:

I may be wrong, but when scaling down using simpler scale#n=, even with bilinear filtering turned on, it seems to just discards whole lines.

Indeed I was in the need of a fast way to get the average luminance of the screen, and just scaling down to a 1x1 textures using absolute scale#n gave me just a pixel, probably from the middle of the texture.

Using mipmapping with a looow precision worked good instead and it gave me the perfect average luminance. This was handy to cheaply detect when a scene change.

That way, the “ambilight™” like effect is slow and not distracting when the scene is the same, but will instantly change when the scene changes.

The change is available in the latest commit.


Yes, you’re correct. Using the mips is the best way to get a fullscreen average.


Right then, so I need to rethink some other downscaling passes made for (big blurs) bloom and ambient lights itself. Thank you

edit: also, I wonder if downscaling this way is the correct behaviour of retroarch; since specifying bilinear filtering doesn’t change a thing; same very bad quality; It’s more like a decimation filter than a down scaler.

If you set mipmap_input on for a pass it automatically takes a mipmapped version, so smoothed together if the resolution is lower.

I do this in a few passes in the Mega Bezel, an example is below, the first pass is really almost like the stock shader, but it linearize it so the blurs work properly and blend in linear space.

// Reduce Resolution  ----------------------------------------------------------------
//      Reduce the resolution to a small static size regardless of final resolution
//      Allows consistent look and faster at different final resolutions for blur
//      Mipmap option allows downscaling without artifacts
shader17 = ../../shaders/HyperspaceMadness/hsm/hsm-linearize-crt.slang
mipmap_input17 = true
filter_linear17 = true
scale_type17 = absolute
// scale_x17 = 480
// scale_y17 = 270
// scale_x17 = 960
// scale_y17 = 540
scale_x17 = 800
scale_y17 = 600
alias17 = "BR_MirrorLowResPass"

// Add Blur for the Reflection (Horizontal) ----------------------------------------------------------------
shader18 = ../../shaders/HyperspaceMadness/hsm/hsm-blur-outside-screen-horiz.slang
mipmap_input18 = true
filter_linear18 = true

// Add Blur for the Reflection (Vertical) ----------------------------------------------------------------
shader19 = ../../shaders/HyperspaceMadness/hsm/hsm-blur-outside-screen-vert.slang
filter_linear19 = true
alias19 = "BR_MirrorBlurredPass"

// Reduce resolution ----------------------------------------------------------------
// Reduced to a very small amount so we can create a blur which will create a glow from the screen
//      Mipmap option allows smoother downscaling
shader20 = ../../../../blurs/blur9x9.slang
mipmap_input20 = true
filter_linear20 = true
scale_type20 = absolute
scale_x20 = 128
scale_y20 = 128
alias20 = "BR_MirrorReflectionDiffusedPass"

// Add Diffused glow all around the screen ----------------------------------------------------------------
//      Blurred so much that it's non directional
//      Mipmap option allows downscaling without artifacts
shader21 = ../../../../blurs/blur9x9.slang
mipmap_input21 = true
filter_linear21 = true
scale_type21 = absolute
scale_x21 = 12
scale_y21 = 12
alias21 = "BR_MirrorFullscreenGlowPass"

So If i understood properly, this means i just need to set mipmap_input in the slangp preset and then normally use its scaling parameter and normal texture() lookup instead of textureLod() to get the mipmapped texture?

That would be handy!

1 Like

Yeah that’s what it seems to do, as if it was doing textureLod() and giving the correct mipmap level


Hi there, I just released 3.0 version.

The shader is faster (its gpu usage is under 65% with all of the features enabled on my poor Haswell igpu) and also looks better than ever to me.

The biggest addition is the bezel emulation for which I have to say a big thank you to HSM for allowing me to rip his automagically generated monitor frame to a simple png file.

However, the code implementation is very different from Mega Bezel, because koko-aio needs transparent areas for tube.
Adapting Mega Bezel backgrounds should still be possible with little work.
That said, standard/classic overlays works just fine, you just need to replace background.png and adapt zoom levels, curvature and image border right from the shader options. (see screenshot)

One thing still missing are presets for HiDPI screens.
If you like how the shader looks @1080p and have some free time, there are several options in the Mask area to hopefully make a preset that looks good with higher resolutions.

Yes, screenshot or it didn’t happen, ofc.

Fake, but cheap ntsc emulation:

Running with all bells and whistles and zoomed-in monitor frame

Zoomed out and with a background image reacting to ambient light,
cold color temperature

A standard Overlay, I just elarged “the hole”
with Gimp to allow the reflections to pass through too,
aperturegrille, shamelessly over-bloomed, because why not?

Some relevant changes from the latest releases:

  • Better denoise function
  • Allow to zoom out the output
  • Bezel and reflections
  • Interlace flickering emulation (you should see it live)
  • Several performance improvements (about 5% faster now)
  • Tweaked ambient light leds parameters
  • Better Bezel hilights and reflections
  • Whole image zooming depending on the content brightness
  • Improved scene detection logic for Ambient led lights
  • Bloom: allow to choose how much it affects bright colors
  • Allow to set an RGB Mask size for HiDPI screens
  • Allow to draw a gap between rgb mask triads
  • Presets tweaking
  • VMASK: implemented green-magenta to rgb subpixel layout
  • Add support for RGB offset
  • Add Contrast, brightness and color temperature controls
  • Add support for background or alpha driven foreground images
  • Add new “vibrant tv” and “fake ntsc” presets
  • Improved bloom look
  • Add simple chroma bleed option
  • Code cleanup

Comments always appreciated, critics needed, have fun!


Hi there.

I just saw the new version of the shader. I tested out the NTSC profile of the shader. Do you know how to get a more accurate NTSC effect so that dither patterns and vertical lines like those you see on Genesis games get blended better? Or do I have to mess around with the bandwidth limited chroma bleeding option? Nice shader by the way!

Hi, full NTSC encoding/decoding is just not implemented (yet), so the specific “patterns to color” feature is something you cannot achieve. All of my systems was PAL, so I wasn’t interested in implementing it and by now the only thing you can do is to play with chroma bleeding, blur (input glow) and RGB shifting, but it will trick only untrained, naked eyes :slight_smile:

1 Like

Ah ok, that’s a shame. I really like the “blurry composite” feel of the shader though.