Shader Plugin Developer Guide

Z uses a hardware-accelerated OpenGL 3.3 Core rendering engine. Create effects, transitions, color grades, and audio-reactive shaders by adding annotated .frag files under plugins/.

Create and reload a plugin

  1. Create a .frag file in any subfolder of plugins/.
  2. Add an @name annotation and the uniforms your shader needs.
  3. In Z, choose Effects → Reload Plugins, then add or re-select the effect.

Reload is manual. Z scans plugins on launch and when you choose Effects → Reload Plugins; it does not watch files for changes automatically.

Overview & How It Works

When Z Video Editor launches, the C++ PluginManager scans the plugins/ folder and all its subdirectories. It parses top-of-file comment annotations to build GUI controls (sliders, checkboxes, keyframe tracks) and dynamically compiles GLSL shaders on the GPU.

No application rebuild is required. Edit a .frag file, choose Effects → Reload Plugins, then add or re-select the effect.

Folder Hierarchy & UI Tree Auto-Mapping

Subfolders inside plugins/ automatically map directly to nested categories in the editor's Effects browser panel:

Directory Taxonomy Structure
Z/
└── plugins/
    ├── Color & FX/
    │   └── my_film_grade.frag       --> Appears under "Color & FX"
    ├── Glitch & Datamosh/
    │   └── my_glitch.frag           --> Appears under "Glitch & Datamosh"
    └── Retro Gaming/
        └── Rock Band/
            └── my_rockband_fx.frag  --> Appears under "Retro Gaming -> Rock Band"

Header Annotations (@param)

Place comment tags at the very top of your .frag file to define name, category, and UI controls:

Annotation Tag Syntax Example Description
@name // @name My Custom Effect Name displayed in the Effects Browser and Timeline clip inspector.
@category // @category Retro Gaming/Rock Band Explicit tree category override (optional if in subfolder).
@desc // @desc VHS tracking static filter Tooltip description displayed when hovered in UI.
@param // @param var Label min max default [bool] Exposes a float uniform parameter as an interactive GUI control.

Creating Toggle Switches (Booleans)

If you add bool to the end of a @param declaration, Z Video Editor renders it as an ON/OFF checkbox toggle switch instead of a slider:

Boolean Checkbox Annotation Example
// Syntax: // @param <uniformName> <Label> <min> <max> <default> bool
// @param active Enable Glitch 0.0 1.0 1.0 bool
uniform float active; // Evaluates to 1.0 when checked, 0.0 when unchecked

Engine Data Uniforms (Passed from Video Pass)

Z Video Engine passes a rich set of video, timeline, mouse, and audio spectrum data to all fragment shaders automatically on every render frame:

Uniform Name GLSL Type Description & Data Range
videoTexture sampler2D Primary input video frame texture (Texture Unit 0).
videoTexture2 sampler2D Secondary video frame texture (used during cut transitions).
feedbackTexture sampler2D Previous frame FBO texture (used for temporal ghosting & motion blur trails).
time float Timeline playback position in seconds (e.g. 12.45).
resolution vec2 Viewport dimensions in pixels (width, height) (e.g. 1920.0, 1080.0).
aspect float Aspect ratio width / height (e.g. 1.77777 for 16:9).
frameIndex int Exact timeline frame counter floor(time * 30.0).
fps float Timeline target framerate (e.g. 30.0).
mouse vec2 Normalized cursor position (x, y) in range [0.0, 1.0].
mouseX float Normalized cursor X coordinate [0.0, 1.0].
mouseY float Normalized cursor Y coordinate [0.0, 1.0].
mousePressed int Left mouse button state: 1 if pressed, 0 if released.
audioBass float Realtime FFT Audio Bass energy amplitude [0.0 - 1.0].
audioMid float Realtime FFT Audio Midrange frequency amplitude [0.0 - 1.0].
audioTreble float Realtime FFT Audio Treble frequency amplitude [0.0 - 1.0].
audioVolume float Master audio RMS volume level [0.0 - 1.0].
progress float Transition cut progress [0.0 - 1.0].

Line-by-Line Code Tutorial 1: Color Grade & Time Wobble

Below is a complete, line-by-line annotated shader showing how color grading, luminance thresholding, and time-based sine wave warping work:

plugins/Color & FX/tutorial_color_wobble.frag
#version 330 core

// Step 1: Define UI Header Annotations
// @name Tutorial 1: Color & Time Wobble
// @category Color & FX
// @desc Line-by-line educational shader for color manipulation
// @param wobbleSpeed Wobble Speed 0.1 5.0 1.0
// @param saturation Saturation Boost 0.0 2.0 1.2
// @param active Enable Filter 0.0 1.0 1.0 bool

// Step 2: Declare standard inputs from vertex shader pass
in vec2 TexCoord;    // Normalized UV coordinates [0.0, 0.0] top-left to [1.0, 1.0] bottom-right
out vec4 FragColor;  // Final RGBA pixel color written to framebuffer

// Step 3: Declare built-in video engine uniforms
uniform sampler2D videoTexture; // Input video texture sampler
uniform float time;             // Timeline playback time in seconds
uniform vec2 resolution;         // Viewport width and height in pixels

// Step 4: Declare custom user parameters defined in @param annotations
uniform float wobbleSpeed;
uniform float saturation;
uniform float active;

void main() {
    // Step 5: Read original source video pixel color at current TexCoord
    vec4 baseColor = texture(videoTexture, TexCoord);

    // Step 6: If active toggle is OFF (0.0), bypass shader and output raw video
    if (active < 0.5) {
        FragColor = baseColor;
        return;
    }

    // Step 7: Calculate horizontal UV offset using trigonometric sine wave
    vec2 uv = TexCoord;
    float wave = sin(uv.y * 20.0 + time * wobbleSpeed * 4.0) * 0.01;
    uv.x += wave;

    // Step 8: Sample video frame at distorted UV position
    vec4 color = texture(videoTexture, uv);

    // Step 9: Calculate perceptual luma using ITU-R BT.601 weights
    float luma = dot(color.rgb, vec3(0.299, 0.587, 0.114));

    // Step 10: Interpolate between grayscale luma and color for saturation adjustment
    vec3 saturatedColor = mix(vec3(luma), color.rgb, saturation);

    // Step 11: Write final RGBA color output
    FragColor = vec4(saturatedColor, color.a);
}

Line-by-Line Code Tutorial 2: Audio-Reactive FFT Pulse

This shader uses the video engine's audioBass, audioMid, and audioTreble uniforms to pulse colors and warp geometry in sync with background music audio:

plugins/Motion & Audio/tutorial_audio_pulse.frag
#version 330 core

// @name Tutorial 2: Audio FFT Visualizer
// @category Motion & Audio
// @desc Pulsating chromatic energy tied to audio bass and treble
// @param bassScale Bass Zoom Scale 0.0 2.0 1.0
// @param active Enable Audio FX 0.0 1.0 1.0 bool

in vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D videoTexture;
uniform float audioBass;    // Realtime low-frequency FFT amplitude [0.0 - 1.0]
uniform float audioTreble;  // Realtime high-frequency FFT amplitude [0.0 - 1.0]
uniform float bassScale;
uniform float active;

void main() {
    vec4 baseColor = texture(videoTexture, TexCoord);
    if (active < 0.5) { FragColor = baseColor; return; }

    vec2 centerUV = TexCoord - vec2(0.5);
    float dist = length(centerUV);

    float zoomFactor = 1.0 - (audioBass * 0.15 * bassScale);
    vec2 pulsedUV = centerUV * zoomFactor + vec2(0.5);

    vec4 pulsedColor = texture(videoTexture, pulsedUV);
    vec3 flash = vec3(0.8, 0.2, 1.0) * audioTreble * smoothstep(0.2, 0.5, dist);

    FragColor = vec4(pulsedColor.rgb + flash, baseColor.a);
}

Line-by-Line Code Tutorial 3: Mouse-Interactive Ripple Shader

This shader uses mouse, mouseX, mouseY, and mousePressed to distort video around the interactive cursor position in realtime:

plugins/Distortion & Warp/tutorial_mouse_ripple.frag
#version 330 core

// @name Tutorial 3: Mouse Ripple Distortion
// @category Distortion & Warp
// @desc Interactive ripple centered on mouse cursor position
// @param rippleRadius Ripple Radius 0.05 0.5 0.2
// @param active Enable Mouse Ripple 0.0 1.0 1.0 bool

in vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D videoTexture;
uniform vec2 mouse;          // Normalized cursor position (x, y) in [0.0, 1.0]
uniform int mousePressed;    // 1 when left mouse button is held down, 0 otherwise
uniform float time;
uniform float rippleRadius;
uniform float active;

void main() {
    vec4 baseColor = texture(videoTexture, TexCoord);
    if (active < 0.5) { FragColor = baseColor; return; }

    vec2 uv = TexCoord;
    vec2 dir = uv - mouse;
    float dist = length(dir);

    if (dist < rippleRadius) {
        float amp = (mousePressed == 1) ? 0.08 : 0.03;
        float wave = sin(dist * 50.0 - time * 10.0) * amp * (1.0 - dist / rippleRadius);
        uv += normalize(dir) * wave;
    }

    FragColor = texture(videoTexture, uv);
}

Line-by-Line Code Tutorial 4: Bitwise XOR Hardware Glitch

This tutorial demonstrates how GPU bitwise integer operators (^, &, |) are used to simulate hardware digital frame corruptions:

plugins/Glitch & Datamosh/tutorial_xor_hardware.frag
#version 330 core

// @name Tutorial 4: Bitwise XOR Hardware Glitch
// @category Glitch & Datamosh
// @desc Bitwise XOR integer bitmask corruption of RGB video channels
// @param xorMask Red/Green Bitmask 0.0 255.0 128.0
// @param active Enable XOR Glitch 0.0 1.0 1.0 bool

in vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D videoTexture;
uniform float xorMask;
uniform float active;

void main() {
    vec4 baseColor = texture(videoTexture, TexCoord);
    if (active < 0.5) { FragColor = baseColor; return; }

    uvec3 intColor = uvec3(baseColor.rgb * 255.0);
    uint maskVal = uint(clamp(xorMask, 0.0, 255.0));
    uvec3 mask = uvec3(maskVal, maskVal / 2u, 0u);

    uvec3 xorColor = intColor ^ mask;
    vec3 finalRGB = vec3(xorColor) / 255.0;

    FragColor = vec4(finalRGB, baseColor.a);
}