Particle Emission Mask β‘π ‘π π £π
The node that actually runs the particle simulation
- masks
- emitters
- vortices
- wells
- static_bodies
- MASK
- IMAGE
Every other particle node in this pack - ParticleEmitter, GravityWell, Vortex, the modulation nodes - produces a definition, not an effect. This is the one that actually runs the simulation. Feed it one or more emitters and, optionally, the force fields you want acting on them, and it steps the physics forward frame by frame on pymunk (a real physics engine, not an approximation) and renders the result as a mask and an image.
How it works
emitters takes a PARTICLE_EMITTER - usually a chain built from ParticleEmitter nodes stacked via previous_emitter. vortices and wells optionally add VORTEX and GRAVITY_WELL force fields into the mix, with well_strength_multiplier as a global dial on how hard the gravity wells pull without having to re-tune each one individually. static_bodies optionally adds fixed collision obstacles particles can bounce off.
Beyond the force fields, this node owns its own simulation-wide settings: particle_count (a hard cap, up to 10,000), particle_lifetime (in seconds, how long each particle survives before disappearing), wind_strength/wind_direction (a uniform force across the whole system, independent of any gravity well or vortex), gravity (straight-down pull, separate from the point-based GravityWell nodes), and warmup_period - frames simulated before the visible output starts, useful for letting a system settle into a natural-looking state instead of starting from a jarring empty frame.
masks and the shared strength/invert/subtract_original/grow_with_blur set are the same base contract used across this pack's mask nodes, applied on top of the simulation result. respect_mask_boundary is the standout option here - turned on, particles physically collide with the mask's edges rather than just being confined to spawning inside it, so a mask becomes an actual solid boundary in the simulation, not just a spawn region. emission_strength scales the whole effect's intensity, and start_frame/end_frame bound when emission is active.
The inputs and outputs that matter
emitters(required,PARTICLE_EMITTER) - fromParticleEmitter.vortices/wells/static_bodies(optional) - force fields and obstacles.particle_count(default 200),particle_lifetime(default 4s) - simulation scale.gravity,wind_strength/wind_direction- global forces.respect_mask_boundary- makes the mask a physical collision boundary.
Two outputs: MASK (the particle system rendered as a mask, ready for compositing) and IMAGE (a rendered preview of the same simulation).
How to install it
ComfyUI Manager: search "RyanOnTheInside", install, restart. Manually:
cd ComfyUI/custom_nodes
git clone https://github.com/ryanontheinside/ComfyUI_RyanOnTheInside
cd ComfyUI_RyanOnTheInside
pip install -r requirements.txt
then restart. This node depends on pymunk for physics and (per the rendering output) likely pygame, both pinned in requirements.txt - a pymunk version mismatch is a known way for this entire particle-system side of the pack to fail on install.
Common issues & troubleshooting
Built emitters and force fields but nothing shows up here. This is the single node that actually runs the simulation - check that your ParticleEmitter chain (and any GravityWell/Vortex nodes) are wired into this node's emitters/vortices/wells inputs specifically. It's easy to build the whole chain and forget the final connection.
Simulation is painfully slow on longer sequences. particle_count at the high end combined with a long frame range is genuinely expensive - this is a per-frame physics simulation and render, not a cheap filter. Drop particle_count or particle_lifetime before assuming something's misconfigured.
Particles pass straight through your mask despite respect_mask_boundary. Confirm it's actually toggled on (it defaults to off) - without it, the mask only constrains where particles spawn, not where they can travel afterward.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| masks | MASK | Input mask or sequence of masks to be processed (MASK type) | |
| strength | FLOAT | 1.000β1 | Overall strength of the mask effect (0.0 to 1.0) |
| invert | BOOLEAN | false | When enabled, inverts the mask output (black becomes white and vice versa) |
| subtract_original | FLOAT | 0.000β1 | Amount of the original mask to subtract from the result (0.0 to 1.0) |
| grow_with_blur | FLOAT | 0.00β10 | Amount of Gaussian blur to apply for mask growth (0.0 to 10.0) |
| emitters | PARTICLE_EMITTER | List of particle emitter configurations (PARTICLE_EMITTER type) | |
| particle_count | INT | 2001β10000 | Maximum number of particles in the system (1 to 10000) |
| particle_lifetime | FLOAT | 4.00.1β10 | How long each particle lives in seconds (0.1 to 10.0) |
| wind_strength | FLOAT | 0-100β100 | Strength of the wind force (-100.0 to 100.0) |
| wind_direction | FLOAT | 00β360 | Direction of the wind in degrees (0.0 to 360.0) |
| gravity | FLOAT | 0-1000β1000 | Strength and direction of gravity (-1000.0 to 1000.0) |
| warmup_period | INT | 00β1000 | Number of frames to simulate before starting (0 to 1000) |
| start_frame | INT | 00β1000 | Frame to start particle emission (0 to 1000) |
| end_frame | INT | 00β1000 | Frame to end particle emission (0 to 1000, 0 means until end) |
| respect_mask_boundary | BOOLEAN | false | Whether particles should collide with mask boundaries |
| emission_strength | FLOAT | 1.000β1 | Strength of particle emission effect (0.0 to 1.0) |
| vorticesopt | VORTEX | Optional list of vortex configurations (VORTEX type) | |
| wellsopt | GRAVITY_WELL | Optional list of gravity well configurations (GRAVITY_WELL type) | |
| well_strength_multiplieropt | FLOAT | 1.00β10 | Global multiplier for gravity well strengths (0.0 to 10.0) |
| static_bodiesopt | STATIC_BODY | Optional list of static collision bodies (STATIC_BODY type) |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| MASK | MASK | β |
| IMAGE | IMAGE | β |