As VDMX6 and other tools were forced to sunset Quartz Composer, we've gotten quite a few requests to help revive some beloved FX patches. In some cases—like the classic Rutt Etra effect—the process of modernization requires a rewrite in Metal as a built-in FX. Fortunately for many of the v002 plugins from vade the custom QC object was mostly a wrapper for a cool GLSL shader that could be easily adapted to ISF.
To take advantage of the ISF versions of the v002 FX, just use the .fs versions when selecting from the list of FX in VDMX instead of the .qtz versions where possible.
For those who are curious about how these shaders were adapted from GLSL to ISF, in this tutorial we will look at the ‘v002 Glitch Analog’ plugin as an example.
What is ISF? Why should we use it?
ISF (Interactive Shader Format) is a simple, flexible format for writing GPU-accelerated visual effects that run in real time. It’s used by apps like VDMX, Lumen, CoGe, and even in web-based environments with the right embedding.
ISF shaders are written in GLSL, just like traditional fragment shaders, but they include a JSON metadata block at the top for defining control parameters, persistent memory buffers, multiple passes, and other useful information.
Like with Quartz Composer compositions, one of the best parts of working with ISF shaders is they can be easily opened and modified. Using any standard text editor you can create your own custom remixes of your favorite video generators and visual FX.
Getting Started
The first step is getting the code that we will be converting from the Github repository,
The original v002.AnalogGlitch.frag GLSL shader code from the plugin.
The v002AnalogGlitchPlugIn.m file for the QC plugin that wraps the .frag file and declares the inputs.
Beyond declaring the names and ranges for the input controls, we don’t need anything from the .m file in this case; most of the code here is simply a wrapper for loading and rendering the GLSL code, all of which is handled automatically when working with ISF based shaders.
JSON section describing the input controls and other metadata for the FX in BBEdit.
Converting from GLSL to ISF
The general process here will be:
Create the new ISF file by,
Using the “New” menu option in the ISF Editor, or
Creating a new file with the .fs file extension in your favorite text editor, such as BBEdit
Declare the input controls and other metadata in the JSON section at the top of the ISF.
We can find the original declarations for the plugin in the ‘+ (NSDictionary*) attributesForPropertyPortWithKey:(NSString*)key’ portion of the v002AnalogGlitchPlugIn.m file.
Don’t forget to includes the credits!
Adapt the AnalogGlitch.frag file to ISF. In this case there are only a few details to change.
Switch from using texture2DRect() calls to IMG_NORM_PIXEL() and IMG_PIXEL() functions.
Use the RENDERSIZE built-in variable instead of texdim0.
Use the isf_FragNormCoord built-in variable instead of texcoord0.
End Result
The final adapted version of v002.AnalogGlitch.frag that we now include with VDMX6 looks like this:
/*
{
"CATEGORIES" : [
"Glitch", "Distortion Effect"
],
"DESCRIPTION" : "Emulates classic analog video interference, distortion and sync issues.",
"ISFVSN" : "2",
"INPUTS" : [
{
"NAME" : "inputImage",
"TYPE" : "image"
},
{
"NAME" : "inputDistortionImage",
"TYPE" : "image"
},
{
"NAME" : "inputDistortion",
"TYPE" : "float",
"MAX" : 5,
"DEFAULT" : 0.0,
"LABEL" : "Distortion",
"MIN" : 0
},
{
"NAME" : "inputBarsAmount",
"TYPE" : "float",
"MAX" : 1,
"DEFAULT" : 0.25,
"LABEL" : "Distortion Mix",
"MIN" : 0
},
{
"NAME" : "inputVSYNC",
"TYPE" : "float",
"MAX" : 2,
"DEFAULT" : 0.0,
"LABEL" : "V Sync",
"MIN" : 0
},
{
"NAME" : "inputHSYNC",
"TYPE" : "float",
"MAX" : 2,
"DEFAULT" : 0.0,
"LABEL" : "H Sync",
"MIN" : 0
},
{
"NAME" : "inputResolution",
"TYPE" : "float",
"MAX" : 10,
"DEFAULT" : 5,
"LABEL" : "Scan Line Size",
"MIN" : 1
},
{
"NAME" : "inputResolutionMix",
"TYPE" : "float",
"MAX" : 1,
"DEFAULT" : 0.5,
"LABEL" : "Scan Lin Mix",
"MIN" : 0
}
],
"CREDIT" : "Vade / VIDVOX"
}
*/
// Original v002 Analog Glitch by Vade
// https://github.com/v002/v002-Glitch/blob/master/v002AnalogGlitchPlugIn.m
// Adapted to ISF by VIDVOX 2025
void main (void) {
vec2 point = isf_FragNormCoord.xy;
// sample center of the 1st pixel, down the height
vec4 bars = IMG_NORM_PIXEL(inputDistortionImage, vec2(0.5, point.y));
// scanlines
float stripe = mod(floor(gl_FragCoord.y), floor(inputResolution)) / floor(inputResolution);
float scanlineMask = mix(1.5, 0.75, stripe);
scanlineMask = mix(1.0, scanlineMask, inputResolutionMix);
// get rough luma
vec4 key = IMG_PIXEL(inputImage, (vec2(point.y, point.y)) * RENDERSIZE);
key += IMG_PIXEL(inputImage, (1.0 - vec2(point.y, point.y)) * RENDERSIZE);
key -= bars.r;
float d = (key.r + key.g + key.b) / 3.0 - 0.5;
point.x -= d * inputDistortion * 0.1;
//sync
vec2 texcoord = point + ( mod(vec2(inputHSYNC, inputVSYNC), 1.0));
// wrap
texcoord = mod(texcoord, 1.0);
// outout
vec4 result = IMG_PIXEL(inputImage, texcoord * RENDERSIZE);
result.rgb = result.rgb * scanlineMask;
gl_FragColor = mix(result, bars*result, inputBarsAmount);
}
For more examples of ISF shaders that are recreations of QC plugins and compositions from vade, look for the following files in your /Library/Graphics/ISF/ directory:
v002 Bleach Bypass.fs
v002 Crosshatch.fs
v002 Dilate.fs
v002 Dilate.vs
v002 Erode.fs
v002 Erode.vs
v002 Light Leak.fs
v002 Light Leak.vs
v002 Technicolor.fs
v002 Vignette.fs
v002-CRT-Displacement.fs
v002-CRT-Mask.fs
You can also find more tips and tricks in ISF documentation for converting GLSL shaders to work in ISF.