LED Grid Overlay Shader (Adjustable Aspect Ratio) v.2.0
LED Grid Overlay Shader (v2.0 – Retro FX Update)
This canvas_item shader, created for Godot 4.x, transforms your screen content into a highly convincing, customizable LED display. It samples colors from the underlying scene and renders them as bright, circular LEDs on a dark background, making it perfect for retro arcade cabinets, low-resolution screens, or sci-fi UI elements.
🌟 Key Features
-
Adjustable Aspect Ratio: Control the X and Y resolution independently (
grid_columnsandgrid_rows). This allows for non-square grid cells (like widescreen low-res effects) while guaranteeing the LEDs themselves are always perfectly circular. -
Authentic Screen Sampling: Uses the
SCREEN_TEXTUREfor accurate and reliable sampling of the game content underneath the overlay. -
Adaptive Glow: The glow intensity now dynamically adjusts based on the sampled color’s brightness, giving brighter LEDs a more noticeable bloom.
✨ New in v2.0: Retro Effects & Animation
| Feature | Description |
| Screen Curvature | Add a subtle lens/barrel distortion (curvature) to simulate a classic curved CRT or monitor glass. |
| Quantization Dithering | When color posterization is enabled, optionally toggle dithering (enable_dither) to minimize color banding and enhance the low-res look. |
| Analog Flicker | Toggle a subtle, per-cell brightness flicker (enable_flicker) for an added layer of imperfect, analog realism. |
| Glow Pulse | Control a pulsing animation (glow_pulse) for dynamic, gameplay-driven effects like a heartbeat, damage, or power-up. |
⚙️ Usage & Parameters
Usage: Apply this shader to a ColorRect or Control node that is positioned above your game content.
| Parameter | Description | Default | Range |
grid_columns/rows |
Horizontal/vertical number of LED cells. | 32 | 8-256 |
led_brightness |
Brightness multiplier for LED colors. | 2.5 | 1.0-5.0 |
led_radius |
Size of the core LED relative to the cell size. | 0.4 | 0.1-0.8 |
glow_size |
Extra falloff distance for the surrounding glow. | 0.3 | 0.0-1.0 |
glow_strength |
Intensity of the glow effect. | 0.5 | 0.0-1.0 |
background_opacity |
Opacity of the dark background between LEDs. | 0.9 | 0.0-1.0 |
curvature |
Controls the screen’s lens distortion effect. | 0.05 | 0.0-0.2 |
color_quantization |
Color posterization level (0 to disable). |
0.0 | 0.0-32.0 |
glow_pulse |
Gameplay driven pulse animation intensity. | 0.0 | 0.0-1.0 |
Feel free to use, improve, and change this shader according to your needs, and consider sharing your modifications on godotshaders.com!
Shader code
// LED Grid Overlay Shader (v2.0)
// Created for Godot 4.x
//
// Creates a highly customizable LED display effect by sampling colors from the
// underlying scene content and rendering them as bright circular LEDs on a dark
// background.
// Perfect for retro arcade cabinet effects, sci-fi UI elements, or low-res display simulation.
//
// Key Feature: Independent X/Y resolution for non-square grid cells while maintaining circular LEDs.
//
// Usage:
// - Apply to a ColorRect or Control node positioned above your game content.
// - Ensure the parent viewport has 'Disable RTT' turned OFF and 'Screen Space AA' set to 'Disabled'
// or 'FXAA' for best results.
//
// ----------------------------------------------------------------------------------------------
// PARAMETERS:
//
// --- CORE GRID & LED ---
// - grid_columns/rows: Number of LED cells horizontally/vertically (8-256, default 32).
// - led_brightness: Brightness multiplier for LED colors (1.0-5.0, default 2.5).
// - led_radius: Size of each LED relative to its grid cell (0.1-0.8, default 0.4).
//
// --- GLOW & BACKGROUND ---
// - glow_size: Extra falloff for the glow effect (0.0-1.0, default 0.3).
// - glow_strength: Intensity of the glow effect (0.0-1.0, default 0.5). Glow is now adaptive to LED brightness.
// - background_color: Color of the dark areas between LEDs (default black).
// - background_opacity: Opacity of the background (0.0-1.0, default 0.9).
//
// --- RETRO FX ---
// - curvature: Adds a subtle lens/barrel distortion for a CRT-like curve (0.0=off, default 0.05).
// - color_quantization: Optional color posterization (0.0=off, 8-32 typical, default 0.0).
// - enable_dither: If quantization is on, adds a simple dither pattern to smooth color banding.
// - enable_flicker: Adds subtle, randomized, per-cell brightness flicker for an analog look.
//
// --- ANIMATION / GAMEPLAY ---
// - glow_pulse: Animates the overall intensity of the LEDs/glow (0.0=no pulse, 1.0=max pulse).
// ----------------------------------------------------------------------------------------------
shader_type canvas_item;
// --- Built-in needed for screen sampling ---
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture; // MUST be added
// --- LED Grid Dimensions ---
uniform int grid_columns : hint_range(8, 256) = 32;
uniform int grid_rows : hint_range(8, 256) = 32;
// --- LED Display Parameters ---
uniform float led_brightness : hint_range(1.0, 5.0) = 2.5;
uniform float led_radius : hint_range(0.1, 0.8) = 0.4;
uniform float glow_size : hint_range(0.0, 1.0) = 0.3;
uniform float glow_strength : hint_range(0.0, 1.0) = 0.5;
uniform float color_quantization : hint_range(0.0, 32.0) = 0.0;
uniform vec3 background_color : source_color = vec3(0.0, 0.0, 0.0);
uniform float background_opacity : hint_range(0.0,1.0) = 0.9;
// --- Gameplay / animation / Retro FX ---
uniform float glow_pulse : hint_range(0.0,1.0) = 0.0;
uniform float curvature : hint_range(0.0,0.2) = 0.05;
uniform bool enable_dither = true;
uniform bool enable_flicker = true;
void fragment() {
// --- 1. Optional curvature warp ---
vec2 centered = UV * 2.0 - 1.0;
vec2 warped_uv = UV + centered * dot(centered, centered) * curvature;
// --- 2. Grid setup ---
vec2 cell_count = vec2(float(grid_columns), float(grid_rows));
vec2 cell_size = 1.0 / cell_count;
// Quantize UV to find the cell's center
vec2 grid_pos = floor(warped_uv / cell_size + 0.0001);
vec2 cell_center = (grid_pos + 0.5) * cell_size;
// --- 3. Sample base color from screen texture (modified to account for cell center) ---
vec2 screen_uv_cell_center = SCREEN_UV + (cell_center - UV);
vec4 sampled_color = texture(SCREEN_TEXTURE, screen_uv_cell_center);
// --- 4. Color quantization and dithering ---
if (color_quantization > 0.0) {
if (enable_dither) {
// Simple pseudo-random noise for dithering (from a fixed UV seed)
vec2 noise = vec2(fract(sin(dot(UV.xy, vec2(12.9898,78.233)))*43758.5453));
float dither = (noise.x + noise.y) * 0.5 / color_quantization;
sampled_color.rgb = floor((sampled_color.rgb + dither) * color_quantization) / color_quantization;
} else {
sampled_color.rgb = round(sampled_color.rgb * color_quantization) / color_quantization;
}
}
// --- 5. Per-cell flicker effect ---
if (enable_flicker) {
float flicker = 0.95 + 0.05 * sin(TIME * 6.283 + dot(grid_pos, vec2(13.37, 42.42)));
sampled_color.rgb *= flicker;
}
// --- 6. Cell-local coordinates for circular LED rendering ---
vec2 cell_uv = fract(warped_uv / cell_size) - 0.5;
float cell_aspect = cell_size.x / cell_size.y;
cell_uv.x /= cell_aspect; // Normalize X based on cell aspect ratio to ensure perfectly round LEDs
float dist2 = dot(cell_uv, cell_uv);
// --- 7. LED & Glow Falloff Calculation ---
float r = led_radius * 0.5;
float r_sq = r * r;
float led_intensity = 1.0 - smoothstep(r_sq * 0.95, r_sq * 1.05, dist2);
float glow_r = (led_radius + glow_size) * 0.5;
float glow_r_sq = glow_r * glow_r;
// Adaptive glow: Brighter colors get a stronger glow effect
float lum = dot(sampled_color.rgb, vec3(0.299,0.587,0.114));
float adaptive_glow = mix(0.2, 1.0, lum);
float glow_intensity = (1.0 - smoothstep(r_sq, glow_r_sq, dist2)) * glow_strength * adaptive_glow;
// Pulse multiplier for gameplay
float pulse_multiplier = 1.0 + glow_pulse * 0.3;
// --- 8. Final Color Mix ---
float total_intensity = max(led_intensity, glow_intensity) * pulse_multiplier;
vec3 brightened_color = sampled_color.rgb * led_brightness;
vec3 final_color = mix(background_color, brightened_color, total_intensity);
float final_alpha = mix(background_opacity, sampled_color.a, total_intensity);
COLOR = vec4(final_color, final_alpha);
}


