Quick answer: Particles disappear on mobile because the device GPU cannot handle the particle texture size, the active particle count, or both. Reduce particle textures to 128x128 or smaller, keep active particle count under 500, and verify WebGL support on the target device. For guaranteed compatibility, use a sprite-based particle fallback instead of the Particles plugin.
Your particle effects look great in the desktop browser preview — explosions, fire trails, sparkles, all rendering perfectly. You export to mobile and test on a phone. The particles are gone. No error, no warning, no crash. Every other visual element renders fine, but particles are completely invisible. This is a GPU capability mismatch, and it affects a significant number of mobile devices.
The Symptom
You have one or more Particles objects in your Construct 3 project. In the desktop browser preview, they render correctly — particles spawn, move, fade, and destroy as configured. When you export the project and run it on a mobile device (phone or tablet, iOS or Android), some or all particle effects are invisible.
The rest of the game works fine. Sprites render, animations play, text displays, and audio plays. Only the Particles plugin output is missing. In some cases, the particles appear briefly and then vanish. In other cases, they never appear at all. On higher-end phones the particles may work but on budget or older devices they do not.
There are no console errors related to particles. The game does not crash or slow down noticeably (because the invisible particles are not consuming draw calls). If you add a debug text showing the particle count, it may report that particles exist — they are being created and tracked, just not rendered.
What Causes This
There are five causes, all related to the gap between desktop and mobile GPU capabilities:
1. Particle texture exceeds mobile GPU texture size limits. Desktop GPUs typically support textures up to 8192x8192 or 16384x16384 pixels. Mobile GPUs often cap at 2048x2048 or 4096x4096. If your particle image is large (for example, a 512x512 high-detail sparkle), and Construct 3 packs it into a sprite sheet that exceeds the device’s maximum texture size, the entire texture fails to load and the particles render as invisible. This is the most common cause on older Android devices.
2. Too many active particles overwhelm the GPU. Each active particle is a separate draw element. Desktop GPUs handle thousands of particles with ease. Mobile GPUs, especially on budget devices, struggle above 500-1000 active particles. When the GPU cannot keep up, it may silently drop particles rather than slow down — the browser’s compositing layer skips the particle draw calls to maintain frame rate.
3. WebGL context limitations on mobile browsers. Some mobile browsers, particularly older versions of Android WebView or Safari on older iOS versions, have WebGL implementations that do not fully support certain blend modes or shader operations used by the Particles plugin. The particles may be drawn with the wrong blend mode (rendering as black rectangles against a dark background) or not drawn at all.
4. Memory pressure causing texture eviction. Mobile devices have limited GPU memory. When the game uses many textures (sprite sheets, tilemaps, backgrounds, UI), the GPU may evict less-recently-used textures from memory to make room. Particle textures, being small and frequently changing, are prime candidates for eviction. Once evicted, the particles render as invisible until the texture is reloaded, which may not happen automatically.
5. Power saving mode throttles GPU performance. Many mobile devices have aggressive power-saving modes that reduce GPU clock speed. Particle rendering, which requires frequent small draw calls, is disproportionately affected by reduced GPU throughput. The browser may decide to skip particle rendering entirely to maintain the target frame rate for the rest of the game.
The Fix
Step 1: Reduce particle texture size. Keep particle images as small as possible. A 64x64 or 128x128 texture is sufficient for most particle effects. Resize your particle image in the Construct 3 image editor.
// Recommended particle texture sizes by effect type:
// Sparkle / star: 16x16 or 32x32
// Smoke puff: 64x64 or 128x128
// Fire particle: 32x32 or 64x64
// Explosion debris: 32x32
// Rain / snow: 8x8 or 16x16
// Trail dot: 8x8 or 16x16
// AVOID: 256x256+ particle textures on mobile
// The detail is wasted at particle scale and
// consumes GPU memory that could be used elsewhere.
Step 2: Limit active particle count. Reduce the particle rate and shorten the lifetime to keep the total number of simultaneously active particles manageable.
// Particle property recommendations for mobile:
// Rate: 30-50 per second (not 100+)
// Lifetime: 0.5 - 2 seconds (shorter = fewer active)
// Max active: rate * lifetime
// 50/sec * 1sec = 50 active (safe)
// 100/sec * 3sec = 300 active (borderline)
// 200/sec * 5sec = 1000 active (too many for mobile)
// Adjust rate based on platform at runtime:
System: On start of layout
Browser: Is on mobile
→ ExplosionParticles: Set rate to 30
→ TrailParticles: Set rate to 15
System: Else
→ ExplosionParticles: Set rate to 100
→ TrailParticles: Set rate to 50
Step 3: Verify WebGL support and handle fallbacks. Check at runtime whether the device supports the rendering features your particles need.
// Check WebGL support at startup using scripting
const canvas = document.createElement("canvas");
const gl = canvas.getContext("webgl2")
|| canvas.getContext("webgl");
if (gl) {
const maxTexSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
console.log("Max texture size:", maxTexSize);
if (maxTexSize < 4096) {
// Low-end device - reduce particle quality
runtime.globalVars.UseSimpleParticles = 1;
}
} else {
// No WebGL - disable particles entirely
runtime.globalVars.UseSimpleParticles = 1;
}
Step 4: Build a sprite-based particle fallback. For effects that must work on every device, replace the Particles plugin with manually managed Sprite instances. This is more work but gives you complete control and guaranteed rendering.
// Sprite-based particle system (event sheet)
// Create a Sprite called "FXParticle" with:
// - A small image (32x32 soft circle)
// - Instance variables: VelX, VelY, Life, MaxLife
// - Initial visibility: Invisible (set visible on spawn)
// Spawn particles:
System: Every 0.03 seconds
GlobalVar: UseSimpleParticles = 1
→ System: Create object FXParticle
on layer "Effects"
at (Player.X, Player.Y)
→ FXParticle: Set VelX to random(-100, 100)
→ FXParticle: Set VelY to random(-200, -50)
→ FXParticle: Set Life to 0
→ FXParticle: Set MaxLife to random(0.5, 1.5)
→ FXParticle: Set visible
// Update particles every tick:
System: For Each FXParticle
→ FXParticle: Set X to
Self.X + Self.VelX * dt
→ FXParticle: Set Y to
Self.Y + Self.VelY * dt
→ FXParticle: Add dt to Life
→ FXParticle: Set opacity to
1 - (Self.Life / Self.MaxLife)
// Destroy expired particles:
FXParticle: Life ≥ FXParticle.MaxLife
→ FXParticle: Destroy
Step 5: Use the scripting API for a robust particle system. For maximum control over particle behavior and rendering, implement the particle system entirely in JavaScript.
// Scripting API - managed particle pool
const POOL_SIZE = 200;
let particlePool = [];
function initParticlePool(runtime) {
for (let i = 0; i < POOL_SIZE; i++) {
const p = runtime.objects.FXParticle.createInstance(
"Effects", -1000, -1000
);
p.isVisible = false;
p.instVars.Active = 0;
particlePool.push(p);
}
}
function spawnParticle(x, y, vx, vy, life) {
const p = particlePool.find(
p => p.instVars.Active === 0
);
if (!p) return; // pool exhausted - skip gracefully
p.x = x;
p.y = y;
p.instVars.VelX = vx;
p.instVars.VelY = vy;
p.instVars.Life = 0;
p.instVars.MaxLife = life;
p.instVars.Active = 1;
p.isVisible = true;
p.opacity = 1;
}
function updateParticles(runtime, dt) {
for (const p of particlePool) {
if (p.instVars.Active === 0) continue;
p.x += p.instVars.VelX * dt;
p.y += p.instVars.VelY * dt;
p.instVars.Life += dt;
const t = p.instVars.Life / p.instVars.MaxLife;
p.opacity = 1 - t;
if (t >= 1) {
p.isVisible = false;
p.instVars.Active = 0;
p.x = -1000;
}
}
}
Step 6: Test on real target devices. Emulators and desktop browser mobile mode do not accurately represent mobile GPU limitations. Always test particle effects on actual devices.
// Add a runtime particle diagnostics display
// to help identify issues during device testing
System: Every 1.0 seconds
→ DebugText: Set text to
"FPS: " & round(fps) & newline &
"Particles: " & ExplosionFX.Count & newline &
"FX Sprites: " & FXParticle.Count & newline &
"Platform: " & Browser.Platform
Why This Works
The fundamental issue is that desktop and mobile GPUs have vastly different capabilities, and Construct 3’s Particles plugin does not automatically adapt to these differences.
Smaller textures stay within mobile GPU texture size limits and consume less GPU memory. A 32x32 particle texture uses 4 KB of GPU memory versus 1 MB for a 512x512 texture. With hundreds of active particles, this difference determines whether the GPU can hold all textures in memory.
Lower particle counts reduce the number of draw calls per frame. Mobile GPUs are optimized for fewer, larger draw operations (like sprite batching). Hundreds of tiny individual particle draws overwhelm the GPU’s command buffer, causing the browser to skip draws to maintain frame rate.
Sprite-based fallbacks work because regular Sprite objects use Construct 3’s standard rendering pipeline, which includes sprite batching. Multiple sprites using the same image are drawn in a single batch call, making them far more efficient than individual particle draw calls on mobile GPUs.
Object pooling in the scripting approach prevents the overhead of creating and destroying objects every frame. Creating and destroying DOM-backed objects triggers garbage collection, which causes frame hitches on mobile. Recycling from a fixed pool eliminates GC pressure entirely.
"Never trust desktop preview for mobile particle performance. A particle system that runs at 60fps on your development machine can render zero particles on a phone that is three years old. Test on real devices early and often, and always have a fallback plan."
Related Issues
If your particle spawning loop is skipping some spawn events, see Fix: Construct 3 For Each Loop Skipping Instances. For particles attached to platform characters that fall through floors, check Fix: Construct 3 Platform Behavior Falling Through Platforms. If you are loading particle configurations from a remote API and getting errors, see Fix: Construct 3 AJAX Request CORS Error. And if particle state is not preserved after loading a save, see Fix: Construct 3 Save/Load System Not Restoring State.
Test on real phones. Emulators lie about GPU limits.