Skip to content
Engine

World

EffectService

Define visual effects once (particles, sounds, lights, decals, models and camera shake), then play them pooled, quality-scaled and broadcast from the server.

ServiceShared

Getting it

local EffectService = Engine:GetService("EffectService")

Overview

An effect is a named recipe: a list of particle emitters, sounds, lights, decals, models and camera shakes that play together. You define it once with Define, then play it anywhere by name. EffectService takes care of the rest:

  • Pooling: the instances for each effect are built once and reused, so playing an explosion 50 times a second does not create 50 new emitters.
  • Quality scaling: effects read PerformanceService Quality and use the matching level of detail, so low-end devices get fewer particles and no shadows.
  • Distance culling: effects farther from the camera than MaxDistance are skipped.
  • Broadcasting: the server calls Broadcast and every relevant client plays the effect locally, sent through NetworkService in batches.

Effects are visual, so they play on the client. The server only broadcasts. Define each effect on both sides (a shared ModuleScript that both require is the easiest way), because the client needs the recipe to play it and the server needs it to know its MaxDistance when broadcasting.

Shared module: ReplicatedStorage.Effects
local Engine = require(game:GetService("ReplicatedStorage"):WaitForChild("Engine"))
local EffectService = Engine:GetService("EffectService")

EffectService:Define("Explosion", {
	Duration = 1.5,
	MaxDistance = 400,
	PoolSize = 8,
	Particles = {
		{ Texture = "rbxassetid://123", Burst = 60, Speed = NumberRange.new(20, 40), Lifetime = NumberRange.new(0.4, 0.9) },
	},
	Sounds = {
		{ SoundId = "rbxassetid://456", Volume = 0.8, RollOffMaxDistance = 300 },
	},
	Lights = {
		{ Kind = "Point", Color = Color3.fromRGB(255, 170, 60), Brightness = 4, Range = 24, Flicker = { Speed = 20 } },
	},
	Shakes = {
		{ Magnitude = 3, Radius = 80 },
	},
	Lod = {
		{ MinQuality = 7 },                                          -- full effect on good devices
		{ MinQuality = 3, ParticleRateScale = 0.5 },                 -- half the particles
		{ MinQuality = 0, ParticleRateScale = 0.25, DisableLights = true },
	},
})

return true

On the server (on the server the Engine you require is ServerScriptService.Engine, see Server and Client):

EffectService:Broadcast("Explosion", position)

Defining effects

Define

EffectService:Define(id: string, def: EffectDef)

Registers an effect. Each id can be defined once. Invalid definitions raise an error that names the wrong field.

FieldDescription
Particles: { ParticleDef }?Particle emitters.
Sounds: { SoundDef }?Sounds played at the effect.
Lights: { LightDef }?Point or spot lights, optionally flickering.
Decals: { DecalDef }?Decals on the host part, or on a small carrier part.
Models: { ModelDef }?Copies of a model or part placed at the effect.
Shakes: { ShakeDef }?Camera shakes, fading with distance.
Duration: number?Seconds before the effect stops by itself. Without it (or with Loop) it plays until you stop it.
Loop: boolean?Keep playing until stopped.
MaxConcurrent: number?Most copies at once; the oldest is removed to make room.
PoolSize: number?Copies to keep ready, and to build in advance with PreloadAsync.
MaxDistance: number?Do not play when the camera is farther away. Also the default broadcast radius.
Lod: { LodTier }?Level of detail tiers, see below.

ParticleDef

Any of: Texture, Color, Size, Transparency, Lifetime, Rate, Burst, Speed, SpreadAngle, Acceleration, Drag, RotSpeed, Rotation, LightEmission, LightInfluence, Squash, EmissionDirection, Orientation, ZOffset. They map to the ParticleEmitter properties of the same name; numbers are accepted where Roblox wants a NumberSequence or NumberRange. With Burst, the emitter emits that many particles once instead of emitting continuously at Rate.

SoundDef

SoundId (required), Volume, PlaybackSpeed, RollOffMode, RollOffMinDistance, RollOffMaxDistance, Looped.

LightDef

Kind ("Point" or "Spot", required), Color, Brightness, Range, Angle (spot lights), Shadows, Flicker ({ Speed?, Intensity? }, a smooth random flicker).

DecalDef

Texture (required), Face, Transparency, Color3, Size (used for the carrier part when there is no host part), ResampleMode (an Enum.ResamplerMode, such as Pixelated for pixel art).

Decals = { { Texture = "rbxassetid://1234567", ResampleMode = Enum.ResamplerMode.Pixelated } }

ModelDef

Template (an Instance, required), Offset (CFrame relative to the effect), Scale.

ShakeDef

Magnitude, Roughness, FadeInTime, FadeOutTime, and Radius: the shake fades out linearly with the camera's distance from the effect and is skipped beyond Radius.

LodTier

FieldDescription
MinQuality: numberThe tier applies when quality is at least this. The highest matching tier wins.
ParticleRateScale, LightBrightnessScaleMultipliers.
DisableParticles, DisableLights, DisableDecals, DisableModels, DisableShakesSwitch parts of the effect off.

Quality is PerformanceService.Quality + EffectService.QualityBias. Attached effects update their tier when quality changes.

Playing effects (client)

Play

EffectService:Play(id: string, at: Vector3 | CFrame | Instance, config: PlayConfig?): Effect

Plays an effect at a position, a CFrame, or on a BasePart, Attachment or Model. On a part, lights and decals go onto that part. Returns a handle; when the effect is culled by distance the handle is a stand-in that does nothing.

Attach

EffectService:Attach(id: string, instance: BasePart | Model, config: PlayConfig?): Effect

Plays an effect that follows a part or model, such as a burning torch or a speed trail.

PlayConfig

FieldDescription
Scale: number?Multiplies particle sizes.
Color: Color3?Tints particles.
Volume: number?Multiplies sound volumes.
Params: { [string]: any }?Reserved; not read by the current version.

Effect handle

MemberDescription
:Stop()Stops emitting and lets existing particles and sounds finish, then returns the effect to the pool.
:Destroy()Ends the effect immediately.
Ended: SignalFires once when the effect is fully done.
Playing, Destroyed, Position, DefinitionState.

PreloadAsync

EffectService:PreloadAsync(ids: { string }): Promise

Preloads every texture and sound the effects use and builds PoolSize copies of each, so the first play has no delay. Call it during a loading screen.

Broadcasting (server)

Broadcast

EffectService:Broadcast(id: string, at: Vector3 | CFrame | Instance, config: PlayConfig?, filter: Filter?)

Tells clients to play an effect. Without a filter it goes to players whose character is within the effect's MaxDistance (or to everyone when the effect has none).

FilterDescription
Players: { Player }?Exactly these players.
Radius: number?Players within this many studs.
Center: Vector3?Measure the radius from here instead of the effect position.

Inspection

MethodDescription
:GetDefinition(id)The validated definition, or nil.
:GetActiveCount(id)Copies playing right now.
:GetPoolStats(id){ Size, FreeCount, InUseCount } of the effect's pool (client).

Properties

PropertyDescription
QualityBias: numberAdded to the performance quality when choosing a level of detail. Use a negative value to make effects lighter than the device could handle, for example in busy scenes.

Configuration

Defaults from DefaultConfiguration/Services/EffectService20 settings
SettingDefaultWhat it does
DefaultBroadcastRate20Packets per second the shared Broadcast Remote's Stream flushes at.
DefaultParticleTexture"rbxasset://textures/particles/sparkles_main.dds"Texture a Particle def gets when it sets none.
DefaultParticleRate20Emit rate a Particle def gets when it sets none.
DefaultParticleLifetime1Seconds a Particle def's particles live when it sets none; also the release-grace floor.
DefaultLightBrightness1Brightness a Light def gets when it sets none.
DefaultLightRange16Range a Light def gets when it sets none.
DefaultSpotAngle90Cone angle a SpotLight def gets when it sets none.
DefaultFlickerSpeed10Flicker speed a Light def gets when Flicker is set without one.
DefaultFlickerIntensity0.3Flicker intensity a Light def gets when Flicker is set without one.
DefaultFlickerReleaseGraceSeconds0.2Minimum release grace when any Light in an effect flickers.
DefaultSoundVolume0.5Volume a Sound def gets when it sets none.
DefaultSoundRollOffMin10RollOffMinDistance a Sound def gets when it sets none.
DefaultSoundRollOffMax100RollOffMaxDistance a Sound def gets when it sets none.
DefaultDecalSizeVector2.new(1, 1)Size of the carrier a Decal gets when there is no host BasePart to decal directly.
DefaultShakeMagnitude1Camera shake magnitude when a Shake def sets none.
DefaultShakeRoughness10Camera shake roughness when a Shake def sets none.
DefaultShakeFadeInTime0.1Seconds a camera shake ramps in when a Shake def sets none.
DefaultShakeFadeOutTime0.3Seconds a camera shake ramps out when a Shake def sets none.
ShakeRotationDegreesPerMagnitude0.5Degrees of rotational shake per unit of Magnitude.
ShakeMaxRotationDegrees8Hard clamp on rotational shake regardless of Magnitude.

Types

export type EffectDef
export type ParticleDef
export type SoundDef
export type LightDef
export type LightFlicker = { Speed: number?, Intensity: number? }
export type DecalDef
export type ModelDef
export type ShakeDef
export type LodTier
export type PlayConfig
export type Effect
export type Filter = { Players: { Player }?, Radius: number?, Center: Vector3? }