Quick answer: Replace GPUParticles2D/3D with CPUParticles2D/3D on mobile builds. Set the renderer to Compatibility (GLES 3) for the broadest device support. Cap amount around 200–500 per emitter and lifetime under 2s for safety.

Particles work great on PC. Build to Android — they pop in and out, count varies frame to frame, sometimes nothing emits at all. GPUParticles relies on compute paths that not every mobile GPU supports cleanly.

The Symptom

GPUParticles flicker, drop frames worth of particles, or render zero-duration. Sometimes works on Pixel devices, broken on Samsung. Editor preview is normal. CPUParticles in the same scene work fine.

What Causes This

GPUParticles compute on the GPU via shader storage buffers. Mobile drivers vary in how reliably they support the necessary features:

The Compatibility renderer (GLES 3) silently falls back to a slower path that doesn’t handle GPU particles consistently.

The Fix

Step 1: Switch to CPUParticles. Right-click your GPUParticles2D/3D → Convert to CPUParticles2D/3D. Godot copies most settings over; review the result. Process Material maps to CPUParticles fields directly.

CPUParticles2D:
  amount:        200
  lifetime:      1.5
  emission_shape: Sphere
  initial_velocity: 100
  scale_amount_curve: ...

Step 2: Pick the right renderer. Project Settings → Rendering → Rendering Device → Driver. For Android target, set rendering_method.mobile = mobile (Vulkan) for newer devices, compatibility (GLES) for the broad market. Test on the lowest-spec device you support.

Step 3: Cap counts and durations. Mobile RAM is finite. Lifetime × emission rate = max alive count. Keep this under 500 unless you have profiled.

Per-Platform Configs

Use platform branches in your spawning code:

extends Node

@export var gpu_scene: PackedScene
@export var cpu_scene: PackedScene

func spawn() -> void:
    var scene = cpu_scene if OS.has_feature("mobile") else gpu_scene
    add_child(scene.instantiate())

One asset for desktop, one for mobile. The OS feature flag picks at runtime.

Verifying on Device

Run a development export with verbose logging. OS.has_feature("mobile") + RenderingServer.get_video_adapter_name() in a print. Confirm the renderer name matches your expectation; switch if needed.

“CPUParticles on mobile. Compatibility renderer for the long tail. Cap counts. Particles render every frame.”

Related Issues

For shaders failing to compile on mobile, see shader compile. For Godot mobile crash, see Android launch crash.

CPU on mobile. Compatibility renderer. Particles steady.