Retro VHS Glitch for Godot 4

A customizable CRT-style glitch shader that simulates signal distortion, scanlines, color channel shifts, noise, slicing, ghosting, and optional negative inversion. Ideal for retro aesthetics, VHS/TV effects, or adding chaotic glitch transitions to your 2D projects.

Shader code
shader_type canvas_item;

uniform float intensity : hint_range(0.0, 1.0) = 0.5;
uniform float glitch_frequency : hint_range(0.1, 10.0) = 2.0;
uniform float glitch_duration : hint_range(0.01, 0.5) = 0.1;

uniform float red_displacement : hint_range(0.0, 0.1) = 0.02;
uniform float green_displacement : hint_range(0.0, 0.1) = 0.02;
uniform float blue_displacement : hint_range(0.0, 0.1) = 0.02;

uniform float scanline_intensity : hint_range(0.0, 1.0) = 0.3;
uniform float noise_intensity : hint_range(0.0, 1.0) = 0.2;
uniform float slice_intensity : hint_range(0.0, 1.0) = 0.4;
uniform float ghost_intensity : hint_range(0.0, 0.5) = 0.1;
uniform float negative_intensity : hint_range(0.0, 1.0) = 0.0;

float random(vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

float noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));
    
    vec2 u = f * f * (3.0 - 2.0 * f);
    
    return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

void fragment() {
    vec2 uv = UV;
    vec4 base_color = texture(TEXTURE, uv);
    vec4 final_color = base_color;
    
    float glitch_time = TIME * glitch_frequency;
    float glitch_noise = noise(vec2(glitch_time, 0.0));
    float glitch_active = step(1.0 - intensity, glitch_noise);
    
    if (glitch_active > 0.5) {
        float slice_y = random(vec2(glitch_time * 10.0, 0.0));
        if (abs(uv.y - slice_y) < 0.02 * slice_intensity) {
            float slice_offset = (random(vec2(glitch_time * 15.0, 0.0)) - 0.5) * 0.1 * slice_intensity;
            uv.x += slice_offset;
        }
        
        float vertical_shift = random(vec2(glitch_time * 8.0, 0.0)) * 0.05 * intensity;
        uv.y += vertical_shift;
        
        float wave = sin(uv.y * 100.0 + glitch_time * 5.0) * 0.01 * intensity;
        uv.x += wave;
        
        final_color = texture(TEXTURE, uv);
    }
    
    float shift_amount = red_displacement * glitch_active;
    vec4 red_channel = texture(TEXTURE, uv + vec2(shift_amount, 0.0));
    vec4 green_channel = texture(TEXTURE, uv + vec2(-green_displacement * glitch_active, 0.0));
    vec4 blue_channel = texture(TEXTURE, uv + vec2(blue_displacement * glitch_active, 0.0));
    
    final_color.r = mix(final_color.r, red_channel.r, glitch_active);
    final_color.g = mix(final_color.g, green_channel.g, glitch_active);
    final_color.b = mix(final_color.b, blue_channel.b, glitch_active);
    
    float scanline = sin(uv.y * 300.0 + glitch_time * 2.0) * 0.5 + 0.5;
    final_color.rgb *= 1.0 - scanline * scanline_intensity * intensity;
    
    float static_noise = random(uv + glitch_time) * noise_intensity * glitch_active;
    final_color.rgb += static_noise;
    
    if (ghost_intensity > 0.0) {
        vec4 ghost_color = texture(TEXTURE, uv + vec2(0.01, 0.01)) * ghost_intensity;
        final_color.rgb = mix(final_color.rgb, ghost_color.rgb, ghost_intensity * 0.5);
    }
    
    if (negative_intensity > 0.0) {
        vec4 negative_color = vec4(1.0 - final_color.rgb, final_color.a);
        final_color.rgb = mix(final_color.rgb, negative_color.rgb, negative_intensity * glitch_active);
    }
    
    float glow = glitch_active * 0.2 * intensity;
    final_color.rgb += glow;
    
    COLOR = final_color;
}
Live Preview
Tags
2d, CRT, glitch, godot4
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Noman
Noman
4 months ago

Amazing