# Creating Smooth Particle Emission With Sub-Frame Interpolation

Particle effects greatly spice up game visuals. They are usually not the main focus of a game, but many games rely on particle effects to increase their visual richness. They are everywhere: dust clouds, fire, water splashes, you name it. Particle effects are usually implemented with discrete emitter movement and discrete emission "bursts". Most of the time, everything looks just fine; however, things break down when you have a fast-moving emitter and high emission rate. This is when sub-frame interpolation comes into play.

## Demo

This Flash demo shows the difference between a common implementation of a fast-moving emitter and the sub-frame interpolation approach at different speeds.

Tip: Sub-frame interpolation is slightly more computationally expensive than regular implementation. So if your particle effects look just fine without sub-frame interpolation, it's usually a good idea not to use sub-frame interpolation at all.

## A Common Implementation

First, let's take a look at a common implementation of particle effects. I will present a very minimalistic implementation of a point emitter; on each frame, it creates new particles at its position, integrates existing particles, keeps track of each particle's life, and removes dead particles.

For simplicity's sake, I will not use object pools to reuse dead particles; also, I will use the `Vector.splice` method to remove dead particles (you usually do not want to do this because `Vector.splice` is a linear-time operation). The main focus of this tutorial is not efficiency, but how the particles are initialized.

Here are some helper functions we'll need later:

```// linear interpolation
public function lerp(a:Number, b:Number, t:Number):Number
{
return a + (b - a) * t;
}

// returns a uniform random number
public function random(average:Number, variation:Number):Number
{
return average + 2.0 * (Math.random() - 0.5) * variation;
}```

And below is the `Particle` class. It defines some common particle properties, including lifetime, grow and shrink time, position, rotation, linear velocity, angular velocity, and scale. In the main update loop, position and rotation are integrated, and the particle data is finally dumped into the display object represented by the particle. The scale is updated based on the particle's remaining life, compared to its grow and shrink time.

```public class Particle
{
// display object represented by this particle
public var display:DisplayObject;

// current and initial life, in seconds
public var initLife:Number;
public var life:Number;

// grow time in seconds
public var growTime:Number;

// shrink time in seconds
public var shrinkTime:Number;

// position
public var x:Number;
public var y:Number;

// linear velocity
public var vx:Number;
public var vy:Number;

// orientation angle in degrees
public var rotation:Number;

// angular velocity
public var omega:Number;

// initial & current scale
public var initScale:Number;
public var scale:Number;

// constructor
public function Particle(display:DisplayObject)
{
this.display = display;
}

// main update loop
public function update(dt:Number):void
{
// integrate position
x += vx * dt;
y += vy * dt;

// integrate orientation
rotation += omega * dt;

// decrement life
life -= dt;

// calculate scale
if (life > initLife - growTime)
scale = lerp(0.0, initScale, (initLife - life) / growTime);
else if (life < shrinkTime)
scale = lerp(initScale, 0.0, (shrinkTime - life) / shrinkTime);
else
scale = initScale;

// dump particle data into display object
display.x = x;
display.y = y;
display.rotation = rotation;
display.scaleX = display.scaleY = scale;
}
}```

And finally, we have the point emitter itself. In the main update loop, new particles are created, all particles are updated, and then dead particles are removed. The rest of this tutorial will focus on the particle initialization within the `createParticles()` method.

```public class PointEmitter
{
// particles per second
public var emissionRate:Number;

// position of emitter
public var position:Point;

// particle life & variation in seconds
public var particleLife:Number;
public var particleLifeVar:Number;

// particle scale & variation
public var particleScale:Number;
public var particleScaleVar:Number;

// particle grow & shrink time in lifetime percentage (0.0 to 1.0)
public var particleGrowRatio:Number;
public var particleShrinkRatio:Number;

// particle speed & variation
public var particleSpeed:Number;
public var particleSpeedVar:Number;

// particle angular velocity variation in degrees per second
public var particleOmegaVar:Number;

// the container new particles are added to
private var container:DisplayObjectContainer;

// the class object for instantiating new particles
private var displayClass:Class;

// vector that contains particle objects
private var particles:Vector.<Particle>;

// constructor
public function PointEmitter
(
container:DisplayObjectContainer,
displayClass:Class
)
{
this.container = container;
this.displayClass = displayClass;
this.position = new Point();
this.particles = new Vector.<Particle>();
}

// creates a new particle
private function createParticles(numParticles:uint, dt:Number):void
{
for (var i:uint = 0; i < numParticles; ++i)
{
var p:Particle = new Particle(new displayClass());
particles.push(p);

// initialize rotation & scale
p.rotation = random(0.0, 180.0);
p.initScale = p.scale = random(particleScale, particleScaleVar);

// initialize life & grow & shrink time
p.initLife = random(particleLife, particleLifeVar);
p.growTime = particleGrowRatio * p.initLife;
p.shrinkTime = particleShrinkRatio * p.initLife;

// initialize linear & angular velocity
var velocityDirectionAngle:Number = random(0.0, Math.PI);
var speed:Number = random(particleSpeed, particleSpeedVar);
p.vx = speed * Math.cos(velocityDirectionAngle);
p.vy = speed * Math.sin(velocityDirectionAngle);
p.omega = random(0.0, particleOmegaVar);

// initialize position & current life
p.x = position.x;
p.y = position.y;
p.life = p.initLife;
}
}

// removes dead particles
{
// It's easy to loop backwards with splicing going on.
// Splicing is not efficient,
// but I use it here for simplicity's sake.
var i:int = particles.length;
while (--i >= 0)
{
var p:Particle = particles[i];

// check if particle's dead
if (p.life < 0.0)
{
// remove from container
container.removeChild(p.display);

// splice it out
particles.splice(i, 1);
}
}
}

// main update loop
public function update(dt:Number):void
{
// calculate number of new particles per frame
var newParticlesPerFrame:Number = emissionRate * dt;

// extract integer part
var numNewParticles:uint = uint(newParticlesPerFrame);

// possibly add one based on fraction part
if (Math.random() < newParticlesPerFrame - numNewParticles)
++numNewParticles;

// first, create new particles
createParticles(numNewParticles, dt);

// next, update particles
for each (var p:Particle in particles)
p.update(dt);

// finally, remove all dead particles
}
}```

If we use this particle emitter and make it move in a circular motion, this is what we'll get:

### Let's Make It Faster

Looks fine, right? Let's see what happens if we increase the emitter's movement speed:

See the discrete point "bursts"? These are due to how the current implementation assumes that the emitter is "teleporting" to discrete points across frames. Also, new particles within each frame are initialized as if they are created at the same time and bursted out at once.

## Sub-Frame Interpolation to the Rescue!

Let's now focus on the specific part of code that results in this artifact in the `PointEmitter.createParticles()` method:

```p.x = position.x;
p.y = position.y;
p.life = p.initLife;```

To compensate for the discrete emitter movement and make it look as if the emitter movement is smooth, also simulating continuous particle emission, we are going to apply sub-frame interpolation.

In the `PointEmitter` class, we'll need a Boolean flag for turning on sub-frame interpolation, and an extra `Point` for keeping track of the previous position:

```public var useSubFrameInterpolation:Boolean;
private var prevPosition:Point;```

At the beginning of the `PointEmitter.update()` method, we need a first-time initialization, which assigns the current position to `prevPosition`. And at the end of the `PointEmitter.update()` method, we will record the current position and save it to `prevPosition`.

So this is what the new `PointEmitter.update()` method looks like (the highlighted lines are new):

```public function update(dt:Number):void
{
// first-time initialization
if (!prevPosition)
prevPosition = position.clone();

var newParticlesPerFrame:Number = emissionRate * dt;
var numNewParticles:uint = uint(newParticlesPerFrame);
if (Math.random() < newParticlesPerFrame - numNewParticles)
++numNewParticles;

createParticles(numNewParticles, dt);

for each (var p:Particle in particles)
p.update(dt);

// record previous position
prevPosition = position.clone();
}```

Finally, we'll apply sub-frame interpolation to particle initialization in the `PointEmitter.createParticles()` method. To simulate continuous emission, the initialization for particle position now linearly interpolates between the emitter's current and previous position. The particle lifetime initialization also simulates the "time elapsed" since the last frame up till the particle's creation. The "time elapsed" is a fraction of `dt` and is also used to integrate the particle position.

We will therefore change the following code inside the `for` loop in the `PointEmitter.createParticles()` method:

```p.x = position.x;
p.y = position.y;
p.life = p.initLife;```

...to this (remember that `i` is the loop variable):

```if (useSubFrameInterpolation)
{
// sub-frame interpolation
var t:Number = Number(i) / Number(numParticles);
var timeElapsed:Number = (1.0 - t) * dt;
p.x = lerp(prevPosition.x, position.x, t);
p.y = lerp(prevPosition.y, position.y, t);
p.x += p.vx * timeElapsed;
p.y += p.vy * timeElapsed;
p.life = p.initLife - timeElapsed;
}
else
{
// regular initialization
p.x = position.x;
p.y = position.y;
p.life = p.initLife;
}```

Now, this is what it looks like when the particle emitter is moving at high speed with sub-frame interpolation:

Much better!

## Sub-Frame Interpolation Is Not Perfect

Unfortunately, sub-frame interpolation using linear interpolation is still not perfect. If we further increase the speed of the emitter's circular motion, this is what we'll get:

This artifact is caused by trying to match the circular curve with linear interpolation. One way to remedy this is not to just keep track of the emitter's position in the previous frame, but instead to keep track of previous position within multiple frames, and interpolate between these points using smooth curves (like Bezier curves).

In my opinion, however, linear interpolation is more than enough. Most of the time, you won't have particle emitters moving fast enough to cause sub-frame interpolation with linear interpolation to break down.

## Conclusion

Particle effects can break down when the particle emitter is moving at a high speed and has a high emission rate. The discrete nature of the emitter becomes visible. To improve the visual quality, use sub-frame interpolation to simulate smooth emitter movement and continuous emission. Without introducing too much overhead, linear interpolation is usually used.

However, a different artifact would start showing up if the emitter moves even faster. Smooth curve interpolation can be used to fix this problem, but linear interpolation usually works well enough and is a nice balance between efficiency and visual quality.