Hey everyone, lately, I've been asked to dive back into the world of retro aesthetics, and one thing that always stands out is the look of old CRT (Cathode Ray Tube) monitors. That characteristic scanline flicker, the subtle noise, and the slight color fringing – it's all part of a nostalgic visual language. I wanted to bring that look into Baselight (last time I did this it was for Mistika. https://www.johndaro.com/blog/2020/10/28/vhs-shader), so I built a custom Matchbox shader that accurately simulates these CRT artifacts. This post breaks down the process, from finding inspiration to creating a user-friendly tool.
The Inspiration: Shadertoy
My journey started on Shadertoy, a fantastic resource for exploring and learning about GLSL shaders. I found a great CRT effect shader (https://www.shadertoy.com/view/Ms3XWH) that captured the essence of the look I was after. It used clever techniques to generate scanlines, add noise, and even simulate chromatic aberration (that slight color separation you see at the edges of objects on old TVs).
However, Shadertoy shaders are self-contained and designed for a specific environment. To make this useful in Baselight I needed to adapt it for the Matchbox framework.
From Shadertoy to GLSL Standard
The first step was to "translate" the Shadertoy-specific code into standard GLSL. This involved a few key changes:
mainImage
tomain
: Shadertoy uses a function signaturemainImage(out vec4 fragColor, in vec2 fragCoord)
. Standard GLSL, and Matchbox, usevoid main(void)
. We also replacefragCoord
with the built-ingl_FragCoord
and output the color togl_FragColor
.Uniform Inputs: Shadertoy provides inputs like
iResolution
(screen resolution) andiChannel0
(the input texture) automatically. In Matchbox, we need to explicitly declare these asuniform
variables:adsk_result_w
,adsk_result_h
, andsrc
, respectively. We also addediTime
as a uniform to control animation.Texture Sampling: Shadertoy's
texture
function becomes the standardtexture2D
in GLSL.
Here's a snippet illustrating the change:
Shadertoy:
void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord.xy / iResolution.xy; // ... vec4 tex = texture(iChannel0, uv); fragColor = tex; }
Standard GLSL (for Matchbox):
uniform float adsk_result_w; uniform float adsk_result_h; uniform sampler2D src; void main (void) { vec2 uv = gl_FragCoord.xy / vec2(adsk_result_w, adsk_result_h); // ... vec4 tex = texture2D(src, uv); gl_FragColor = tex; }
Making it Controllable: Matchbox XML
The real power of Matchbox comes from its ability to expose shader parameters as user-adjustable controls. This is done through an XML file that describes the interface. I wanted to give users control over the key aspects of the CRT effect:
Scanline Width: How thick the scanlines appear.
Noise Quality: The granularity of the vertical noise (lower values create more distinct lines).
Noise Intensity: The amount of horizontal jitter.
Scanline Offset: The intensity of the vertical scanline displacement.
Chromatic Aberration: The strength of the color fringing.
Time: The speed of the animation.
To achieve this, I did the following:
GLSL Changes: In the GLSL code, I replaced the
const float
variables that controlled these parameters withuniform float
variables. This is crucial – it tells Matchbox that these values can be changed externally.
// Before (hardcoded): const float range = 0.05; // After (Matchbox controllable): uniform float scanlineRange;
XML Creation: I created an XML file (with the same name as the GLSL file) that defines the controls. Each control is specified using a <Uniform>
tag. The most important attribute is Name
, which must match the corresponding uniform
variable name in the GLSL code.
<Uniform Max="0.1" Min="0.0" Default="0.05" Inc="0.001" ... Name="scanlineRange"> </Uniform>
The XML also includes attributes like DisplayName
(the label in the Baselight UI), Min
, Max
, Default
, Tooltip
, and layout information (Row
, Col
, Page
). These define how the control appears and behaves in Baselight.
Putting it All Together
The final step was to place both the .glsl
and .xml
files in the usr/fl/shaders directory. Baselight automatically recognizes the shader and makes it available in the Matchbox node. Pro tip, my shaders directory is a link to a network location. This way all the Baselights can share the same shaders and it makes updating easier.
Now, when I add a Matchbox node and select the CRT effect, I get a set of sliders and controls that let me tweak the look in real-time. I can easily adjust the scanline thickness, add more or less noise, and dial in the perfect amount of retro goodness.
Download the Files
You can download the complete GLSL and XML files for this Matchbox shader here:
Conclusion
This project was a great upgrade to my GLSL knowledge, demonstrating how to take a cool shader effect from a platform like Shadertoy and adapt it into a practical, user-friendly tool for Baselight. The combination of GLSL's power, speed, and Matchbox's flexibility opens up a world of possibilities for creating custom effects that can be used through out the entire post pipeline. It might be old tech but still very useful today. I hope this breakdown inspires you to experiment with your own implementation. Let me know what you think, and feel free to share your own shader creations!