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.
When to use nesting
Section titled “When to use nesting”Nesting fits any case where two or more visual behaviours should be tied together as a unit:
- Fireball —
Modelcontaining a textured SpherePart, aBeambetween two of itsAttachments for a flame trail, and aPointLightfor the world-glow. One emit triggers all three. - Each-particle-leaves-trail —
PartwhoseRenderTemplatecontains anAttachmentwith aTrailparented to it. Every Part-particle drags a tiny streak as it flies. - Each-particle-glows —
PartwhoseRenderTemplatecontains aPointLight. Every Part-particle emits real light into the scene around it. - Sub-burst —
PartwhoseRenderTemplatecontains anotherPartemitter, with a higherEmitCount. 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.
How discovery works
Section titled “How discovery works”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 endendGetDescendants() 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.
How triggering works
Section titled “How triggering works”For each nested emitter discovered, the plugin calls its internal EnableEmit flow with three relevant attributes:
EmitCount— how many one-shot bursts to emit (default1for nested case).EmitDuration— seconds of continuous emission after the burst (default0, 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.
How inherited motion works
Section titled “How inherited motion works”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
CFrameis set via:PivotTo()(Model) or assigned to the parent BasePart’sCFrame(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/Attachment1references 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.
How inherited scale works
Section titled “How inherited scale works”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/Attachmentparticles —SizeX,SizeY,SizeZeach frame. - Nested
Beamparticles —Width0,Width1,CurveSize0,CurveSize1, andSegmentseach frame.TextureLengthis 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.
One level deep, not recursive
Section titled “One level deep, not recursive”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.
Cleanup propagation
Section titled “Cleanup propagation”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
BeamandPointLightclones 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 leaveEmitParentempty 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.
Animate mode loop snapshots
Section titled “Animate mode loop snapshots”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.
Recursion depth
Section titled “Recursion depth”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.
Canonical patterns, in detail
Section titled “Canonical patterns, in detail”Fireball
Section titled “Fireball”Setup:
- Create a
Modelin the workspace. - Set its
PrimaryPartto a SpherePart(the visible orb, with a fire texture). - Add a
Beamas a child of the orb, withAttachment0at the orb’s centre andAttachment1at a position that will trail behind during motion (or use a script to update the trailing attachment). - Add a
PointLightas a child of the orb (or directly under the Model). - Transform the Model. The Sphere, Beam, and PointLight are all auto-detected as nested emitters.
- Set
SpeedandAccelerationon 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.
Each-particle-leaves-trail
Section titled “Each-particle-leaves-trail”Setup:
- Create a
Partin the workspace. - Inside the Part, add an
Attachmentand Transform that Attachment. - Inside the Attachment, add a
Trailinstance with appropriate properties (Color, Transparency, WidthScale graphs). The Trail uses twoAttachments as its endpoints — these can be inside the Part or fixed siblings. - Transform the parent Part.
- Set the parent Part’s
SpeedandEmissionDirectionto 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.
Each-particle-glows
Section titled “Each-particle-glows”Setup:
- Create a
Part, transform it. - Inside the Part, add a
PointLightand Transform it. - Set the PointLight’s
Range,Brightness,Colorgraphs 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.
Sub-burst
Section titled “Sub-burst”Setup:
- Create a parent
Part, transform it. - Inside the parent, add another
Part(a smaller “sub-particle”), transform it. - Set the inner Part’s
EmitCount = 5,Lifetime = 0.3,Speedhigher 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.
Footguns and how to avoid them
Section titled “Footguns and how to avoid them”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.
What’s next
Section titled “What’s next”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.