technique
VFX and particles
How hit sparks, explosions, smoke, dust, and fire are built as short frame sequences from a shared bright-to-dark ramp, and when to hand the work to a code-driven particle instead.
Before this
This page assumes you are comfortable with:
- techniqueSprite animationHow many frames each standard cycle needs, which poses carry a walk, how to time idle and attack animations, and the small tricks that make a handful of frames read as motion.
- techniquePalettes and rampsBuild each material's colors as a short dark-to-light ramp that shifts hue and saturation as it climbs, share the darkest steps across ramps, and keep the whole palette small enough to swap.
Why you need this
Every action in a game needs a visible answer. A sword that connects with nothing but a sound feels broken; the same sword with a 3-frame spark and a one-frame white flash feels heavy. Visual effects (VFX) are the frames that answer actions: impacts, explosions, landings, pickups. They are the cheapest art in the game per frame and the most numerous, so the win is a shared method and a shared palette. This is stage 3 of the pipeline, "make it move", applied to things that are not characters.
The idea
An effect is a short frame sequence with no body underneath it. Most are non-looping: they play once, from nothing to nothing, and are removed. A few are looping: fire, a sparkle on a pickup, a torch. Because there is no body to keep consistent, effects are freer than character animation, and because they last well under a second, nobody sees an individual frame.
Three properties make an effect read:
- Expand, then dissipate. Almost every impact grows fast and fades slowly. Front-load the size, back-load the fade.
- Bright core, dark rim. The hottest part is the lightest color; the edge is the darkest. This is one ramp read from its light end to its dark end over time.
- Break up. Solid shapes become fragments, then holes, then nothing. Transparency is done with holes in the pixel grid, not with alpha blending, so the effect matches the crisp art around it.
The standard set
| Effect | Frames | ms per frame | Loop | Size at 16-px scale |
|---|---|---|---|---|
| Hit spark | 3 to 4 | 50 ms (3 frames at 60 fps) | No | 8 to 12 px |
| Explosion | 6 to 8 | 60 to 80 ms (4 to 5 frames at 60 fps) | No | 24 to 32 px |
| Smoke puff | 5 to 6 | 100 ms (6 frames at 60 fps) | No | 12 to 16 px |
| Dust on landing | 3 to 4 | 80 ms (5 frames at 60 fps) | No | 12 px wide, 4 px tall |
| Sparkle | 4 | 120 ms (7 frames at 60 fps) | Yes | 5 to 7 px |
| Fire | 4 to 6 | 100 ms (6 frames at 60 fps) | Yes | 8 to 16 px |
The hit spark is the fastest because it is the one the player must never wait for; it is over before the follow-up input. The smoke puff is the slowest because smoke drifts.
The effects ramp
One ramp serves fire, explosions, sparks, and muzzle flashes. Written dark to light, in the cluster's convention: #3a3a3a #c7321f #ff8c1a #ffe066 #ffffff. Read light to dark over time and it is an explosion cooling; read light to dark from center to edge and it is a fireball in one frame. Smoke and dust use a gray ramp: #3a3a3a #6e6e6e #a5a5a5 #d9d9d9.
Worked example
An 8-frame explosion at 60 ms per frame (4 frames at 60 fps), 480 ms total. Radius is in pixels from the center. "Holes" means transparent pixels inside the shape.
| Frame | Radius | Colors used, center to edge | Holes |
|---|---|---|---|
| 1 | 2 | white | None; a 4-px dot |
| 2 | 5 | white, yellow | None |
| 3 | 8 | white, yellow, orange | None; rim starts to wobble |
| 4 | 10 | yellow, orange, red | A few 1-px holes near the center |
| 5 | 11 | orange, red, dark gray | Center hollow; ring of fragments |
| 6 | 11 | red, dark gray | Broken into 5 to 8 fragments plus gray smoke |
| 7 | 10 | dark gray, mid gray | Fragments gone; smoke only, drifting up 1 px |
| 8 | 8 | mid gray, light gray | Thin scattered smoke; mostly holes |
The radius peaks at frame 5 and never grows after; the last three frames are all fade. The color at the center always steps down the ramp by one per frame or two: white to yellow to orange to red to gray.
Frame 3 and frame 5 on a 16-wide grid (the full effect would be 24 wide; this shows the core):
Frame 3 (radius 8) Frame 5 (radius 11, hollow)
. . . . . 1 1 1 1 1 1 . . . . . . . . 1 . . . . . . . 1 . . . .
. . . 1 1 2 2 2 2 2 2 1 1 . . . . 1 . . 1 1 . . . 1 1 . . 1 . .
. . 1 2 2 3 3 3 3 3 3 2 2 1 . . . . . 1 2 . 1 . 1 . 2 1 . . . .
. 1 2 2 3 3 4 4 4 4 3 3 2 2 1 . 1 . 1 2 . . . . . . . 2 1 . 1 .
. 1 2 3 3 4 4 4 4 4 4 3 3 2 1 . . . 2 . . . . . . . . . 2 . . .
1 2 2 3 4 4 4 4 4 4 4 4 3 2 2 1 . 1 2 . . . . . . . . . 2 1 . .
1 2 3 3 4 4 4 4 4 4 4 4 3 3 2 1 1 . . . . . . . . . . . . . 1 .
1 2 3 4 4 4 4 4 4 4 4 4 4 3 2 1 . 1 2 . . . . . . . . . 2 1 . .
. transparent, 1 red #c7321f, 2 orange #ff8c1a, 3 yellow #ffe066, 4 white #ffffff.
Frame 3 is a solid disc with the ramp running from the white core outward. Frame 5 is the same disc with the center removed and the rim broken into pieces; the eye reads the pieces as the disc still being there.
The hit flash
When a character takes damage, swap every non-transparent pixel of its sprite to white for exactly 1 frame at 50 ms (3 frames at 60 fps), then back. No new art is drawn; the engine does it with a palette swap or a shader that ignores the source color. Paired with a 3-frame spark at the contact point, it is the single most effective effect in the game for its cost, which is zero frames.
Additive-looking effects without blending
Additive blending (light sources adding their brightness on top of the scene) is not available when the game is drawn from a fixed palette with no alpha blending. The look is faked: use the brightest colors in the ramp, let the core go pure white, and keep the outer pixels the saturated mid-ramp colors rather than dark ones. A white core surrounded by yellow reads as glowing on any background. What breaks the illusion is a dark outline around the effect; effects never get outlines.
Reuse via rotation and mirroring
A hit spark drawn once can be mirrored horizontally, vertically, or both for four variants, and rotated by 90 degrees for four more, all at no art cost. Rotation by 90 degrees is exact on a pixel grid; rotation by any other angle is not, and it smears the pixels, so restrict engine rotation to multiples of 90. A dust puff is mirrored horizontally when the character lands facing left. An explosion is drawn without a light direction bias so it can be flipped freely.
Particles from code vs hand-drawn frames
A particle is a tiny sprite (2 by 2 or 3 by 3 pixels) spawned in numbers, each with its own position, velocity, and age. The engine moves it and picks its color from a life ramp by age: a spark particle is white for its first third, yellow for the second, red for the last, then removed.
// One particle, one frame of update. dt is seconds since the last frame.
function updateParticle(p, dt) {
p.age += dt;
p.x += p.vx * dt;
p.y += p.vy * dt;
p.vy += 200 * dt; // gravity, pixels per second squared
const t = p.age / p.life; // 0 at birth, 1 at death
p.color = t < 0.33 ? "#ffffff" : t < 0.66 ? "#ffe066" : "#c7321f";
return p.age < p.life; // false means remove it
}
Rendering rounds p.x and p.y to whole pixels so the particle stays on the grid.
Prefer code particles when the effect is many small identical things (sparks off a grindstone, embers above a fire, rain, debris), when its shape depends on physics (fragments that bounce), or when it must vary every time. Prefer hand-drawn frames when the effect has a designed shape (a slash arc, a magic circle), when it must read as one object (an explosion's fireball), or when the whole game runs at a fixed 60 fps and 8 frames is simply less work than a particle system. Most games use both: a drawn fireball with code-driven embers spraying off it.
Screen shake and hit stop
Two effects cost no art at all. Screen shake offsets the camera by a random whole number of pixels, 1 to 3, for 3 to 6 frames, decaying to zero; it sells a heavy impact, and it must be integer offsets or the whole screen shimmers. Hit stop freezes the game (or just the two sprites involved) for 2 to 4 frames at 60 fps at the moment of contact, so the strike pose from the attack animation is seen for a beat before the enemy flies back. Both are set in code, tuned in minutes, and make every drawn effect land harder.
In a game's art pipeline
Stage 3, make it move. Effects are drawn after the characters, because they attach to character frames: the spark spawns where the strike frame's weapon tip is, the dust spawns at the feet on the landing frame. The shared effects ramp comes from the palette fixed in stage 1 (Palettes and ramps). Effect frames ship on the sprite sheet with a center pivot rather than a feet pivot, which Sprite sheets and export covers. The timing vocabulary is from Sprite animation.
Common mistakes
- Slow growth. An explosion that expands evenly over 8 frames looks like a balloon inflating. Reach full radius by frame 4 or 5 and spend the rest fading.
- Fading by alpha. A half-transparent smoke puff over crisp pixel tiles looks like a different medium. Fade with holes and with the ramp.
- Outlined effects. A dark outline on a fireball kills the glow. Effects have no outlines.
- Rotation by arbitrary angles. A spark rotated 37 degrees by the engine turns into a smear. Mirror, or rotate by 90.
- Every effect its own palette. Ten effects with ten ramps look like ten packs. One fire ramp, one gray ramp.
- Effects that outlast the action. A 480-ms explosion is right; a 480-ms hit spark hides the next attack. Sparks are done in 150 to 200 ms.
Cost
An effect frame is fast: no anatomy, no consistency with a previous frame beyond the ramp, and most under 16 pixels. A hit spark is minutes; an 8-frame explosion is an hour. The count is what adds up: a modest action game wants a spark, a block spark, a dust puff, a smoke puff, an explosion, a pickup sparkle, a fire loop, a heal glow, and a death burst, which is 40 to 50 frames before any variants. File size is negligible; 50 frames at 16 by 16 is under 15 kilobytes. The real cost arrives when a palette change forces every effect to be recolored, so plan a shared effects palette (the fire ramp and the gray ramp) before the first spark is drawn, and add hues only for effects that need them (a green poison cloud, a blue ice shard).
Going further
- Sprite animation, for the attack frames that effects attach to.
- Palettes and ramps, for building the fire ramp so it steps evenly in value.
- Dithering, for breaking up smoke without adding colors.
- Sprite sheets and export, for center pivots on effect frames.
- Any editor with an onion-skin view, to check the radius grows on every frame until the peak.