If you just want to look at the patterns as they are randomly generated
When you want to see a different pattern, just refresh that page (Ctrl+R).
You can also try
if you want to have a black background.
If you are interested in one specific pattern, you can click these links. They still will be randomly generated, but only of the chosen type. Refreshing the page restarts the animation with different starting parameters.
Circles
Squares
Continous line
Line to a fixed point
Line between linked points
Line between separately moving points
Connect edges
Connect edges with more separated lines
Connect edges irregularly
Sine line
Wing pattern
Scribble
Spirograph
Epicycloid
Harmonograph
Spring
Triangle spring
Polygon dots
To set dark mode for the patterns, write ?darkmode=true after their url, like this:
https://mircuskasarok.neocities.org/hexcode_screensaver/specific_patterns/01_circles?darkmode=true
Or use the checkbox below:
It's completely fine if you only want to enjoy the nice patterns, but I've had a lot of fun making this, and I'd like to write about the math and programming behind it.
On the computer screen, each color is displayed by combining red, green and blue light in each pixel. If we only have red, green or blue light, we get red, green or blue colors. If we add no colored light, we get black. If all the colors are on maximal value, the result will be white. (White light contains a mix different of wavelengths, just think of the Sun's light, when it meets any surface, we see one of the countless possible colors, created by absorbing and reflecting different wavelengths of the Sun's white light. I won't go more in detail here, but it's an interesting topic, you can read about it, but at the end you will probably reach the conclusion that colors aren't real.) By changing the amount of each colored light, the final color changes.
So, how can we work with this? We have to tell the computer what color to display, how can we communicate it? First of all, there are dictionaries that both computers and humans can understand, standard color names that are recognized. You can for example tell the computer to display a text in green, white, or if you want to get fancy you can even try deep sky blue or medium violet red. There is a lot of them. But there are more colors a screen can display than for how many we could reasonably give names.
The obvious solution is to represent the colors by numbers. There are three common ways for this. HSL, RGB and the hexcodes. HSL works in a different way, it sets the hue-saturation-lightnes values of a color, I won't go into detail about it here. RGB and the hex values are linked. RGB stands for Red, Green and Blue, the three colors that are used for creating all the colors on the screen. The amount of each color is given with a number, and this can be understood by the computer. Each color's strength is a value between 0 and 255. So as you see, rgb(255, 220, 0) contains maximal red (first number), some green (second number) and no blue (last number). Hexcodes are a shortened, more symbolic and uniform way to express the same thing.
The hex values always have the format of a hashtag followed by six characters. Like this: #00e6ac. The first two numbers stand for the red, the second two for the green, and the last two for the blue. But, as you see, two digits should represent a numeric value that could often be three digits long. This is because the numbers of the RGB values are translated to base 16, or hexadecimal, this is why they are called hex values.
How does hexadecimal work? I everyday life we use base 10 (decimal). It's a numeric system that has 10 distinct digits (0123456789). If we reach the biggest digit (9), we add a new decimal place, increase that by one, and set the one before to zero (This is how we get 10, and can easily represent big numbers). 332 for example means, that we have three times hundred, three times ten and two times one. But what happens if we have more than ten possible digits? We don't have to introduce a new place until we used all the ones we have. In hexadecimal there are 16 possible digits (0123456789abcdef). "a" represents 10 in a single digit. "b" is 11, "c" is 12, "d" is 13, "e" is 14 and "f" is 15. (If it confuses you why there is no digit for 16, think about it, base 10 has no single digit for 10. It comes when all the digits were used up. So how will 16 be written in hexadecimal? It will be "10".)
How can you convert a hexadecimal number to decimal? There are two digits, the one on the left represents the "16"s, the one on the right represents the "1"s. So as we went through the meaning of 332 in decimal, let's see for example what a hexadecimal "b4" translates to. "b" means 11, and it stands on the place of "16"s, so it's 16*11=176. On the place of "1"s there is a "4", so it means 1*4=4. These together mean 176+4=180. And what does "ff" mean? "f" is 15. Following the previous method, we get 16*15 + 1*15 = 240+15 = 255. And look, this is the maximal value we can represent with two hexadecimal digits! And what was the highest RGB value a color could take? 255. Now you see, this is the reason behind it.
So seeing this, we can start to combine the three hexadecimal values for the colors.
#000000 is black (0 of all colors)
#ff0000 is red (255 of red, 0 for the others)
#00ff00 is green (255 of green, 0 for the others)
#0000ff is blue (255 of blue, 0 for the others)
#ffffff is white (maximal value of all colors)
And if we combine them, #9933ff for example has a good amount of red, some green and maximal blue.
If you want to, you can experiment with it a bit:
#00e6ac
I wanted to make the color gradually change in a way that makes it possible that every color can appear There are 16777216 possible hex colors (more than 16 million). So my idea was to handle the whole hex value as one single number, and not as a composite of three separate things. And if I increase it one by one (000000, 000001, 000002 ... ffffff), it will go through all possible colors. It would require several days, but if you would leave the page running for that long, it would cycle through them all. This means that the red value is mostly fixed, the green slowly changes and the blue flips rapidly in each frame.
The way I actually solved this (steadily counting up in hexadecimal, and using these values as the color of the patter), is a bit complicated. I'm sure there are good tools for handling hexadecimal numbers, but I felt like I could program this manually. I've made two lists, one consisted of the 16 possible digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f), and the other one consisted of six random numbers between 0 and 15. The second list represents the actual hexcode, each element can be paired with an element from the first list. How the actual counting works is the following: the function looks at the last element of the second list. If it's not the greatest possible value (15), it increases it by one. If it is 15, then it looks at the digit on it's left, and increases that one by one, and sets the one on the right to zero. If this one was 15 too, it goes left again, until it finds une that can be increased. After this, it returns to the rightmost digit. If ffffff is reached, it resets to 000000.
This is the logic that is followed in each step. So for example [2, 11, 7, 9, 15, 15] will change to [2, 11, 7, 10, 0, 0]. When it's done, the six digits of the second list are translated to hexadecimal one by one, with the help of the first list, and are used as the color of the animation.
This also means that there are sudden color changes in the colors, which don't look that well. In the version of the site I linked from the main page the colors loop, and don't infinitely change. Two hues (red and blue) are fixed, only green changes gradiually from 00 to ff and then back to 00. You can see these looping colors clearly, without moving animation, here.
The random number generation in javascript is a bit convuluted. There is no built-in function for getting a whole number between two values.
But there is one for getting a random fraction between 0 and 1, it's "Math.random()". So the standard way to get a random number uses this one. If you multiply it with a number, that one will be the maximal number it can generate, since the function can never return a value greater than 1, the minimal value is still 0. (This maximal number is a limit that is never reached, so if you write 10 here, 9 will be the greatest possible generated value.) If we add a nunber to the value we got, that one will be our minimal value, since it's always there, no matter what random number is generated. But in this case the minimal value has to be subtracted from the maximal before the multiplication. If it wasn't we could get numbers that are bigger than the maximal value. If we have all of this , we have random number between the min and max values, but it's still a fraction. Luckily javascript has a built-in function for rounding the numbers downwards, it's "Math.floor()". (It's a good practice in this case to round them downwards, because a simple rounding would result in the smallest and the greatest numbers appearing with half the probablity of the other numbers.) So let's look at how this function works, if we would want to get a number between one and 10:
Math.floor(Math.random()*(10-1)+1)
So this is how random numbers can be generated in javascript.
Making a canvas element is simple, you just write the tags "<canvas"></canvas>" in the HTML code. But it doesn't have much use this way yet. More practical thing is to write is something like:
"<canvas id="canvas-one" width="200" height="100" style="border:1px solid black;"></canvas>"
It needs an id, if we want to draw on it with javascript, the width and height specifies it's size and I gave it here a border, so it's visible in the page.
The canvas element is basically a coordinate system of pixels. There are x (horizontal) and y (vertical) coordinates, and they show how far a point is from the sides of the canvas. Important thing to note is that the (0, 0) point on the canvas is the top left corner. So the increasing x coordinates goes right, the increasing y coordinates goes downwards. On the canvas you can select a bunch of pixels by using the coordinate system, and tell them what to do.
To start, head over to the javascript file, or the script section of your HTML file. Define your canvas with it's id, and you should also call the "getContext(2d)" function on it. The context is the thing you will use for drawing on the canvas.
const canvas = document.getElementById("canvas-two");
const ctx = canvas.getContext("2d");
After this, you can actually work with the pixels you need. You can draw a line, a rectangle, an arch, fill these, draw an image, or write text there.
To draw a line, you have move the context to the starting point, draw a line to the end point, and then stroke the selected line. Like this:
ctx.moveTo(0, 0);
ctx.lineTo(300, 200);
ctx.stroke();
If what you need is not a single straight line, you can draw a path. Drawing paths is also the way for arches and circles. To make a circle, you have to give a lot of parameters. An example for that is:
ctx.beginPath();
ctx.arc(250, 100, 40, 0, 2 * Math.PI);
ctx.fill();
The first two numbers are the x and y coordinates (the position on the canvas). The third number is the radius of the circle (it determines the size).
The last two numbers are the start and end angles of the arc, for a circle the arc goes all the way around, from 0 to 360, but it's written as 2*Math.PI in radian.
Drawing a rectangle is much simpler. You have to give the coordinates of the rectangle's top left corner, and the width and height of it. Like this:
ctx.fillRect(40, 50, 120,100);
The color of these shapes can be set before they are drawn. This is something that has an effect on the whole canvas, so you have to change it to something different, if you want to draw a second shape with a second color. It works like this for example:
ctx.fillStyle = "green";
ctx.strokeStyle = "#0000ff";
So this the second canvas after setting the colors and drawing these things:
As I said before, the canvas is a coordinate system of pixels. It only cares about pixels. If you draw something on it, they are just pixels too, the canvas won't remember the previous things. You can't take a circle, and tell it to go slightly to the left, because the canvas doesn't store any information about your circle. So what you do is to tel the canvas to delete everything, and then you draw a new circle, slightly to the left. And you repeat this until you need it.
To make an animation, you have to request animation frames. In each frame the canvas is checked, and the neccesary steps are made, then it calls the next frame. A frame lasts for a fraction of a second.
Let's see for example a movement that goes side to side. The x coordinate is increased, until it reaches the end of the canvas, then it starts to decrease (blue ball). Same can go for the y coordinate (red ball). And we can ever combine the two (green ball).
Let's see the code. How do the coordinates move?
var x = 0;
var y = 0;
var xSpeed = 1;
var ySpeed = 1;
function moveCoordinates() {
if (y > 200 || y < 0) {
ySpeed = -ySpeed;
}
if (x < 0 || x > 300) {
xSpeed = -xSpeed;
}
x += xSpeed;
y += ySpeed;
};
So we have the x and y coordinates, and the speed they move with. (It doesn't have to be these values. )
The function checks if the x or y coordinate would become too big or too small. If so, it reverses the speed, so what has been going downwards so far, will go upwards.
The x and y coordinates are moved a bit away in each step by the amount of speed in their direction.
After the coordinates' movement work, they can be used for actually drawing things on the canvas. We just use them, instead of static numbers, in the functions that create the shapes.
const animate = () => {
ctx.clearRect(0, 0, 300, 200);
moveCoordinates();
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(10, y, 10, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(x, 10, 10, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = "green";
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.fill();
window.requestAnimationFrame(animate);
};
Let's see what it does. At first it deletes everything from the canvas (ctx.clearRect(0, 0, width, height);). This is so that the moving circles won't leave a trail behind. Then it calls the function you read a few paragraphs ago, the one that changes the x and y coordinates. Then it draws three circles, the color is changed before each. The only difference between drawing them is in their coordinates. They are the first two values you have to give for drawing a circle. If the constantly changing x or y variable is used in this place, the circle will be drawn on this changed coordinate. The last step is requesting the next frame (which will again clear the canvas, move the coordinates, draw the circles, and request the next frame...)
The patterns in this program are all created by leaving the trails of the moving shapes on the canvas.
This means that none of them cleans the canvas between the frames, and all the previous positions remain visible until overwritten.
It's purpose is to to visualize movement.
The color is changed in each frame with the hexcode method I explained above. This results in going back and forth between two colors, as thew both slowly change, and once in a while there is a bigger jump.
The movement in many cases is based on the bouncing screensaver movement I described before, with the x and y coordinates wander between the edges of the canvas independently. Where there's something more, I will go into detail there.
There are many random-generated parts, like the starting coordinates, the shape size or line width, the speed in different directions and the starting color, so refreshing the page always gives a different pattern.
The bouncing screensaver movement, with circles being drawn around the moving coordinates. The amount of separation between them depends on the speed and the size. If they are bigger and faster, they can look like a line.
The bouncing movement. The moving coordinates determine the top left corner of the square, it's width and height is randomly decided upon opening the page.
Here a line is drawn along the bouncing movement. A path is started (beginPath), the starting point is given as the x and y coordinates from the previous frame (moveTo). After this the coordinates are moved, and a line is drawn to the new, changed coordinates (lineTo). Finally the "stroke" function is called.
It looks different, but works really similar to the one before, a random point is chosen when the page is loaded, and in each step the line is drawn always from this point to the moving coordinates, not from the previous position.
Two random points are chosen, and both of them are moved by the same speed in the x and y directions. These points are then connected to each other in each frame.
There are two points with their own, independent speed in the x and y directions. They both do the bouncing movement, and are not linked. They are connected in each frame.
This one works differently. Here the first point starts at the (0, 0) coordinate, the second one is chosen to be on the edge of the canvas (either x or y is 0 or max, but they have to be on the further half of the canvas).
Both points move counterclockwise with the same speed. If they are on the left side, they will go downwards, if they are on the bottom, they will go right, on the right upwards, on the top left.
It's all checked through the coordinates of the previous frame. Since the direction is always fixed, there is no need for separate speed variables for the separate coordinates.
In each frame after they move, they are connected together.
It usually results in an empty diamond or eye-shape in the middle.
Similar to the one above, but it counts the frames, and the line between the points is drawn only in every fourth frame, so there is more space between them, and the movement is less fluid.
Similar to the previous two examples, but the second point's speed is not constant, it's decided randomly in each frame. The points still both move only on the edges. It results in a less ordered pattern.
For the next few examples the sine and cosine functions are used.
I'll try to make a short summary about them, I hope it will make sense.
These functions are used for calculations with angles. If you start drawing a circle with the radius length of 1, you draw an arc of 360 degrees. A half-circle has the half of it, 180 degrees.
If you start at a fixed point and move along the circle's outline, you can reach any angle you'd like to. 60 degrees is 1/6 circle, 90 degrees is 1/4 circle.
We put this circle with the 1 unit length of radius into a coordinate system, choose the (0, 0) point as the center, and we try to draw the angles along the circle's outline again.
Now we can measure the distance of the end point at the angle from the x and the y coordinates.
The y coordinate is given by the sine (blue line), the x coordinate is given by the cosine (green line).
As we slowly increase the angle, we can observe how the x and y coordinates change. They both periodically increase and decrease, but they only have the same value in four points. Their values follows each other with a delay.
As you can see, the values of sine and cosine are always between 1 and -1, since the radius of the circle is 1, and so their distance from the axis can't be greater than 1.
A really important property of these functions is that they are periodical.
If the angle would be greater than 360, the rotating radius (green) would just go around further, and the values of x and y would be the same as in the first round.
So for example 540 degree (360+180) have the same sine and cosine values as 180.
No matter how big number we try, the sine and cosine will be between 1 and -1, because the radius length won't change.
An other important thing to note is that these functions in javascript work with radian, not degrees.
Radian is basically the length of the circle part between the starting point and the end of the angle with 1 as radius length.
The ratio of the diameter and the whole circle is described with π (can be called with Math.PI in javascript), an infinite and irrational number, that is usually rounded to 3.14.
The diameter is twice as long as the radius, and if the radius is 1, the whole circle outline will be 2*π.
We see that a half circle's length is exactly π, if the radius is 1, and the angle for a half circle is 180 degrees, so one degree in radian is exactly π/180.
Knowing this, we can calculate the radian from the degrees with the following formula: radian = degree*π/180.
If the radius is not one, and you want to use the sine and cosine functions for the coordinates, you have to multiply it with the real radius.
To have a point that moves around a center, the following function can be used, while the degree is constantly increased:
(center.x + radius*Math.cos(deg*Math.PI/180), center.y - radius*Math.sin(deg*Math.PI/180));
For the calculation of the y coordinate beware that on the canvas
upward is the negative direction.
These functions give a really nice, swinging and continous movement with smooth turns. Really, you can do almost anything with them, it will look cool.
In this first pattern the x coordinate simply moves back and fort, but the y is determined by a sine function. It gives wavy lines in the middle of the page.
This pattern combines the bouncing movement with a sine function. A line is always started at the next coordinate of the bouncing and ended at a point with it's coordinates shifted so that its y coordinate has the value sine function added to it.
This is very similar to the previous wing pattern, but looks much less organized. It connects the coordinates of a bouncing movement combined with an added sine value. The bouncing movement is a bit less apparent.
This one is really interesting. I got the main thought from this site: https://engaging-data.com/interactive-spirograph/ Its motion is a bit complicated to visualize. There are two circles, one is static, the other one rolls around inside the first one. There is a point somewhere on the inner circle, and the motion of that point is followed. The sizes of the circles and the position of the point are the things that determine the end result.
To my luck, wikipedia had the function that describes this movement, so I didn't have to figure it out. Here is how the code works:
x = centerX+((R-r)*Math.cos(theta)+d*Math.cos(theta*(R-r)/r));
y = centerY-((R-r)*Math.sin(theta)-d*Math.sin(theta*(R-r)/r));
This calculates the next position of the point, and a line is drawn there from the previous position.
The size of the inner circle and the moving point's distance from the center is randomized.
This is really similar to the one before. It works with two circles again, but it works so that one moves around the other, not inside it. I took the formula from wikipedia again. Here is the code I used for the coordinates:
x=centerX+((R+r)*Math.cos(theta)-re*Math.cos(theta*(R+r)/r));
y=centerY-((R+r)*Math.sin(theta)-r*Math.sin(theta*(R+r)/r));
The r and R are calculated in a way that together they give the half-length of the screen's height, so the result will always be the size of the screen.
This is the third one I got from wikipedia. This imitates the motions of two connected pendulums. I honestly copied part of the code from here , and having an example helped figuring out what parameters I can randomize, and what should be fixed on which values. These patterns usually repeat themselves in a decreasing and distorting way.
This pattern draws circles around a point that bounces around. The circle's coordinates are calculated with sine and cosine (the angle changes a bit in each frame), but their center moves too. So the central coordinates and the angle change constantly as it was discussed before, and they both move like this:
x= centerX + (r*Math.cos(alpha));
y= centerY - (r*Math.sin(alpha));
Works similar to the circular spring, but here the angle changes with a fixed value ((Math.PI * 2) / 3, the third of 360 degrees), to make triangles.
All three lines are drawn in one frame, this is why it's faster than the circular one.
This also means that it distorts less.
This could be done with any regular shape, not just triangles, you just have to adjust the number you divide 360 degrees with, and the number of lines you draw.
This is a bit different from the rest. Here a random point is chosen, and six circles in the shape of a hexagon are drawn around it.
The coordinates for the points of the hexagon are calculated like the points of the triangle before,
360/6 degrees are added to the last angle, and then the sine and cosine of this angle are used for y and x.
These six coordinates are collected into a list, and one of them is chosen as the next center.
Now six points are drawn around the new center, and this repeats.
If the edge of the screen is reached, the center coordinates become the middle of the screen.
This could be done with other regular shapes too, but hexagons look the best.