Lecture 12 - Particles

Published

October 22, 2026

Last time we worked out a general physics engine and briefly introduced you to a fairly specific physics engine that is used extensively for procedural effects – the particle system

The particle system is essentially a special case of the system we constructed last time – it is basically a large number of very small objects that are subject to forces in their environment

They get used to make - fireworks - explosions - smoke - flocks of birds - water splashes - etc…

They can also be connected together to simulate flexible surfaces like fabric or water

There are a couple of small difference from the system we built earlier

One of the questions to answer is what to do when a particle “ages out”. If we are generating a lot of them, then we may be able to just reuse them immediately.

Another possibility is to store them in a pool for later use. This saves us the time required to create a new object. I bring this up because creating a pool to retain resources is a common practice in many branches in CS (managing threads in a multithreaded application is an example of this)

Sprites

I mentioned sprite last time, but we are going to take a few minutes to dive into them in a little more detail.

A sprite is really just a 2D image that we associate with some position information so it can move freely through our program. These are used a lot in computer games. Even in 3D, things that look like 3D models may be rendered to an image and “billboarded” – set to be rotated towards the camera.

For particle systems, it is relatively common to use sprites instead of drawing something complex. This could be something like a puff of smoke or fragment of flame, something that will look good when mixed together with a large number of other similar shapes.

There are a number of sprite packages out there that you can download or buy to create various effects, but we are going to spend a moment making our own using a graphics context which you briefly saw earlier in some of the examples I’ve posted.

In the tutorial, we are going to create some rain, so we will start with a raindrop.

Let’s start by just drawing a raindrop.

I started with some constants

const SPRITE_SIZE = 40;
const HALF_SIZE = SPRITE_SIZE / 2;
push();
translate(HALF_SIZE, HALF_SIZE);
stroke("#9595ee");
fill("#c7c7e7");
bezier(0, -5, -10, 20, 10, 20, 0, -5);
pop();

That will work as a basic drop, so now we are going to draw it in a graphics context instead.

function createSprite() {
    const fb = createGraphics(SPRITE_SIZE, SPRITE_SIZE);

    fb.clear(); // removes everything, so the background is transparent
    fb.push();
    fb.translate(HALF_SIZE, HALF_SIZE);
    fb.stroke("#9595ee");
    fb.fill("#c7c7e7");
    fb.bezier(0, -5, -10, 20, 10, 20, 0, -5);
    fb.pop();

    return fb;
}

We can treat the graphics context like it is an image.

imageMode(CENTER);
image(sprite, 50, 50);

Since we have this image, we can now use it all over the place.

 for (let x = 20; x < width; x += 20) {
        image(sprite, x, 50);
    }

sprite sheets

Frequently, we would like our sprites to have some variety. We could just make a bunch of images, but a common approach is to make a sprite sheet. This is a single image that has a collection of images at regular intervals. As you saw in the collage tutorial, we can grab just a piece of an image. We will do the same thing to extract particular sprites off the sprite sheet.

Let’s add two more sprites to our sheet.

function createSprite() {
    const fb = createGraphics(SPRITE_SIZE * NUM_SPRITES, SPRITE_SIZE);

    fb.clear();
    fb.push();
    fb.translate(HALF_SIZE, HALF_SIZE);
    fb.stroke("#9595ee");
    fb.fill("#c7c7e7");
    fb.bezier(0, -5, -10, 20, 10, 20, 0, -5);
    fb.pop();

    fb.push();
    fb.translate(SPRITE_SIZE + HALF_SIZE, HALF_SIZE);
    fb.stroke("#9595ee");
    fb.fill("#c7c7e7");
    fb.bezier(0, -5, -5, 10, 5, 10, 0, -5);
    fb.pop();

    fb.push();
    fb.translate(SPRITE_SIZE * 2 + HALF_SIZE, HALF_SIZE);
    fb.stroke("#9595ee");
    strokeWeight(2);
    fb.line(0, -5, 0, 5);
    fb.pop();

    return fb;
}

In this instance, I made the context wider and just moved to the center of each sprite before drawing. If we draw the sheet now, we will see all three sprites.

What we want is to isolate just one sprite at a time. To make that easier, we will create a new function:

function drawSprite(num, x, y) {
    push();
    imageMode(CENTER);
    translate(x, y);

    image(sprite, 
        0, 0,                       // dest position
        SPRITE_SIZE, SPRITE_SIZE,   // dest size
        SPRITE_SIZE * num, 0,       // source position
        SPRITE_SIZE, SPRITE_SIZE    // source size
    );

    pop();
}

Okay, now it is time for you to build a particle system yourself and make it rain.