How to Create a Matrix Rain Effect with JavaScript

Mohammad SarabiMohammad Sarabi
5 min read

We all remember the iconic raining code from The Matrix. If you have ever wanted to create a similar effect in your own project, this tutorial will show you step by step how to create it.

This tutorial is an oversimplified version of my rain-fall library, which is fully customizable with fonts, colors, size, and more, and is fully responsive. Here is a link to the GitHub repository where you can find the complete source code, examples, and a demo.

A matrix-style effect interface with options for font, character size, character range, background, foreground, speed, density factor, and resizing the effect screen.

The rain-char library demo is available in m-sarabi.ir/rain-char

Getting Started

First, let's set up our environment. We will create an HTML file where we'll include a canvas element for rendering the rain effect:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RainChar Effect</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            display: block;
            background-color: black;
        }
    </style>
</head>
<body>
<canvas id="rain-effect"></canvas>
<script src="script.js"></script>
</body>
</html>

In this HTML setup, we have a full-screen canvas where our rain effect will be rendered.

The RainChar Class

Let's now walk through the core functionality of our RainChar class.

1. Constructor

In the constructor, we first define the parameters of our rain effect: font, size, characters, background color, and font color.

Next, we set up the canvas, determining its size and filling in the background color.

Finally, we create an array to store each character's position and size.

class RainChar {
    constructor(font, charSize, chars, bg, fg) {
        // Defining the parameters
        this.font = font;
        this.charSize = charSize;
        this.chars = chars;
        this.bg = bg;
        this.fg = fg;

        // Setting up the canvas
        const canvas = document.getElementById('rain-effect');
        this.context = canvas.getContext('2d');
        this.size = [canvas.offsetWidth, canvas.offsetHeight];
        canvas.width = this.size[0];
        canvas.height = this.size[1];

        this.context.fillStyle = this.bg;
        this.context.fillRect(0, 0, ...this.size);

        // Creating the particles array
        this.particles = [];
        const particleCount = this.size[0] * this.size[1] / (this.charSize ** 2) / 10;

        for (let i = 0; i < particleCount; i++) {
            // We will create an array of particles using the newParticle method that we define later in the code.
            this.particles.push(this.newParticle());
        }
    }
}

The number of characters is roughly one-tenth of the area of the canvas divided by the size of the character.

2. Creating New Particles

We represent each particle as an object with a random position and size.

    newParticle() {
        return {
            x: Math.random() * this.size[0],
            y: -Math.random() * this.size[1] * 2,
            size: Math.floor(Math.random() * (this.charSize * 2 - this.charSize / 2) + this.charSize / 2),
        };
    }

Particle size is a random value between half and double the character size.

Each particle will be positioned somewhere outside the canvas at the top, later we animate them downward.

3. Drawing Particles

Now that we have an array of particles (we created the array of particles in the constructor using the newParticle method), We have to render each one on the canvas. We define the drawParticles method to handle the rendering:

    drawParticles() {
        this.context.fillStyle = this.fg;
        this.particles.forEach(particle => {
            this.context.font = `${particle.size}px ${this.font}`;
            const randomChar = this.chars[Math.floor(Math.random() * this.chars.length)];
            this.context.fillText(randomChar, particle.x, particle.y);
        });
    }
  1. We set the fill style to the font color.

  2. For each particle, we set the font size and get a random character from the chars.

  3. Lastly, we render the particle on the canvas with the fillText method.

4. Updating Particles

For each particle, after we draw it on the canvas, we need to move it downward by the particle size, simulating the falling effect.

    updateParticles() {
        this.particles.forEach(particle => {
            if (particle.y > this.size[1]) {
                Object.assign(particle, this.newParticle());
            } else {
                particle.y += particle.size;
            }
        });
    }

We check if the particle has reached the bottom of the canvas. If it has, we reset it by assigning it a new particle using the newParticle method.

5. Clearing the Canvas

Before redrawing the particles, we need to clear the canvas. We do this by partially erasing the previous frame, creating a smooth trail effect.

    clearCanvas() {
        this.context.globalAlpha = 0.25;
        this.context.fillStyle = this.bg;
        this.context.fillRect(0, 0, ...this.size);
        this.context.globalAlpha = 1;
    }

With the globalAlpha property we control how strong the erasing is. It can control the length of the trail.

6. The Play Method

Finally, we need to create the animation by continuously clearing the canvas, drawing particles, and updating their positions.

    play() {
        this.clearCanvas();
        this.drawParticles();
        this.updateParticles();
        setTimeout(() => {
            this.play();
        }, 50);
    }

The play method calls the clearCanvas, drawParticles, and updateParticles methods in a loop, using the setTimeout to call the play method every 50 milliseconds for roughly 20 frames per second, with higher frame rates resulting in faster animation.

Putting It All Together

We can now create an instance of the RainChar class and start the animation by calling the play method:

const chars = 'ABCDEFGHIJKLMNOPRSTUVWXYZ';
const rain = new RainChar('monospace', 20, chars, 'black', 'lime');
rain.play();

We have created an instance of the RainChar class and called the play method to start the animation.

You can see the code of this tutorial in action in the Codepen below:

Conclusion

With RainChar, you can easily add a dynamic and eye-catching rain effect to your web projects. The library is fully customizable, allowing you to experiment with different fonts, colors, and character sets, and is responsive to resizing. Try it out, and see what stunning visuals you can create!

Feel free to explore the GitHub repository for more examples and customization options. If you have any questions or improvements, don’t hesitate to drop a comment below! If you liked it please share it with your friends!

10
Subscribe to my newsletter

Read articles from Mohammad Sarabi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mohammad Sarabi
Mohammad Sarabi

I am a data analyst and frontend dev from Iran.