🌈 Animated Plasma in JavaScript

Introduction

The plasma effect is a classic demoscene effect that creates fluid, colorful animations resembling swirling clouds or flames. In this tutorial, you'll learn how to create this effect from scratch using pure JavaScript and the <canvas> element.

Step 1: HTML Structure

Let's start with a basic HTML structure with a canvas element:

<canvas id="plasmaCanvas" width="400" height="400"></canvas>

Step 2: Canvas Initialization

The first step is to get the 2D canvas context and prepare a pixel buffer:

const canvas = document.getElementById('plasmaCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;

// Create an ImageData object for pixel manipulation
const imageData = ctx.createImageData(width, height);
const data = imageData.data; // Array of RGBA pixels
💡 Tip: The data array contains RGBA values for each pixel. Each pixel takes up 4 elements: Red, Green, Blue, Alpha.

Step 3: The Math Behind Plasma

The plasma effect is based on overlapping sinusoidal functions. For each pixel (x, y), we calculate a value using a combination of sin() and cos() functions:

function plasma(x, y, time) {
    // Different sine waves with different frequencies
    const value = 
        Math.sin(x / 16.0 + time) +
        Math.sin(y / 8.0 + time) +
        Math.sin((x + y) / 16.0 + time) +
        Math.sin(Math.sqrt(x * x + y * y) / 8.0 + time);
    
    // Normalize the value to range 0-1
    return (value + 4) / 8;
}

Each of these sinusoidal functions creates a different pattern:

Step 4: Color Mapping

We convert the plasma value (0-1) to RGB colors using sinusoidal functions with phase shifts:

function getColor(value) {
    // Use sin() with different shifts for RGB
    const r = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2));
    const g = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 2));
    const b = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 4));
    
    return [r, g, b];
}

Step 5: Rendering

Now let's combine everything into a render function:

function render(time) {
    // Convert time to seconds and slow down
    time = time / 1000;
    
    // For each pixel
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            // Calculate plasma value
            const value = plasma(x, y, time);
            
            // Convert to color
            const [r, g, b] = getColor(value);
            
            // Set pixel in buffer
            const index = (y * width + x) * 4;
            data[index] = r;       // Red
            data[index + 1] = g;   // Green
            data[index + 2] = b;   // Blue
            data[index + 3] = 255; // Alpha (full opacity)
        }
    }
    
    // Draw buffer to canvas
    ctx.putImageData(imageData, 0, 0);
}

Step 6: Animation Loop

The final step is creating an animation loop using requestAnimationFrame:

function animate(time) {
    render(time);
    requestAnimationFrame(animate);
}

// Start the animation
requestAnimationFrame(animate);

Demo: See the Effect in Action

Complete Code



Here's the complete source code for the plasma effect:

const canvas = document.getElementById('plasmaCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const imageData = ctx.createImageData(width, height);
const data = imageData.data;

function plasma(x, y, time) {
    const value = 
        Math.sin(x / 16.0 + time) +
        Math.sin(y / 8.0 + time) +
        Math.sin((x + y) / 16.0 + time) +
        Math.sin(Math.sqrt(x * x + y * y) / 8.0 + time);
    return (value + 4) / 8;
}

function getColor(value) {
    const r = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2));
    const g = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 2));
    const b = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 4));
    return [r, g, b];
}

function render(time) {
    time = time / 1000;
    
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const value = plasma(x, y, time);
            const [r, g, b] = getColor(value);
            const index = (y * width + x) * 4;
            data[index] = r;
            data[index + 1] = g;
            data[index + 2] = b;
            data[index + 3] = 255;
        }
    }
    
    ctx.putImageData(imageData, 0, 0);
}

function animate(time) {
    render(time);
    requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

Experiments and Modifications

Now that you have a working plasma effect, you can experiment with different parameters:

1. Change Wave Frequency

Modify the divisor values (16.0, 8.0) in the plasma() function to create larger or smaller patterns.

2. Add More Waves

Add more sinusoidal functions to the sum, for example:

Math.sin(x / 32.0 - time * 0.5)

3. Change Color Scheme

Experiment with different phase shifts or use other functions for color mapping.

4. Change Animation Speed

Multiply time by a factor to speed up or slow down the animation:

time = time / 1000 * 0.5; // Slow down by half
💡 Optimization: For better performance, you can pre-calculate values or use a smaller canvas size with CSS scaling.

More JavaScript tutorials:

Spinning squares - visual effect (25 lines)

Oldschool fire effect (20 lines)

Fireworks (60 lines)

Animated fractal (32 lines)

Physics engine for beginners