Skip to content

Nesting

A fireball isn’t one thing. It’s a glowing sphere, a textured beam trailing behind it, and a soft halo of light cast on the surrounding world. Three visuals, all moving together, all timed to the same lifetime, all dying at the same moment.

You could build that with three coordinated emitters triggered by a script. But the plugin gives you a more direct way: nest the three transformed items as children of a Model, and the engine emits them all together as a composite particle. One Particle:Emit() call produces a sphere-particle that already has its own beam-particle and its own light-particle attached.

That’s nesting. It’s the difference between a particle that’s a single Part and a particle that’s a coordinated cluster of visual elements bound to one lifetime.

Nesting fits any case where two or more visual behaviours should be tied together as a unit:

  • FireballModel containing a textured Sphere Part, a Beam between two of its Attachments for a flame trail, and a PointLight for the world-glow. One emit triggers all three.
  • Each-particle-leaves-trailPart whose RenderTemplate contains an Attachment with a Trail parented to it. Every Part-particle drags a tiny streak as it flies.
  • Each-particle-glowsPart whose RenderTemplate contains a PointLight. Every Part-particle emits real light into the scene around it.
  • Sub-burstPart whose RenderTemplate contains another Part emitter, with a higher EmitCount. Each parent particle spawns a secondary cloud of three or five sub-particles when it’s born.

Each pattern composes basic emitters into a richer effect. The plugin’s job is to discover the nested emitters when the parent emits, trigger their emission, and clean them up when the parent’s lifetime ends. The user’s job is to mark the inner items as Transformed and shape their properties.

When a parent emitter emits, the plugin clones the parent’s RenderTemplate (or for Model, the entire subtree). Then, on the cloned tree, it does this:

for _, desc in visualPart:GetDescendants() do
if desc:GetAttribute("Transformed") then
-- this is a nested emitter — fire it
end
end

GetDescendants() walks every node in the cloned subtree, depth-first. The filter is the Transformed attribute (set by the Transform tool). Any descendant that has Transformed = true is recognised as a nested emitter and gets triggered. Descendants without that flag are just visual children — they ride along with the parent particle, but they don’t emit anything of their own.

The walk is one-shot per parent emission. For a Part parent emitting at Rate = 30, the walk runs thirty times per second; for Model with a deeply nested hierarchy, the walk visits every descendant each time. The cost is roughly O(descendants × emission rate) — usually negligible, but worth knowing if you’re building deeply nested structures with high emission rates.

For each nested emitter discovered, the plugin calls its internal EnableEmit flow with three relevant attributes:

  • EmitCount — how many one-shot bursts to emit (default 1 for nested case).
  • EmitDuration — seconds of continuous emission after the burst (default 0, meaning no continuous emission).
  • EmissionMode"Emit" (per-emission clones) or "Animate" (singleton in-place animation).

The combinations cover the common cases:

  • EmitCount = 1, EmitDuration = 0 — one burst on parent emit. Most common for nested PointLights and Beams (the visual is the burst itself).
  • EmitCount = 5, EmitDuration = 0 — five bursts on parent emit. Sub-burst pattern.
  • EmitCount = 1, EmitDuration = 0.3 — one burst plus 0.3 seconds of continuous emission. Useful for trailing effects.

EmitDelay on a nested emitter is suppressed for the nested trigger. When the parent emits and the engine walks the cloned tree to fire its transformed descendants, each descendant’s own EmitDelay is forced to 0 for that nested fire — the cascade is parent-driven, not delay-stacked. Animate-mode paths snapshot the original delay and restore it after the call so the authored value isn’t permanently zeroed.

The parent’s own EmitDelay still gates when the parent emits. If you call :AbsoluteEmit(parent) with EmitDelay = 2 on the parent, the whole composite waits 2 seconds, then the parent fires and every nested transformed descendant fires together. There’s no per-layer staggering. For staggered timing, drive the nested emitters from a script with explicit task.wait between calls instead.

Default-zero handling for nested Parts and PointLights

Section titled “Default-zero handling for nested Parts and PointLights”

Part, Attachment, Model, and PointLight emitters all default EmitCount to 0 at Transform — the assumption is you’ll trigger emission from a script, not as part of a parent’s automatic firing. For the nested case, EnableEmit auto-bumps EmitCount to 1 whenever both EmitCount and EmitDuration are at their defaults, so the cloned subtree actually fires when its parent emits regardless of the parent’s type (Part, Attachment, or Model).

Beam doesn’t even need the bump: Transform sets EmitCount = 1 on Beam specifically.

Trail doesn’t go through this path: a transformed Trail uses a different storage path with no Configuration child, and the runtime fires it through a Trail-specific helper that reads EmitDuration directly, not EmitCount.

Particles emitted by nested emitters automatically follow the parent particle as it moves. The mechanism is structural: nested emitters are descendants of the parent’s cloned visual, so when the engine updates the parent’s CFrame each frame (driven by the parent’s Speed, Acceleration, Drag, etc.), the descendants move with it for free, courtesy of Roblox’s parent-child CFrame composition.

Three shapes of inheritance:

  • Part inside Part / Model: Each frame, the parent’s CFrame is set via :PivotTo() (Model) or assigned to the parent BasePart’s CFrame (Part). Descendants follow because they’re children. No explicit tracking needed.
  • Attachment inside Part: Same — the cloned Attachment is a child of the cloned Part, so it inherits the Part’s frame. Particles emitted by the nested Attachment spawn at the Attachment’s now-current world position (which is the Part’s position composed with the Attachment’s local offset).
  • Beam inside Part: A nested Beam keeps its Attachment0/Attachment1 references intact at clone time. If the Beam’s attachments are children of the parent Part (most common), the Beam moves with the Part. If the attachments are external (sometimes set up for cross-effect cases), the nested Beam stays welded to wherever those attachments are, regardless of the parent’s motion.

The Roblox CFrame model handles the heavy lifting. The plugin doesn’t manually re-position nested clones each frame — it only positions the parent, and Roblox propagates the change.

Scale is the one inherited property the plugin handles explicitly, because Roblox’s parent-child relationship doesn’t propagate it. Model:ScaleTo() multiplies every descendant’s Size once, but that’s a single call — if the parent’s Scale graph animates over the lifetime, the nested emitters’ sizes need to follow.

So the plugin tracks each nested emitter’s parent at emit time and, every frame, samples the parent’s Scale graph and applies it as a multiplier on the nested particle’s size properties. You don’t see this happen — from your end, you set a Scale graph on the parent Model, the nested Beam/Part-particle sizes track it.

What gets multiplied:

  • Nested Part / Attachment particlesSizeX, SizeY, SizeZ each frame.
  • Nested Beam particlesWidth0, Width1, CurveSize0, CurveSize1, and Segments each frame. TextureLength is divided by the same factor — the value is “studs per texture tile,” so dividing keeps the visible tile density proportional to the beam’s now-larger apparent length.

The plugin doesn’t pre-scale the config at emit time; it applies the scale at runtime. The reason: if you re-emit with the same source after a previous emission, pre-scaling would compound (the config would be scaled-then-scaled-again), producing wrong sizes. Runtime-only scaling keeps the configs clean and re-emissions consistent.

The scale-inheritance map is one level deep. If a nested emitter inside a Model has its own nested emitter (a grandchild emitter), the grandchild does NOT inherit the Model’s scale through the intermediate. The grandchild emits at its own configured size, untouched by any parent.

For most practical effects, one-level inheritance is enough — most nesting is two layers (parent emitter + immediate children), not three. If you find yourself needing deeper inheritance, consider whether the structure can be flattened (move the grandchildren up to be direct children of the Model) or whether a script-driven approach is cleaner.

When the parent particle’s Lifetime ends, the engine destroys the parent’s cloned visual via Instance:Destroy(). Roblox’s natural parent-destruction takes care of everything underneath: every descendant of the destroyed visual is also destroyed automatically.

For nested emitters, this means:

  • Nested Part-particle clones inside the parent’s hierarchy are destroyed when the parent dies.
  • Nested Beam and PointLight clones similarly.
  • Particles that the nested emitters emitted into a separate EmitParent (not inside the parent’s hierarchy) live independently — their lifetimes are not tied to the parent’s. This is rarely what users want, so leave EmitParent empty on nested emitters.

The plugin marks every emitted clone with a _PartIcleEmit attribute. You can use it to spot plugin-emitted clones in Studio’s Explorer.

If the parent is in Animate mode with AnimateLoop = true, the engine doesn’t destroy the parent’s visual at the end of the cycle — it resets it and replays. The parent’s own state is re-read from its source instance at the moment of restart, so live edits to the parent take effect on the next loop.

Nested emitters, by contrast, are not snapshot-and-restored across loops. Whatever value a nested emitter holds at the end of cycle 1 is the value cycle 2 starts from. If a script mutated a nested emitter’s Speed graph during cycle 1, cycle 2 carries that mutation forward. Avoid mutating nested emitter properties from scripts during an active Animate cycle if you need cycles to be identical.

The takeaway: in Animate-mode parent + nested-emitter setups, edit the source properties via the panel before emitting, not at runtime mid-cycle.

There’s no built-in limit on nesting depth. A nested emitter can contain its own nested emitter, which can contain another, and so on.

In practice, two layers is the common case. Three layers is rare but works. Four or more layers usually means the structure could be flattened or restructured to be cleaner.

The cost of deep nesting is mostly the discovery walk (each parent emission walks all descendants of its cloned subtree). For high emission rates, that walk’s cost adds up. A 5-layer nested structure with a 30-particles-per-second parent does 30 walks of a 5-layer-deep tree per second. Not pathological, but not free either.

Setup:

  1. Create a Model in the workspace.
  2. Set its PrimaryPart to a Sphere Part (the visible orb, with a fire texture).
  3. Add a Beam as a child of the orb, with Attachment0 at the orb’s centre and Attachment1 at a position that will trail behind during motion (or use a script to update the trailing attachment).
  4. Add a PointLight as a child of the orb (or directly under the Model).
  5. Transform the Model. The Sphere, Beam, and PointLight are all auto-detected as nested emitters.
  6. Set Speed and Acceleration on the Model to give the fireball motion.

Result: each Particle:Emit() produces a fireball-particle that is a sphere with a trailing beam and a glowing world-light, all moving together as one unit.

Setup:

  1. Create a Part in the workspace.
  2. Inside the Part, add an Attachment and Transform that Attachment.
  3. Inside the Attachment, add a Trail instance with appropriate properties (Color, Transparency, WidthScale graphs). The Trail uses two Attachments as its endpoints — these can be inside the Part or fixed siblings.
  4. Transform the parent Part.
  5. Set the parent Part’s Speed and EmissionDirection to give it motion.

Result: each emitted Part-particle carries a small Trail that draws a streak as the Part flies. Useful for missile trails, comet tails, glow streaks behind individual sparks.

Setup:

  1. Create a Part, transform it.
  2. Inside the Part, add a PointLight and Transform it.
  3. Set the PointLight’s Range, Brightness, Color graphs to taste.

Result: each emitted Part-particle has its own PointLight emitting real light into the scene. Combined with high Brightness/Range, you get fireflies, magic sparks that illuminate their surroundings, glow-in-the-dark effects.

Setup:

  1. Create a parent Part, transform it.
  2. Inside the parent, add another Part (a smaller “sub-particle”), transform it.
  3. Set the inner Part’s EmitCount = 5, Lifetime = 0.3, Speed higher than the parent’s.

Result: each parent-particle, on emission, immediately produces five sub-particles outward. The parent flies along its own trajectory; the sub-particles fly outward from wherever the parent was at that moment, each with their own short lifetime.

A few things that catch first-time users.

The nested item must be Transformed. A non-transformed child instance is just a static visual decoration of the parent’s RenderTemplate — it rides along but doesn’t emit. Mark Transformed = true (or, equivalently, run Transform on it in Studio) before expecting it to emit.

Default EmitCount = 0 on Part/Attachment/Model/PointLight. These types default to zero, intended for script-driven Particle:Emit(). The auto-bump in :EnableEmit covers any nested case — a nested transformed item with both EmitCount = 0 and EmitDuration = 0 gets bumped to a single-burst fire when its parent emits, regardless of whether the parent is a Part, Attachment, Model, or any other type. If you want more than one burst per parent emission, set EmitCount explicitly on the nested item.

Nested EmitDelay is overridden to 0. You can’t time-stagger a nested emitter relative to its parent’s emit. If you need staggered timing, restructure: one parent with multiple non-nested emitters that each emit from scripts at different delays, rather than one nested setup.

Beam attachments matter. If a nested Beam’s Attachment0/Attachment1 reference attachments outside the parent’s hierarchy, the Beam stays welded to those external attachments and doesn’t follow the parent’s motion. For “Beam tail trailing the particle,” ensure both attachments are inside the parent’s hierarchy.

Scale inheritance is one level deep. Nesting deeper than two layers loses scale propagation past the second level. If you need deeper, flatten the structure or use scripted scale management.

Animate mode resets nested configs. Edits made to the live cloned hierarchy at runtime are reverted on each loop iteration. Edit the source item, not the runtime clone.

Nesting is the most useful Hidden Depths pattern, and it’s the one most often missing from naive emitter setups. With the mechanism understood, you can build composite effects (fireballs, missile trails, sub-bursts) that look much richer than what a flat single-emitter setup produces. Next: Native Editing — using the plugin’s property panel and bulk-edit tools on Roblox’s ParticleEmitter and Trail instances, even though those aren’t transformed.