Quick answer: Shader uniform arrays do not update from GDScript when you pass a regular Array instead of a PackedFloat32Array (or the matching packed type). Godot’s rendering server requires the exact packed type. Also verify you are setting local uniforms via material.set_shader_parameter() and not confusing them with global uniforms.

Here is how to fix Godot shader uniform arrays not updating. You declare a uniform float my_values[8] in your shader, call material.set_shader_parameter("my_values", my_array) from GDScript, and the shader keeps using the default values as if nothing happened. No error in the output panel, no warning, no visual change. The array data you send from the CPU side simply never arrives at the GPU. This is one of the most common gotchas when working with custom shaders in Godot 4.

The Symptom

You have a shader with a uniform array declaration. Maybe it stores light positions, color palette entries, or per-segment data for a procedural effect. You set the array from GDScript using set_shader_parameter, and the shader ignores the values entirely. The default values (usually all zeros) are used instead.

Sometimes the array works when set in the Inspector (via the ShaderMaterial resource) but not when set from code. Or it works once during _ready() but stops updating when you change values in _process(). In other cases, the first element updates correctly but the rest stay at zero.

The problem is maddeningly inconsistent: scalar uniforms (uniform float speed) work fine with set_shader_parameter, but arrays silently refuse.

What Causes This

1. Type mismatch between GDScript array and shader uniform. This is the root cause in the vast majority of cases. When you write [1.0, 2.0, 3.0] in GDScript, you get a Variant array, not a PackedFloat32Array. The rendering server expects the exact packed type that maps to the shader’s uniform type. A uniform float values[4] requires a PackedFloat32Array. A uniform vec3 positions[4] requires a PackedVector3Array. A uniform vec4 colors[4] requires a PackedColorArray. Passing the wrong type silently fails — the rendering server ignores the value and the shader keeps its defaults.

2. Global uniform vs local uniform confusion. Godot 4 supports both local uniforms (uniform float x;) and global uniforms (global uniform float x;). They look similar in shader code but are set through entirely different APIs. Local uniforms are per-material and set via material.set_shader_parameter(). Global uniforms are set via RenderingServer.global_shader_parameter_set(). If you declare a global uniform but try to set it via the material, nothing happens. If you declare a local uniform but try to set it via the RenderingServer, nothing happens.

3. Shared material resource. If multiple nodes reference the same ShaderMaterial resource and you set a uniform on one node’s material reference, all nodes change. This is expected behavior, but the problem arises when you think you’re setting a per-instance value but are actually modifying a shared resource. Conversely, if you duplicate the material at runtime, you might be setting values on the original instead of the duplicate.

4. Array size mismatch. If your shader declares uniform float values[8] and you pass a PackedFloat32Array with 6 elements, the behavior is undefined. Some GPU drivers pad the remaining elements with zeros, others ignore the entire upload. Always match the array size exactly.

The Fix

Step 1: Use the correct packed array type.

# Shader code (my_effect.gdshader)
shader_type canvas_item;

uniform float health_values[4];
uniform vec3 light_positions[4];
uniform vec4 palette_colors[8];

void fragment() {
    // Use uniform arrays in the shader
    float h = health_values[0];
    vec3 pos = light_positions[0];
    COLOR = palette_colors[0];
}
# GDScript - WRONG: regular arrays silently fail
func _ready() -> void:
    var mat = material as ShaderMaterial
    # These will NOT work:
    mat.set_shader_parameter("health_values", [1.0, 0.8, 0.5, 0.3])
    mat.set_shader_parameter("light_positions", [Vector3(1, 2, 3)])

# GDScript - CORRECT: use packed types
func _ready() -> void:
    var mat = material as ShaderMaterial

    # float[] requires PackedFloat32Array
    var health := PackedFloat32Array([1.0, 0.8, 0.5, 0.3])
    mat.set_shader_parameter("health_values", health)

    # vec3[] requires PackedVector3Array
    var positions := PackedVector3Array()
    positions.append(Vector3(1.0, 2.0, 3.0))
    positions.append(Vector3(4.0, 5.0, 6.0))
    positions.append(Vector3(7.0, 8.0, 9.0))
    positions.append(Vector3(10.0, 11.0, 12.0))
    mat.set_shader_parameter("light_positions", positions)

    # vec4[] / color[] requires PackedColorArray
    var colors := PackedColorArray()
    for i in 8:
        colors.append(Color(i * 0.1, 0.5, 1.0 - i * 0.1))
    mat.set_shader_parameter("palette_colors", colors)

The type mapping is: float[]PackedFloat32Array, vec2[]PackedVector2Array, vec3[]PackedVector3Array, vec4[] / color[]PackedColorArray, int[]PackedInt32Array.

Step 2: Ensure unique materials for per-instance values.

If you need different uniform values on different sprites or meshes that share the same shader, you must duplicate the material. Either enable “Resource > Local to Scene” in the Inspector or duplicate at runtime:

func _ready() -> void:
    # Create a unique material copy for this instance
    material = material.duplicate()
    var mat = material as ShaderMaterial
    var values := PackedFloat32Array([randf(), randf(), randf(), randf()])
    mat.set_shader_parameter("health_values", values)

Global Uniforms

If you want to set a value once and have all shaders that reference it update automatically, use global uniforms. These are declared differently in the shader and set through the RenderingServer:

In the shader: global uniform float time_scale;. In GDScript: RenderingServer.global_shader_parameter_set("time_scale", 0.5). You also need to register the global parameter in Project > Project Settings > Shader Globals, specifying its type. Forgetting the registration step causes the shader to compile with the uniform but never receive values from the RenderingServer.

Global uniforms do not currently support arrays in Godot 4.x. If you need a globally shared array, encode the data in a texture and pass that as a global uniform instead.

Why This Works

Godot’s rendering server communicates with the GPU driver via a command queue. When you call set_shader_parameter, the value is serialized into this queue. Packed arrays have a well-defined binary layout that maps directly to GPU uniform buffer memory. Regular GDScript arrays are variant containers with per-element type tags — they cannot be directly memcpy’d to GPU memory. The rendering server could theoretically convert them, but the current implementation simply rejects the wrong type rather than risk a slow implicit conversion every frame.

“If set_shader_parameter silently does nothing, check the type. It is almost always the type. PackedFloat32Array, not Array.”

Related Issues

If scalar uniforms update but the visual result looks wrong, the issue may be a coordinate space mismatch between GDScript and the shader — see Shader Screen Space Coordinates Wrong. If the shader compiles but produces unexpected output on certain hardware, check Shader Compatibility on Mobile GPUs for precision and driver-specific issues.

PackedFloat32Array, not Array. PackedVector3Array, not Array[Vector3]. The GPU does not negotiate types.