Lightweight CRT Effect

This shader is a lightweight and optimized CRT effect, designed to balance visual quality and performance. I originally created it for a mobile game, as most CRT shaders I tried were too resource-intensive and drained the battery too quickly.

This shader offers a subtle CRT look while remaining efficient, making it ideal for mobile devices or performance-sensitive projects. Several parameters allow you to fine-tune the effect to match your needs:

  • scan_line_amount – Adjusts the intensity of scanlines.
  • warp_amount – Controls screen curvature distortion.
  • vignette_amount & vignette_intensity – Fine-tunes the vignette effect.
  • grille_amount – Simulates a pixel grid for a retro feel.
  • brightness_boost – Enhances screen brightness.

How to Apply the Shader:

  1. Create a CanvasLayer at the top of your scene to ensure the effect is applied globally.
  2. Add a ColorRect node as a child of the CanvasLayer.
  3. Set the ColorRect size to match the screen resolution.
  4. Apply the CRT shader to the ColorRect’s material.
  5. Enable “Expand” to ensure it covers the screen.
Shader code
shader_type canvas_item;

uniform sampler2D SCREEN_TEXTURE : hint_screen_texture;
uniform vec2 resolution = vec2(320.0, 180.0);

uniform float scan_line_amount : hint_range(0.0, 1.0) = 0.5;
uniform float warp_amount : hint_range(0.0, 1.0) = 0.05;
uniform float vignette_amount : hint_range(0.0, 1.0) = 0.5;
uniform float vignette_intensity : hint_range(0.0, 1.0) = 0.3;
uniform float grille_amount : hint_range(0.0, 1.0) = 0.05;
uniform float brightness_boost : hint_range(1.0, 2.0) = 1.2;

void fragment() {
    vec2 uv = SCREEN_UV;
    
    vec2 delta = uv - 0.5;
    float warp_factor = dot(delta, delta) * warp_amount;
    uv += delta * warp_factor;
    
    float scanline = sin(uv.y * resolution.y * PI) * 0.5 + 0.5;
    scanline = mix(1.0, scanline, scan_line_amount * 0.5); // Rรฉduit l'impact
    
    float grille = mod(uv.x * resolution.x, 3.0) < 1.5 ? 0.95 : 1.05;
    grille = mix(1.0, grille, grille_amount * 0.5);
    
    vec3 color = texture(SCREEN_TEXTURE, uv).rgb * scanline * grille;
    
    vec2 v_uv = uv * (1.0 - uv.xy);
    float vignette = v_uv.x * v_uv.y * 15.0;
    vignette = mix(1.0, vignette, vignette_amount * 0.7);

    color *= vignette * brightness_boost;

    COLOR.rgb = color;
    COLOR.a = 1.0;
}
Live Preview
Tags
2d, CRT, mobile
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

guest

5 Comments
Oldest
Newest Most Voted
Frogman
Frogman
1 year ago

amazing CRT shader, best one yet IMO. however, there does seem to be a bit of a blurry artifact due to the shader, which idk if thats changable but its nice none the less

also, would be nice to add a black border when you up the warp amount, so it doesnt stretch on the edges ๐Ÿ™‚

8/10 ๐Ÿ˜€

DamianArtClub
3 months ago
Reply to  Frogman

Hi! Just came across this shader and its amazing! I was also fighting with the stretching on the edges you mentioned, and I solved it doing the following:

CanvasLayer+ NinepatchRect+ ColorRect (shader here)The ninepatch is set to cover the whole screen, and its literally just a 1 pixel line around the screen, so that the black pixel gets stretched instead of the content behind it, making the “tube” mask appear!

I guess this could be implemented inside the shader itself, by making the pixels on the edges on the screen black, but I really dont know how to code shaders lol. Anyways,

Hope thats usefull ๐Ÿ˜€

Last edited 3 months ago by DamianArtClub
beebster
1 year ago

Nice one, definitely reduces the load on mobile GPU. In fact I commented out other effects and just kept scanline to shave a fraction more off.

Scrython
Scrython
7 months ago

I just wanna say – this is THE BEST CTR SHADER out there!! I had lots of trouble with the other ones, but this one works so well, and is so simple! Thanks!!