Oldschool Demo Effects

This is a small collection of oldschool demo effects. These effects all use direct pixel manipulation to achieve their result. None of them use any hardware acceleration, or anything more advanced than a simple array of pixel data.

All effects are written in typescript. You can view the source on github. I also link to each individual effect alongside the demos below.


The rotozoom is a simple effect that goes through each pixel on the screen and maps the coordinate of that pixel backwards through a sin/cos transform to determine which coordinate of the source image to use. By applying a scale to that transform you also achieve the zooming part of the rotozoom effect. Finally, the source coordinate is wrapped using modulo to give a tiling effect.

You can view the source here.


The fire effect is a classic. The way it works is with an array of color indices into a palette. The palette is arranged so that it fades from black at the bottom of the palette, to orangish red in the higher indices. On each frame, the array is traversed and each pixel is averaged with adjacent pixels. It is then darkened slighly so that the pixels on higher rows are darker than the ones below it. The bottom row (just offscreen) is filled with random indices. This becomes the fuel of the fire effect.

Because the fire is darkened by a constant value, the fire is limited vertically. The classic fire effect is optimized for 55 pixels high. I do the same here, using the canvas to scale the output.

You can view the source here.


There are many versions of plasma. This particular version is a paletteless plasma which is created by adding multiple sin waves together, combined with distortion generated doing things like calculating the distance between pixels.

The color is determined by assigning different parts of the plasma to different color channels. Because I use the frame time for the inputs to the various functions, the plasma will distort and simplify given enough time.

You can view the source here.


To draw this effect, we loop through each pixel and calculate the distance from that pixel to each blob. Then we multiply all these distances together and divide by a constant. Then we subtract this value from another constant, and clamp the result. How we choose the various constants determines how big the blobs are and how fuzzy they are.

Changing the number of blobs or the resolution of the screen requires a lot of hand-adjusting the constants to give a good effect.

The blobs can move around however you like. I just have them running in various lissajous curves.

You can view the source here.


The tunnel effect is accomplished by using 3 arrays, each the size of the screen. The first array contains the distance from that pixel to the center of the screen, scaled and modulated by the height of the texture. The second array contains the angle from that pixel to the center of the screen. The third array contains a clamped distance from the center of the screen.

These arrays are calculated only once. Then they are used to calculate the coordinates of the texture. The first array determines the x coordinate of the texture. The second array determines the y coordinate. The third array determines how to darken the texture so that the center of the tunnel is blackened.

Finally, to pan the center of the tunnel around, we actually make the arrays twice the screen size and simply pan around the arrays.

You can view the source here.


The moire effect is rather simple. You have two points that act as the foci of the moire effect. For each pixel, you calculate the distances from that pixel to both points. XOR these distances together, and then divide by the desired ring width. If the result is odd, make it one color, otherwise another color. This produces the moire effect.

Again, you can move the foci points however you want, I again have them running in lissajous curves.

You can view the source here.


The rain effect is deviously simple. There are two arrays the size of the screen. Each element in the arrays represent the amplitiude of the wave at that location. The algorithm takes the previous array and averages the surrounding amplitudes and subtracts the current amplitude from that. Then the new amplitude is dampened in order to make the ripples die off. Then the arrays are swapped. This actually causes the amplitude to oscillate like a wave. We then use the amplitude to distort our texture, calculating the delta amplitude from left to right and top to bottom. These deltas are then used to modify the texture's coordinates and darken the resulting pixel.

You can view the source here.