Plasma effects

I recently tried to recreate the famous plasma effect that was often used in the demo scene. Since I didn't know a lot about it and how to achieve it I had to do some research first. I thought I'd write a little bit about what the effect is and how I ended up implementing it.

Click inside the square on the right to see the plasma effect in action. Click again to stop the animation.

First a little disclaimer: usually, these effects are coded by very smart, very math-savvy programmers that know their target system inside and out. None of these attributes apply to me so I just brute-forced my way through the implementation making convenient choices whereever possible:

  1. The canvas is 32 units wide and 32 units high.
  2. One such unit is an 8 by 8 pixed div element, making the total width and height 256 pixels.
  3. The color pallette will consist of 256 colors.

After some research I found that the principle behind the effect is really simple. The base is any function f(x, y, t) = n where x and y are the coordinates in the canvas, t is the time and n is an integer between 0-256, representing a color in the palette. When we have such a function we initiate by drawing the canvas for t=0 after which we redraw the canvas periodically for new values of t, which generates the 'flowing' colors. The next step was to find such a function.

The first function

Remember that the output of the function should stay in the 0-256 range. The easiest way to guarantee this is to base our functions on sin and cos, since their result is always between -1 and 1. For clarity I will not discuss the normalization from floats between -1 and 1 to integers between 0 and 256 here so we can suffice with f being in the -1 to 1 range.

A simple example of the type of function we're looking for is then: f(x) = sin(x / 40.74). The 40.74 is to make our sin have a period of about 256. If we then map the output to a greyscale palette where 0 is black and 255 is white we get something like the picture on the right.

The second function

Looks nice, but it doesn't resemble a plasma effect, so let's investigate a little further for a nice function. Something that's used often in this effect is the sinus of a distance to a certain point in the canvas. Here's such a function: f(x, y) = sin(distance(x, y, 128, 256) / 40.74), where distance(x1, y1, x2, y2) is the distance between (x1, y1) and (x2, y2). In our case we're measuring the distance to the center of the bottom edge. Mapping this to our palette we'd get something like the picture on the right again.

Combining two functions

The last picture already looked a bit more like a good base for a plasma effect. The interesting stuff happens if we combine the two functions and average their results. As you can see in the picture on the right, this is getting pretty close to the effect we're looking for: flowing, asymmetrical blobs of color. Two things remain: finding a nicer palette of colors to map and adding in some animation.

Animation

Adding animation is reasonably straightforward. We just introduce a variable t in a strategic place to our functions which we continuously increment. Usually for these effects t starts at 0 and is incremented by 1 each step. In my experience floating point arithmetic isn't particularly slower than integer arithmetic in JavaScript so I chose to increment by 0.1 because it was more convenient. Here's an example of the 2 previous functions with the added t variable:

  1. f(x, t) = sin(x / 40.74 + t) - which gives the effect of the first pattern moving to the left.
  2. f(x, y, t) = sin(distance(x, y, (128 * sin(-t) + 128), (128 * cos(-t) + 128)) / 40.74) - which gives the effect of the second pattern rotating around the center of the canvas.

Colors

Believe it or not, we're not completely done with sinus and cosinus yet. The nicest effects are achieved with palettes that smoothly 'wrap around'. These types of palettes are usually generated with mathematical functions. Here's a few examples where for each palette we iterate i from 0-256 and the red, green and blue values are normalized to 0-256 integers again:

red(i) = sin(π * i / 32)
green(i) = sin(π * i / 64)
blue(i) = sin(π * i / 128)

red(i) = 0
green(i) = cos(π * i / 128)
blue(i) = sin(π * i / 128)

red(i) = cos(π * i / 128)
green(i) = sin(π * i / 128)
blue(i) = 0

Combining all this: the functions, the animation, the palette I ended up with the plasma effect shown above. It should be clear right now that there is an endless range of possibilities for variations on this theme. Like so many things I write about this started of as a little 'how does that work?'-experiment. This is how I think it works but if you're reading this and have a better idea, don't hesitate and let me know!

Have something to say about this post? Share your thoughts!