Classic Plasma Effect – Swirling Color Fields with Sine Waves

234 views 0 replies
Live Shader
Loading versions...

The plasma effect is one of the most iconic demos in computer graphics history, dating back to the demoscene of the late 1980s and early 1990s. At its core, it relies on layering sine and cosine waves across screen coordinates to produce smooth, organically shifting color fields. Despite its simplicity, the technique can produce strikingly beautiful visuals that feel alive and fluid. In this post, we will build a full plasma shader from scratch, break down every component, and explore the math that makes it work.

The Complete Plasma Shader

Below is a fully standalone WebGL1 fragment shader. Copy it directly into any GLSL sandbox that provides iResolution and iTime uniforms and you will see an animated, swirling plasma field.

precision mediump float;

uniform vec2 iResolution;
uniform float iTime;

/*
 * Classic Plasma Effect
 * ---------------------
 * Combines multiple overlapping sine/cosine wave patterns
 * evaluated at each pixel to produce smooth, animated color fields.
 * No textures or external data required — pure math.
 */

void main()
{
    // Normalize pixel coordinates to roughly [-1, 1] with aspect correction
    vec2 uv = (gl_FragCoord.xy - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);

    // Scale up so the pattern has more visible detail
    uv *= 3.0;

    float t = iTime * 0.6;

    // --- Layer 1: horizontal ripple ---
    float v1 = sin(uv.x + t);

    // --- Layer 2: diagonal ripple ---
    float v2 = sin(uv.y + uv.x * 0.7 + t * 1.3);

    // --- Layer 3: radial pulse from a moving center ---
    vec2 center1 = vec2(sin(t * 0.8) * 1.5, cos(t * 0.6) * 1.5);
    float d1 = length(uv - center1);
    float v3 = sin(d1 * 3.0 - t * 2.0);

    // --- Layer 4: second radial pulse, different orbit ---
    vec2 center2 = vec2(cos(t * 0.5) * 1.2, sin(t * 0.9) * 1.2);
    float d2 = length(uv - center2);
    float v4 = cos(d2 * 2.5 + t * 1.7);

    // --- Layer 5: swirl / rotational distortion ---
    float angle = atan(uv.y - sin(t * 0.3), uv.x - cos(t * 0.4));
    float radius = length(uv);
    float v5 = sin(angle * 3.0 + radius * 2.0 - t * 1.5);

    // --- Combine all layers ---
    float composite = (v1 + v2 + v3 + v4 + v5) * 0.2;

    // --- Color mapping ---
    float r = sin(composite * 3.14159 + 0.0) * 0.5 + 0.5;
    float g = sin(composite * 3.14159 + 2.094) * 0.5 + 0.5;
    float b = sin(composite * 3.14159 + 4.189) * 0.5 + 0.5;

    // Boost contrast slightly with a power curve
    vec3 color = pow(vec3(r, g, b), vec3(0.85));

    gl_FragColor = vec4(color, 1.0);
}

How the Plasma Pattern Forms

A plasma effect is essentially a sum of periodic functions evaluated at every pixel. Each function contributes a smooth value that varies across the screen, and when you add several of them together, you get interference patterns — regions where the waves reinforce each other and regions where they cancel out. This interference is what creates the blobby, organic shapes that define the classic plasma look.

The key insight is that none of these individual wave layers look particularly interesting on their own. A single sin(x) produces simple vertical stripes. But the moment you combine five or six layers with different frequencies, orientations, and moving centers, the result becomes complex and visually rich.

Breaking Down the Wave Layers

The first two layers are the simplest — linear sine waves that travel across the screen along different axes.

// Horizontal wave: stripes that scroll left to right
float v1 = sin(uv.x + t);

// Diagonal wave: stripes at an angle, slightly faster
float v2 = sin(uv.y + uv.x * 0.7 + t * 1.3);

By themselves, these just produce moving stripe patterns. The interesting behavior begins when we add radial components — waves that emanate from a point and expand outward like ripples in water.

// Compute distance from a center that orbits in a circle
vec2 center1 = vec2(sin(t * 0.8) * 1.5, cos(t * 0.6) * 1.5);
float d1 = length(uv - center1);

// Use distance as input to sine — creates concentric rings
float v3 = sin(d1 * 3.0 - t * 2.0);

The length() function computes the Euclidean distance from each pixel to the moving center. Passing that distance into sin() produces concentric rings. Subtracting time from the argument makes the rings expand outward over time.

The Swirl Component

What elevates this plasma from blobby waves to swirling vortex is the rotational layer using atan().

float angle = atan(uv.y - sin(t * 0.3), uv.x - cos(t * 0.4));
float radius = length(uv);

// Mixing angle and radius creates a spiral pattern
float v5 = sin(angle * 3.0 + radius * 2.0 - t * 1.5);

The two-argument atan(y, x) returns the angle in radians from the positive x-axis. By multiplying the angle by 3, we get three-fold rotational symmetry (three spiral arms). Adding the radius causes those arms to twist outward, and subtracting time makes the whole thing rotate.

Color Mapping with Phase-Shifted Sines

Once we have our composite value, we turn it into color by feeding it through sine functions with different phase offsets for each RGB channel.

// Phase offsets are 0, 2π/3, and 4π/3 — equally spaced around the circle
float r = sin(composite * 3.14159 + 0.0)   * 0.5 + 0.5;
float g = sin(composite * 3.14159 + 2.094) * 0.5 + 0.5;
float b = sin(composite * 3.14159 + 4.189) * 0.5 + 0.5;

Since 2π/3 radians is exactly 120 degrees, the three channels are evenly spaced around the unit circle. This guarantees that as the composite value sweeps from low to high, the color smoothly cycles through the entire hue spectrum.

Ideas for Experimentation

This shader is a great starting point for experimentation. Try adding more wave layers for additional detail, changing the color palette, adding turbulence by warping UV coordinates, or combining with distance fields to mask the plasma against shapes.

The beauty of plasma shaders is their simplicity. Every line is doing something mathematically transparent, yet the emergent visual complexity is far greater than the sum of its parts.

Moonjump
Forum Search Shader Sandbox
Sign In Register