Lets build #1 - Matrix rain
Have you ever thought to yourself: “How the hell do I make that damn matrix rain animation thing in JavaScript”?
You probably have not, but anyways, I need this letter falling down animation for a million user app landing page that I am building right now. Since this should be a relatively easy thing to make I decided to make it a tutorial to hopefully help you if you want to build something similar.
The research
Bu surfing the web I have found a couple websites for reference:
- https://matrix.logic-wire.de/
- https://www.quenq.com/matrix/index.html
- https://codepen.io/tommyho/pen/NWZRvwj
It seems that all these examples use the canvas element to animate the letters. We will go this way as well.
Initialising the project
First things first, go to your project folder and create html css and js files
with $ touch index.html script.js style.css
. After this paste this boilerplate
into the html and css:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Matrix rain project</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<canvas class="m-rain" width="1380" height="668">
Your browser does not support the canvas element.
</canvas>
<script src="script.js" type="module" defer />
</body>
</html>
/* style.css */
html,
body {
padding: 0px;
margin: 0px;
}
canvas {
border: 3px solid black;
}
We just have a canvas element that indicates wether the browser supports such
element. We will do most of our work inside the script.js
file.
Working with the canvas element
In order for us to draw something to the canvas element we will have to get the 2d canvas context. This object has all the methods we need to draw anything we want.
// script.js
/** @type {HTMLCanvasElement | null} */
const canvas = document.querySelector("canvas.m-rain");
// Set width and height to window
canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;
// Get the canvas context
const ctx = canvas.getContext("2d");
// Paint the entire canvas black
ctx.fillRect(0, 0, canvas.width, canvas.height);
Rendering text
As we will need to render a lot of text on the screen, lets try to just render
the good old “Hello world!”. The method to do so is fillText()
.
// script.js
// ...the code from before
// Set the fill style so the text is not black
ctx.fillStyle = "white";
// Make the font bigger
ctx.font = "34px sans";
ctx.fillText("Hello world!", 100, 100);
This will basically tell the canvas to render the “Hello world!” string at coordinates (100, 100);
This is how the page should look like right now:
Making things move
Okay so we now know how to render some text, but this is now why we came here, we want to make some matrix rain god damnit! Let’s animate some letters right now.
Firstly we will some variables to keep track of all the letter positions, font sizes and so on…
// script.js
// Letter that we want to animate
const letter = "O";
/** @type {[Number,Number][]}
* This is where we will store the x and y axis for our falling letter
*/
const letterPositions = [];
const fontSize = 8;
Then we will want a function that spawns the letter at a random position above our canvas:
//script.js
function randomSpawn() {
// Random x
const x = Math.round(Math.random() * canvas.width);
// Above the canvas
const y = -10;
letterPositions.push([x, y]);
// Keep the max letter count up to 500 for performance
if (letterPositions.length > 500) {
letterPositions.shift();
}
}
Okay now we have our variables and a method that spawns a new letter into our
array. The last thing that we need for now is a tick()
method that would act
as like a render method. The tick()
needs to refresh the screen, then draw
all the letters from the array and then nudge the letters down a bit. I
came up with something like so:
// script.js
function tick() {
// We refresh the page with a black that has a little transparency for a nice effect
ctx.fillStyle = "#0000000a";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Set up the white font
ctx.fillStyle = "white";
ctx.font = `${fontSize}px sans`;
// For every letter draw it to the screen
for (const pos of letterPositions) {
ctx.fillText(letter, pos[0], ++pos[1]);
}
}
Finnaly put both the tick()
and randomSpawn()
inside an interval. You can
play around with the time values for different effects.
//script.js
setInterval(tick, 40);
setInterval(randomSpawn, 100);
Your page should look something like this:
Its kinda mesmerizing huh? I caugth myself staring at this screen for way too long dude.
The finishing touches
It may not seem like it, but we are actually done. I sugest that you try and do the rest alone, since it is a nice exercise. The finished product should look like this:
If you want to see the code here it is aswell:
//script.js
/** @type {HTMLCanvasElement | null}*/
const canvas = document.querySelector("canvas.m-rain");
canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;
const ctx = canvas.getContext("2d");
ctx.fillRect(0, 0, canvas.width, canvas.height);
const letters = "马吾伊艾哦儿屁艾勒艾诶娜迪艾弗吉尺艾杰艾艾西吉比艾开";
/** @type {[Number, Number, Number][]} */
const letterPositions = [];
const fontSize = 18;
const numOfColumns = 20;
const maxLetters = 500;
function randomSpawn() {
const x = Math.round(Math.random() * canvas.width);
const y = -10;
const s = Math.floor(Math.random() * 25 + 5);
letterPositions.push([x, y, s]);
if (letterPositions.length > maxLetters) {
letterPositions.shift();
}
}
function tick() {
ctx.fillStyle = "#0000002a";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "green";
for (const pos of letterPositions) {
ctx.font = `${pos[2]}px sans`;
ctx.fillText(
letters.split("")[Math.floor(Math.random() * letters.length)],
pos[0],
pos[1],
);
pos[1] += pos[2];
}
}
setInterval(tick, 50);
setInterval(randomSpawn, 5);
Comments