Confetti (ColorRect) [Godot 4.5+]

A canvas_item shader that rains colorful confetti pieces from the top of the screen. All 120 particles are simulated entirely on the GPU, so no nodes, no particles system, no GDScript needed at runtime.

Each piece has its own randomized fall speed, sway frequency, sway amplitude, spin direction, size, and aspect ratio, giving the effect a natural and lively feel. A subtle shimmer simulates the pieces flipping as they rotate. The 8-color palette is fully customizable directly in the Inspector.

Simply apply the shader to a full-screen ColorRect and set the resolution uniform to your viewport size via script

 

Shader code
shader_type canvas_item;

// Usage: 
// 1. Append a ColorRect (Full size)
// 2. Add this Shader to the ColorRect


const int PARTICLE_COUNT = 120;

uniform float   speed         : hint_range(20.0, 500.0)  = 120.0;
uniform float   sway_strength : hint_range(0.0,  200.0)  = 60.0;
uniform float   sway_speed    : hint_range(0.1,  5.0)    = 1.4;
uniform float   spin_speed    : hint_range(0.0,  10.0)   = 3.5;
uniform float   piece_size    : hint_range(2.0,  30.0)   = 10.0;
uniform float   aspect_ratio  : hint_range(0.2,  3.0)    = 2.0;
uniform vec2    resolution    = vec2(1920.0, 1080.0);

uniform vec4    col0 : source_color = vec4(1.00, 0.20, 0.20, 1.0);
uniform vec4    col1 : source_color = vec4(1.00, 0.75, 0.10, 1.0);
uniform vec4    col2 : source_color = vec4(0.10, 0.80, 0.30, 1.0);
uniform vec4    col3 : source_color = vec4(0.15, 0.55, 1.00, 1.0);
uniform vec4    col4 : source_color = vec4(0.85, 0.20, 1.00, 1.0);
uniform vec4    col5 : source_color = vec4(1.00, 0.40, 0.70, 1.0);
uniform vec4    col6 : source_color = vec4(0.10, 0.90, 0.95, 1.0);
uniform vec4    col7 : source_color = vec4(1.00, 1.00, 1.00, 1.0);

float rand(float seed) {
    return fract(sin(seed * 127.1 + 311.7) * 43758.5453);
}

vec4 palette_color(float t) {
    int idx = int(t * 8.0) % 8;
    if (idx == 0) return col0;
    if (idx == 1) return col1;
    if (idx == 2) return col2;
    if (idx == 3) return col3;
    if (idx == 4) return col4;
    if (idx == 5) return col5;
    if (idx == 6) return col6;
    return col7;
}

vec2 rotate2d(vec2 v, float angle) {
    float s = sin(angle);
    float c = cos(angle);
    return vec2(c * v.x - s * v.y,
                s * v.x + c * v.y);
}

void fragment() {
    vec2 px = UV * resolution;
    vec4 out_color = vec4(0.0);

    for (int i = 0; i < PARTICLE_COUNT; i++) {
        float fi = float(i);

        float start_x  = rand(fi * 1.234)  * resolution.x;
        float start_y  = -(rand(fi * 5.678) * resolution.y);
        float t_offset = rand(fi * 9.101)  * (resolution.y + piece_size * 2.0) / speed;

        float my_speed      = speed         * (0.5 + rand(fi * 2.222));
        float my_sway_str   = sway_strength * (0.3 + rand(fi * 3.333) * 1.4);
        float my_sway_spd   = sway_speed    * (0.5 + rand(fi * 4.444) * 1.5);
        float my_sway_phase = rand(fi * 6.666) * TAU;
        float my_spin       = spin_speed    * (0.5 + rand(fi * 7.777) * 1.5)
                              * (rand(fi * 8.888) > 0.5 ? 1.0 : -1.0);
        float my_size       = piece_size    * (0.5 + rand(fi * 0.123) * 1.0);
        float my_ar         = aspect_ratio  * (0.7 + rand(fi * 0.456) * 0.6);

        float t     = mod(TIME + t_offset, (resolution.y + my_size * 2.0) / my_speed);
        float cy    = start_y + t * my_speed;
        float cx    = start_x + my_sway_str * sin(t * my_sway_spd + my_sway_phase);
        float angle = t * my_spin;

        vec2 delta = px - vec2(cx, cy);
        delta = rotate2d(delta, -angle);
        vec2 local = delta / vec2(my_size * my_ar, my_size);

        float inside = step(max(abs(local.x), abs(local.y)), 1.0);

        if (inside > 0.5) {
            float shimmer = 0.5 + 0.5 * sin(t * my_spin * 2.0);
            vec4 base_col = palette_color(rand(fi * 0.321));
            vec4 lit_col  = mix(base_col * 0.5, base_col, shimmer);
            lit_col.a     = 1.0;
            out_color = mix(out_color, lit_col, inside);
        }
    }

    COLOR = out_color;
}
Live Preview
Tags
2d, ColorRect, Confetti
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

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments