Quick answer: Modify node.process_material instance, not the asset. Use .duplicate() for per-emitter variation.

Set custom uniform on the asset; all emitters change. Or modify per-emitter; only one updates because they shared the asset.

The Fix

# Per-emitter uniform
@onready var p: GPUParticles2D = $Emitter

func _ready():
    var mat = p.process_material.duplicate()
    p.process_material = mat
    mat.set_shader_parameter("wind_speed", 5.0)

Duplicate creates a unique material instance. set_shader_parameter then targets only this emitter.

Verifying

Set uniform per-emitter. Each one’s wind speed differs. Without duplicate: all share asset.

“Duplicate. Per-instance uniforms.”

Related Issues

For shader uniform array, see array. For shader instance uniform, see instance uniform.

Duplicate. Set per instance.