I came to Roblox from non-Roblox gamedev. While making my own game I felt a lack of per-particle controls, so I built a voxel particle plugin. I’m new to programming, but I have strong Cinema/VFX background. I added the essentials first and still have many ideas to expand.
Amazing work! My only suggestion would be particle prewarming (as seen in unity), where the complete cycle is pre-generated instead of only observing the start when enabled
Wow! This is pretty cool! Right now, I would say it’s great, but not any better than the current Roblox particles, but as you mentioned, these are just the essentials. I can’t wait to see the things you will accomplish - I know this is going to go far!
NEW UPDATE
math.noise → simple curves (was too complicated per particle)
added 30FPS mode (light interpolation between frames)
This reduced the CPU load by 30%
Tutorials on the way UPD (CPU spike on video due to another game system)
I’ve rolled out a new update. Some people didn’t like that the particle system’s emission vector was defined in world space, and the binder had to convert it to local space when needed. I’ve moved this logic from the binder script into the core system. Now you can skip using the binder entirely and trigger particles directly — they will automatically use the emitter’s rotation for their emission angle (for example, for a flamethrower).
I’ve also added a Line of Sight (LOS) system so emitters won’t render particles through walls. (This is separate from the camera-angle check — that feature was already implemented: if you look away from an emitter, it stops emitting. Now it also stops emitting if you’re looking at it but it’s behind a wall or blocked by an object.)
By default, objects that only have “CanQuery” enabled are ignored, as well as the emitter itself. If you want an emitter to ignore LOS entirely, just add the attribute VoxelIgnoreCulling = true.
I also added smooth rate scaling: if the total number of emitters is getting close to the emission limit, all rates will gradually decrease. Previously the system would simply disable one of the emitters abruptly.
I’ve added rate scaling based on graphics settings as well — if someone forces manual low settings, the emission rate will adjust accordingly.
And lastly, I’ve made a full game that uses the voxel particle system for all its effects — here’s a demo video:
im attempting to learn how to use this in my game and so far its kinda working but how do you code it correctly? ive been reading the actual code but couldn’t find a way to use my presets when i trigger the particle in a script.
this is what im attempting to do:
local particleAPI = require(game.ReplicatedStorage.Shared.VoxelParticleSystem)
local particlePreset = require(game.ReplicatedStorage.Shared.VoxelEmitterPresets:WaitForChild("DashSmoke"))
local dashParticle = particleAPI.Attach(HumanoidRootPart, particlePreset)
this should work on paper (i think) and gives no errors but it always defaults to the default configuration
do you have a sample or an example of how to do it correctly? otherwise ill just keep trying until i figure it out : O
Your preset ModuleScripts don’t return the emitter config directly — they return a “wrapper” table like:
return {
name = "...",
displayName = "...",
emitterWorldCFrame = ...,
state = { ... ACTUAL EMITTER CONFIG ... }
}
VoxelParticleSystem.Attach(anchor, config) expects the config table, so if you pass the whole preset table it won’t see rate/color/etc at the top level and will fall back to defaults. Fix: pass preset.state (or preset.state or preset).
Direct Attach sample:
local RS = game:GetService("ReplicatedStorage")
local Shared = RS:WaitForChild("Shared")
local VPS = require(Shared:WaitForChild("VoxelParticleSystem"))
local preset = require(Shared:WaitForChild("VoxelEmitterPresets"):WaitForChild("Dash")) -- your preset module
local cfg = preset.state or preset -- IMPORTANT
local emitter = VPS.Attach(HumanoidRootPart, cfg)
-- If you want a burst:
emitter:Emit(cfg.burstCount > 0 and cfg.burstCount or 25)
-- Optional cleanup if it’s a one-shot effect:
task.delay(2, function()
emitter:Destroy()
end)
If you’re using the included binder (attributes way)
Recommended to run effects from a LocalScript; if you trigger from server, fire a RemoteEvent or set attributes on the server and let the client binder play it
Hope it helps Im pretty busy with my Astro Farm game and can’t finish tutorial right now or translate comments in code
Added a demo gallery with labeled presets, looping bursts, Bézier effects, and preset combinations!
Since the curve update, I’ve added shared particle/emitter budgets, improved off-screen culling and pool prewarming, and fixed errors when editing presets. Plugin and runtime versions are now unified.
After updating the plugin, click Update project to update your project’s runtime. For native Script Sync projects, follow the update instructions in the README on Git.
Sorry can’t do editable atm, will do it later, need to cleanup server folders from my models (they are hidden in public place)