Resman says hi.

Article contents


I recently had to write an intro screen for an Android app I'm working on. I decided to go old school and use some plasma. This article is the result of my investigations into recreating this well-known effect. Let's not build unnecessary suspense, here's what the final result looks like:

Building the plasma

The plasma is basically a function on 2D space created by adding together a few sinusoids. By combining different types of sines and adding a time component the illusion of motion is achieved. Below are some examples of different types of sinusoids that we can use, and an illustration with and without the time component:

The first sinusoid is simply taken along the x-axis. The coordinates of the squares on the right go from -0.5 to 0.5 in x and y.


Obviously it is also possible to take another sinusoid along the y-axis, or in a diagonal, or at any angle, and to make the angle change with time. Here's what it looks like.


The last type of sinusoid we can use is a concentric sinusoid starting from a point, here we can also animate it and move the center point around in a Lissajous figure:


I'm adding 1 to the square root term to avoid a visible dot at the center when it "folds" at zero.

We can then mix and match these functions and hopefully we get a nice plasma effect. Here I'm simply adding the 3 together.

Adding color

Now we've got something pretty cool looking, but obviously it's not as good as the one you saw in that demo at Assembly '92. Time to add some color.

To preserve the organic, fluid look of the plasma, the color scheme should not have discontinuities. However after adding our sines together, the plasma value is not necessarily constrained in a nice known interval like [0, 1]. An easy way to solve this problem is to take the sinus again of the value we obtained at the end of our plasma function, and use it to create the RGB components of the color. In the examples below r, g and b are the red, green and blue components of the color, with -1 being the lowest intensity (black), and 1 the highest (fully saturated color component).

GLSL implementation

Knowing the formulae, implementing them as a fragment shader in GLSL is straightforward:

Fragment shader
precision mediump float;
#define PI 3.1415926535897932384626433832795
uniform float u_time;
uniform vec2 u_k;
varying vec2 v_coords;
void main() {
    float v = 0.0;
    vec2 c = v_coords * u_k - u_k/2.0;
    v += sin((c.x+u_time));
    v += sin((c.y+u_time)/2.0);
    v += sin((c.x+c.y+u_time)/2.0);
    c += u_k/2.0 * vec2(sin(u_time/3.0), cos(u_time/2.0));
    v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+u_time);
    v = v/2.0;
    vec3 col = vec3(1, sin(PI*v), cos(PI*v));
    gl_FragColor = vec4(col*.5 + .5, 1);

The shader has two uniform parameters: u_k, used as a scale factor, and u_time, which is the current time in seconds. To avoid precision issues when using floats, the values should be kept relatively small. I encoutered issues when time went above a few minutes, so I had to periodically reset the time to zero. The plasma is actually a periodic function itself, in this case with a period of , so the looping of u_time back to zero can be made seamlessly at multiples of seconds.

The varying parameter v_coords comes from the vertex shader and contains the interpolated fragment coordinates. In my case I used a full screen quad with coordinates (-0.5,-0.5),(0.5,0.5), but it could also be used in place of a texture on any object for potentially interesting results.

Final words

Thanks to some simple math we achieved a pretty nice old school effect with a lot of variations. Obviously I had it easy with the fast computer graphics we have today, and I just calculated everything at each frame, but in the good old times the various sinusoids would have needed to be precomputed, and displayed with an offset or rotozoomed. For browser compatibility the examples on this page are written using an HTML5 canvas element, but they can also easily and efficiently be written as a fragment shader and rendered using OpenGL. Simply take a look at the source of this page for the JavaScript code.

Share this article :


Comment left by DurandA on 2017-03-25 15:08:23 :
I reimplemented the plasma outside of a shader and think that there is a small mistake related to the scaling.
When the pixel coordinates scale is really different than from your example the center movement won't be scaled properly. To fix this issue I had to replace every occurrence of u_k/2.0 with 0.5/u_k like so:
vec2 c = v_coords * u_k - 0.5/u_k;
c += 0.5/u_k * vec2(sin(u_time/3.0), cos(u_time/2.0));
Comment left by Garett on 2017-03-10 20:04:31 :
@lol WTF, rude. (And more characters because the form won't accept)
Comment left by lol on 2016-09-28 22:18:08 :
Jesus this is poorly explained.
Comment left by John on 2016-07-28 23:14:12 :
What does mean V in the plasma equations?
Comment left by k. dings on 2016-04-29 22:10:45 :
nice one!, I like.
Comment left by Tuukka on 2015-08-19 07:12:33 :
Neat, thank you for writing this tutorial. I used it as a basis for my plasma implementation on F#:
Comment left by philippe on 2015-05-20 19:50:04 :
Hello, I'm trying to get the javascript version working in Node.js, which doesn't have Canvas implemented, I am writing an app that drives a led matrix, I only need to know the colors at x,y.
I know it's a bit scandalous to ask, but could you help me to adapt this code ? I have tried a few other examples, but I find your output really nice.
Thank you in advance,
Philippe ( AT
Comment left by razor on 2014-07-05 02:19:33 :
this one here goes more in-depth:
Comment left by razor on 2014-07-05 01:56:02 :
Cool effects and functions, however the explanation is a bit lacking IMO.
Especially the relationship between the coordinates and the sinus curve and how this produces the visual effect.
I also don't get why you use xx and yy in your code instead of the real x and y value.

BTW, it would be nice if you could explain how this can be turned into a pre-generated constant table (other plasma programs I've seen use that approach, with the advantage of having to do the costly floating point calculations only once). the only clear part here is how you would prepopulate your palette of 256 colors.
Reply from the author :
x and y are the pixel coordinates while xx and yy are the coordinates in the [-0.5,0.5] domain, this makes it easier to compute sines that way without having to care for the actual pixel dimensions of the canvas.

For the purpose of this article I just wanted to recreate the effect so I didn't do any optimizations like precomputing the sines, it is unnecessary for simple JavaScript illustrations like these. As you can probably see the animations run just fine. The actual final implementation was done in GLSL and I don't think that precomputation is viable in this case.
Comment left by Raslanove on 2014-05-14 19:44:43 :
Thank you very much for the detailed explanation :)
Comment left by Brad on 2014-02-08 12:03:08 :
Thank you for this. very cool
Comment left by jumpifzero on 2013-12-31 00:27:18 :
Very interesting. thanks
Comment left by Dmitry on 2013-09-21 21:46:01 :
Hello! Do you know how can I reproduce this ( effect? I think this is plasma. Thank you!
Comment left by Jez on 2013-07-22 09:26:08 :
Cheers for the explanation, an ideal level of detail.
Comment left by Daniel on 2013-06-12 22:13:41 :
Thank you for all the effort, especially in visualizing every step!
My nostalgia revived with your clarity! :)
Comment left by greenbanksoundcloud on 2013-05-07 20:40:38 :
Thanks for taking the time to write & share, really useful and very nicely explained. Cheers!
Comment left by Jonathan on 2013-03-16 18:51:19 :
Nice! Explained very, very well!
Thank you!
Add a new comment :
Name :
Comment :

Answer the following question : Which is the biggest, an elephant or the Moon ?