Quick answer: Assign a ParticleProcessMaterial to the process_material property. Without it, GPUParticles2D has no simulation rules and emits nothing. Also check that amount is greater than zero, emitting is true, and the visibility_rect is large enough to contain the particle spread.

Here is how to fix GPUParticles2D not emitting in Godot. You added a GPUParticles2D node to your scene, pressed play, and see nothing. No particles, no errors, no warnings. The node exists in the scene tree, it is visible, and emitting is checked. But the screen shows absolutely nothing. This is almost always a missing process material, though several other settings can cause the same blank result.

The Symptom

A GPUParticles2D node shows no particles at all. The node is active and emitting is enabled. In the editor, you might see a small warning icon on the node, or you might not. The particle count in the debugger shows zero active particles. No errors appear in the output panel.

In some cases, particles appear in the editor preview but vanish at runtime. In other cases, they appear for the first frame and then disappear. If you move the camera, particles might flash briefly and then vanish again — this is the visibility_rect culling issue.

What Causes This

1. No process_material assigned. GPUParticles2D delegates all particle simulation (velocity, color, size, gravity) to a ParticleProcessMaterial. Without one, the GPU has no shader to run and emits nothing. Unlike CPUParticles2D which has built-in defaults, GPUParticles2D requires an explicit material.

2. Amount is zero. The amount property defaults to 8, but if you accidentally set it to 0, no particles are allocated. Since there are no particles to simulate, nothing appears.

3. visibility_rect is too small. The visibility_rect is used for frustum culling. If particles travel outside this rectangle, Godot considers the entire particle system off-screen and stops rendering it. The default rect is small and centered on the node’s origin.

4. one_shot consumed. If one_shot is true and the particle lifetime has elapsed before the camera sees the node, the burst has already finished. The particles emitted and died before you could see them.

The Fix

Step 1: Assign a process material.

# Assign a process material in code
func _ready():
  var particles = $GPUParticles2D
  if particles.process_material == null:
    var mat = ParticleProcessMaterial.new()
    mat.direction = Vector3(0, -1, 0)
    mat.initial_velocity_min = 50.0
    mat.initial_velocity_max = 100.0
    mat.gravity = Vector3(0, 98, 0)
    particles.process_material = mat
    print("Process material assigned")

Step 2: Verify amount and emitting state.

func _ready():
  var p = $GPUParticles2D
  print("Amount: %d" % p.amount)
  print("Emitting: %s" % p.emitting)
  print("One shot: %s" % p.one_shot)
  print("Material: %s" % p.process_material)

  # Force restart if one_shot already consumed
  if p.one_shot:
    p.restart()

Step 3: Expand the visibility_rect. Select the GPUParticles2D node in the editor. In the Inspector, expand Visibility and set the Rect to cover the full area where particles might travel. For an explosion that spreads 200 pixels in each direction, use Rect2(-200, -200, 400, 400):

# Set a generous visibility rect
$GPUParticles2D.visibility_rect = Rect2(-500, -500, 1000, 1000)

“GPUParticles2D without a process material is like a projector with no film. The hardware is ready, but there is nothing to project. Assign the material and everything starts working.”

Why This Works

GPU particles run entirely on the graphics card via compute shaders. The ParticleProcessMaterial generates the shader that controls every aspect of simulation: spawn position, velocity, acceleration, color over lifetime, size curves, and more. Without this shader, the GPU allocates the particle buffer but never writes any position data to it. The particles exist in memory but at position (0,0) with zero size, making them invisible. Assigning the material provides the simulation rules that bring them to life.

The visibility_rect culling is a performance optimization. Godot checks whether the rect overlaps the camera’s viewport before submitting draw calls. If it does not overlap, the entire particle system is skipped. This saves GPU time for off-screen effects but causes confusion when particles travel far from their origin node.

Related Issues

If GPU particles work on desktop but fail on mobile, the device may not support compute shaders. Use CPUParticles2D as a fallback. You can convert a GPUParticles2D to CPUParticles2D via the editor’s Particle menu.

If particles appear but look wrong (all white, no texture), assign a texture to the GPUParticles2D’s texture property separately from the process material.

No process material means no particles. Assign one, expand the visibility rect, and check the amount.