What mesh particles give and what they cost
A stock ParticleEmitter draws flat pictures. Roblox’s documentation calls it “an object that emits customizable 2D images (particles) into the world”. A Part-Icles particle is a real instance. It is a duplicate of your own Part, MeshPart or Model, and the Part_Icles module moves and reshapes it over its life.
A real instance can do things a flat picture cannot. It also costs more to keep alive. This page gives both sides, and shows how the module keeps the cost down.
The module is readable source. Everything on this page about the module can be checked by opening it.
What it makes possible:
- An effect built from true 3D shapes, lit by the scene and seen from every side.
- An informed choice between a mesh particle and a stock ParticleEmitter for each layer of an effect.
- A heavy effect made cheaper, by knowing where the module can take a shortcut.
What a real instance gives
Section titled “What a real instance gives”| A mesh particle can | In practice | Described in |
|---|---|---|
| Be any shape | The particle is a duplicate of your Part, MeshPart or Part with a SpecialMesh. It keeps its Material, Decals, Textures and SurfaceAppearance. | Part |
| Change size on each axis | Size has a graph each for X, Y and Z. A shockwave ring can widen without growing taller. | Part |
| Carry pictures that change by themselves | Each Decal on the particle has its own Transparency, Color and Brightness graphs, and its own flipbook. | Decals and flipbooks |
| Change shape during its life | The particle steps through a list of meshes. | Mesh Frames |
| Be a whole Model | The Model is duplicated, moved and scaled as one. | Model |
| Carry other effects | Emitters, stock ParticleEmitters, Trails, Beams and Lights inside the RenderTemplate travel with every particle. | Nesting |
| React to the world | On Hit can remove the particle, stop it or bounce it off a surface. | Events |
| Fire other effects | An event fires another emitter where the particle was made, hit something or ended. | Events |
| Be scripted one by one | An event script can move, recolour, resize, freeze, push or remove a single particle. | The event script and its payload |
| Follow a moving object | A particle in flight can follow a Part, an Attachment, a Model, a Bone or the camera. | Link |
| Play in place | In Animate mode the template itself plays, once or in a loop, with no duplicate made. | Emission |
| Change speed | A Timescale graph slows, freezes or speeds up an emitter. A script can do the same for one play. | Starting, stopping and waiting |
The same Rate, Lifetime and graphs also drive lights, sounds, beams, trails, highlights and screen effects. The Particiliary lists every kind.
The module puts no ceiling of its own on Rate or Lifetime. Its one limit is the Particle Cap.
What each particle costs
Section titled “What each particle costs”A stock particle is not an instance. A mesh particle is, and that sets its cost.
| Cost | Detail |
|---|---|
| One instance for each particle | A Model particle is a whole Model. A Rocks burst, a Lightning bolt and a Rope are many parts each. |
| One update by script, for each live particle, on every frame | All updates run in one script, one after another. |
| Property writes at each step | Position and rotation, Size, Transparency and Color are each written when a particle enters a new step. |
| A further write on every frame | Only for a particle that follows a link, or uses Orientation or Z-Offset. |
| The emitter’s settings are read at every emit | This is why an edit shows on the very next particle. |
| No saving for distance or for being off screen | A particle behind the camera is updated like any other. |
Part-Icles has published no measured comparison with the stock ParticleEmitter. Measuring shows how to compare the two on your own effect.
How the module keeps the cost down
Section titled “How the module keeps the cost down”Graphs are read in steps
Section titled “Graphs are read in steps”A particle’s life is divided into steps. Advanced ▸ Anim. Steps sets how many, and it starts at 100. The particle is moved, sized and coloured only on a frame where it enters a new step. On every other frame nothing is written.
A particle that lives 3 seconds at 100 steps is updated about 33 times a second, not 60. Between two steps the particle holds its pose. Nothing is blended in between.
| Case | Effect of Anim. Steps |
|---|---|
| A long life | Fewer steps mean fewer frames with work to do. |
| A life shorter than the steps allow | No saving. At 60 frames a second a life of 0.5 seconds has 30 frames. 100 steps and 30 steps both do work on all 30. |
Drag, Rotation Mode Speed, Dir. Mode Local, or a Model emitter | The motion is worked out once for each step, so the cost also rises with the number of steps. |
| A Beam emitter | Not used. The Blender, and every Beam graph that is not flat, are followed on every frame. |
| A Trail emitter | The six graph rows are read in steps. The Blender is followed on every frame. |
Advanced describes the row.
Motion is worked out in one calculation
Section titled “Motion is worked out in one calculation”For most particles the module does not walk through the steps one by one. It takes the distance travelled since the last update from the area under the Speed graph, in one calculation. Acceleration is added the same way. The path is the same at any frame rate.
This holds for Part and Attachment particles with Drag at 0, Rotation Mode OverLife, Reverse Motion off, and a Dir. Mode other than Local.
| Setting | What the module does in its place |
|---|---|
| Drag above 0 | Works through each step crossed, slowing the particle as it goes. |
Rotation Mode Speed | Works through each step crossed, adding up speed and turn. |
Dir. Mode Local | Works through each step crossed, and reads the travel direction again from the particle’s new rotation. |
| A Model emitter | Always works through each step crossed. |
| Reverse Motion | Works out the whole path once, when the particle is made, and plays it from the end. At most 500 steps. |
Flat graphs are read once
Section titled “Flat graphs are read once”When a particle is made, each of its number graphs is checked. A graph that is flat, with no random spread, is read once and its value kept. It is not read again for that particle.
| Emitter | Graphs covered |
|---|---|
| Part | Speed, Brightness, Transparency, Size, Rotation Speed, Displacement, Strength |
| Attachment | Speed, Rotation Speed, Displacement |
| Model | Speed, Scale, Rotation Speed, Displacement |
| Beam | Every graph row. A flat one is written once, when the beam is made, and not touched again. |
Three limits apply.
- The Color graph is read at every step, flat or not.
- A graph with a random spread is never flat. Each particle has its own offset.
- The values are still written at each step. The shortcut removes the reading, not the writing.
Turbulence left at 0 is skipped entirely.
Pool reuses finished particles
Section titled “Pool reuses finished particles”With Advanced ▸ Pool off, every particle is built when it is made and destroyed when it ends. With Pool on, a finished particle is hidden and kept, and the next emit takes it back out.
Pool starts off on every type except Rocks. Switch it on for an emitter that streams.
| Subject | Rule |
|---|---|
| How long an idle particle is kept | 5 seconds. Idle particles are cleared in small batches, so one may last a second or two longer. |
| How many idle particles one emitter keeps | About six times its Rate, plus 8. At Rate 10 that is 71. |
| How many are kept in all | 2048, across every emitter. |
| A burst larger than the limit | The surplus is destroyed. A burst of 200 at Rate 10 keeps 71 and rebuilds the rest each time. |
| An edit to the emitter | Its idle particles are discarded, so an old look is never reused. |
Part_Icles:Deactivate() | Every idle particle is destroyed. |
| Animate mode, Sound emitters, Blur, Bloom, ColorCorrection, Atmosphere | Not pooled. |
Pool pays off for a steady stream, where a particle is wanted again within 5 seconds. It does little for a rare, large burst.
Physics is switched off on the template
Section titled “Physics is switched off on the template”Transform sets these on the RenderTemplate once. Every particle is a duplicate of the template, so every particle has them.
| Property | Set to | What it means for a particle |
|---|---|---|
| Anchored | On | It is not moved by physics. |
| CanCollide | Off | Nothing bumps into it. |
| CanQuery | Off | Raycasts pass through it. |
| CanTouch | Off | It fires no Touched events. |
| Massless | On | It adds no weight to anything. |
| CastShadow | Off | It casts no shadow. |
| Locked | On | It cannot be clicked in the viewport. Not set on the parts of a Model emitter. |
The settings are yours to change. Switch CastShadow on in the RenderTemplate, and every later particle casts a shadow.
On Hit does not need CanQuery. It casts a ray from where the particle was to where it is now, and tests that ray against the world.
Live particles are kept in a folder named EmittedPartsUsingPart_icle. It sits under the camera of the machine that made the particles. On a player’s machine that is the player’s own camera. Neither the folder nor the particles are saved with the place. Emit Into puts an emitter’s particles somewhere else.
Idle frames write nothing, and removal is cheap
Section titled “Idle frames write nothing, and removal is cheap”- On a frame where a particle’s step has not changed, the module writes nothing to it.
- The update of a Part, Attachment or Model particle builds no table.
- A finished particle is taken off the live list by moving the last entry into its place. The list is never shifted.
- The first values of every graph are set before the particle is shown. A particle never appears for one frame with the wrong size or colour.
- An emitter that is switched off, with no particles in flight, costs nothing on each frame.
The Particle Cap
Section titled “The Particle Cap”The module stops making particles when the number alive reaches the cap. The cap starts at 1000. Particles in their Linger time count too.
At the cap a new emit is skipped. It is not held back for later. At most once a second, the Output window reads:
[Part-Icles] active particle cap (N) reached - new emits skipped (user Module code still runs).In the editor, ≡ ▸ Settings ▸ Particle Cap sets the cap. In the game, a script sets Part_Icles.MAX_ACTIVE_PARTICLES. A Rocks burst, a Lightning bolt, a Rope and a Model each count as one.
Emission and motion do not depend on the frame rate
Section titled “Emission and motion do not depend on the frame rate”A stream keeps count of time, not of frames. On each frame it emits once for every whole 1 / Rate seconds that have passed, and carries the remainder forward. An emitter at Rate 40 makes 40 particles a second at 30 frames a second and at 144.
Steps are also counted from time. A particle follows the same path, at the same pace, on a slow machine and a fast one.
On a player’s machine the module updates in the render step, after the camera has moved. On a server it updates on Heartbeat.
Nested emitters are not duplicated with their parent
Section titled “Nested emitters are not duplicated with their parent”A RenderTemplate can hold other emitters. When a particle is made, its duplicate is built without them. Each nested emitter then fires in the ordinary way, with the new particle as the thing it follows. The settings of a nested emitter are never duplicated for each parent particle.
Emitters that make one whole thing
Section titled “Emitters that make one whole thing”| Emitter | How it is kept cheap |
|---|---|
| Rocks | The whole path of every chunk is worked out once, 8 chunks on each frame. After that, each frame makes one grouped move of all the chunks. Nothing is written once every chunk is at rest. |
| Lightning | The shape of a bolt is rolled again at its Jitter Rate, not on every frame. All its segments are placed in one grouped move. |
| Rope | The rope is advanced in fixed steps of one sixtieth of a second, at most three on a frame. All its segments are placed in one grouped move. |
| Camera Shake | No instance is made. Every live shake is added up, and the camera is written once on each frame. |
| Distortion | One shared plane serves every distortion that is alive. |
Events
Section titled “Events”- An event script is loaded once and kept. It is loaded again only when its text changes.
- With no event switched on, a particle pays for one check.
- On Hit casts one ray for each particle on each frame. The ray is skipped when the particle has moved less than 0.05 studs.
- On Hit ▸ HitTick spaces the hit tests out. The next ray then covers the whole distance since the last one. A particle that has moved further than half its own diagonal is tested at once.
- At most 256 events run on one frame. The rest are dropped, and the Output window says so.
Textures loaded ahead of time
Section titled “Textures loaded ahead of time”With Advanced ▸ Preload Texture on, the module loads the emitter’s textures when it starts, and holds them. The first particle then shows its picture at once. A texture shared by several emitters is held once. Advanced describes the row.
What Roblox says of the stock ParticleEmitter
Section titled “What Roblox says of the stock ParticleEmitter”Roblox’s page on particle emitters states the costs and the limits of the stock emitter.
Particle size can impact performance due to fill-rate. The more pixels particles occupy on a player’s screen, the more costly it is on the GPU.
Particle count can impact performance due to overdraw, especially when particles are overlapping. The more layers of transparent effects on screen, the more costly it is on the GPU.
A single particle emitter can create up to 400 particles per second (100 per second on mobile).
Particles have a maximum lifetime of 20 seconds.
Choosing between them
Section titled “Choosing between them”| The effect | Use |
|---|---|
| Hundreds or thousands of small particles alive at once: rain, snow, dust, dense smoke | A stock ParticleEmitter. Every mesh particle costs an instance and an update by script. |
| A flat picture that faces the camera and only changes size, colour and transparency | A stock ParticleEmitter. Nothing a real instance gives is being used. |
| An ambient effect that runs all the time, often far away or off screen | A stock ParticleEmitter. The module makes no saving for distance. |
| A shape with depth: a shard, a ring, a slash, a rock, a shockwave dome | A mesh particle. |
| A particle that must bounce, stop on a surface or fire something where it lands | A mesh particle, with On Hit. |
| A particle that carries a light, a trail or a second effect | A mesh particle, with the passengers in its RenderTemplate. |
| A particle a script must reach | A mesh particle. |
| A few strong shapes with a cloud of small sprites around them | Both. |
The two work together. A stock ParticleEmitter placed in a RenderTemplate fires for every mesh particle. A stock ParticleEmitter in a Folder of emitters fires with the rest. The plugin also edits stock ParticleEmitters as they stand. See Editing stock ParticleEmitters, Trails and Beams.
Settings that change the cost
Section titled “Settings that change the cost”| To get | Do this |
|---|---|
| Fewer particles alive at once | Lower Rate, the Emit count, Lifetime or Linger. A stream keeps about Rate times Lifetime alive. |
| Less building and destroying | Switch Pool on for an emitter that streams. |
| Fewer updates on a long-lived particle | Lower Anim. Steps until stepping shows, then raise it a little. |
| The one-calculation motion | Keep Drag at 0, Rotation Mode at OverLife, and Dir. Mode away from Local, where the look allows. |
| Graphs read once | Leave a graph flat, with no random spread, where the look allows. |
| Fewer hit tests | Raise On Hit ▸ HitTick. Switch On Hit off where it is not needed. |
| Less work for each parent particle | Lower the Emit count of nested emitters. Counts multiply. |
The Optimization indicator gives an estimate worked out from the settings of the selected emitters. It is not a measurement. Exercise 20 uses it on a finished effect. Performance lists symptoms and actions.
Measuring
Section titled “Measuring”Roblox’s MicroProfiler shows where each frame’s time goes.
| Step | Control | Action | Result |
|---|---|---|---|
| 1 | Studio, with the game running | Press Ctrl+F6, or ⌘F6 on a Mac | The MicroProfiler opens. |
| 2 | Your script | Fire the effect made of mesh particles | The MicroProfiler shows the frame time during the effect. |
| 3 | Your script | Fire the same look built with a stock ParticleEmitter | The MicroProfiler shows the frame time during that effect. |
| 4 | The MicroProfiler | Compare the two | Each frame shows which part of the engine took the time. |
On a player’s machine the module updates its particles in the render step. Roblox’s tag table lists scripts bound to the render step under fireBindToRenderSteppedCallbacks, and stock particles under updateParticles.
Measure on the hardware your players use.
Limits
Section titled “Limits”- The module does not skip particles that are off screen or far from the camera.
- The module updates every particle in one script. It does not spread the work over several.
- Part, Attachment and Model particles are each moved by their own write. Only Rocks, Lightning, Rope and Distortion use a grouped move.
- The Color graph is read at every step, even when it is flat.
- Part-Icles publishes no benchmark against the stock ParticleEmitter.