If you’re familiar with the demo-scene, you’ve probably seen the plasma effect:

FRACTINT Plasma

In this article, you’ll learn how to implement plasma effects of your own.

The Algorithm

To generate a plasma effect, you iterate the pixels in the screen buffer. For each pixel:

  1. Apply a function to the pixel’s coordinate producing some value \(v\).
  2. Use \(v\) to calculate the new RGB value of the pixel.
  3. Update the pixel’s RGB value in the screen buffer.
  4. Repeat steps (1)-(3) until you have processed the entire image frame.
  5. Display the updated frame.

Applying these steps at a high frequency creates the plasma animation. As you’ll see, the choice of function determines the shape and scale of the output image.

Plasma Functions

What function should you use when generating plasma? The sine function is popular due to its periodic nature. A vanilla sine function produces an image like the one shown below.

Sine

The white areas represent the peaks of the sine function, while the black areas represent the troughs. To produce more interesting plasmas, you can play with the function and its parameters.

The following sections describe a few functions you can use. In the equations that follow:

  • \(d\) is the distance from the center of the screen.
  • \(t\) is the time.
  • \(s\) is the scale factor.
  • \(\theta\) is the angle from the center point.
  • \(p_x\) and \(p_y\) are the pixel’s coordinates relative to the center point.
  • \(d_{min}\) is half of the smallest screen dimension.

Ripple

To produce a ripple effect, you can use the following function:

\[ {f(d, t, s)} = \sin(ds - 2t) \]

Ripple

Spiral

To produce a spiral effect, you can use the following function:

\[ {f(d, t, s, \theta)} = \sin(ds + 3\theta + t) \]

Spiral

Circle

To produce a circle effect, you can use the following function:

\[ {f(d, t, s, \theta)} = \sin(ds + t) + \sin(2\theta + t) \]

Circle

Checkerboard

To produce a checkerboard effect, you can use the following function:

\[ {f(d, t, s, p_x, p_y, d_{min})} = \sin({sp_x \over d_{min}}) * \sin({sp_y \over d_{min}} + t) \]

Checkerboard

Adding Some Color

In “The Algorithm” section, it’s mentioned you should use the value returned by the plasma function to calculate a pixel’s RGB color. How exactly do you do that? One way is to call a hsv_to_rgb() function. The hue argument to the hsv_to_rgb() varies as a function of the value returned by the plasma function:

let v = match self.shape {
    Shape::Ripple => self.ripple(dist, time),
    Shape::Spiral => self.spiral(dist, time, angle),
    Shape::Circle => self.circle(dist, time, angle),
    Shape::Square => self.square(px, py, min_dim, time),
};
// Normalize the plasma value from [-1,1] to [0,1] range for color mapping
let v = v * 0.5 + 0.5;

let (r, g, b) = match self.palette {
    Palette::Rainbow => self.hsv_to_rgb(v * 360.0, 1.0, 1.0),
    Palette::BlueCyan => self.hsv_to_rgb(v * 120.0 + 180.0, 0.8, 1.0),
    Palette::Hot => self.hsv_to_rgb(v * 60.0, 1.0, 1.0),
    Palette::PurplePink => self.hsv_to_rgb(v * 60.0 + 270.0, 0.7, 1.0),
}
*pixel = alpha | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);

The critical bit in the code is the hue value calculation. Hue is circular. The hsv_to_rgb() function expects the hue to be in the range [0,360]. The code maps the plasma value to the hue range of the desired palette. For example, a rainbow palette covers the full range of hues. In contrast, the blue/cyan palette covers 180-300 degrees of the hue circle.

Here’s a short clip showing the different plasma functions with various palettes:

Conclusion

Plasma effects demonstrate how simple mathematical functions can create mesmerizing animations. By combining sine waves, distance calculations, and color transformations, you can produce a variety of classic demo scene effects.

The complete project source is available on GitHub under plasma.