Random walkers
- Mamboleoo

- Jun 20, 2020
- 7 min read
Updated: Jan 20, 2022
Maybe you have heard about random walkers but you don't know how to use them in generative art.
Today we will see how you can control walkers to make them less random and produce more interesting results like this.
💡 Click anywhere to render a new output
🎈 In this tutorial I will be using p5.js for my demos. If you are familiar with Processing, you will quickly notice how similar they are. Remember that the language you are using for Generative Art doesn't matter, it's all about the logic behind your code.
Setup a basic demo
To make a basic demo work of random walkers, we will follow three simple steps:
Create a walker at a random position
Make it walk one step in a random direction (Top, Bottom, Left or Right)
Repeat step 2 until the walker moves out of the artboard
💡You can click anywhere to reset the demo
1. Create the walker
If x & y positions are given (when mouse is clicked) we use those positions, otherwise create the walker at the center of the scene.
We also call the draw() method to draw the first position of our walker.
class Walker {
constructor(x, y) {
this.x = x || floor((width / cell) / 2) * cell;
this.y = y || floor((height / cell) / 2) * cell;
this.draw();
}
}
2. Check if the walker is within the boundaries
With this one line condition we are checking if the x & y positions are either under 0 (left or top) or higher than the width/height of the scene (right or bottom).
isOut () {
return(this.x < 0 || this.x > width || this.y < 0 || this.y > height);
}
3. Move the walker
If the walker is within the scene, we make it move. First we generate a random number between [0, 1] (the direction variable).
Then based on its value we will move in one of four directions:
[0, 0.25] Go up
[0.25, 0.5] Go down
[0.5, 0.75] Go left
[0.75, 1] Go right
move () {
const direction = random();
if (direction < 0.25) {
// Go up
this.y -= cell;
} else if (direction < 0.5) {
// Go down
this.y += cell;
} else if (direction < 0.75) {
// Go left
this.x -= cell;
} else if (direction < 1) {
// Go right
this.x += cell;
}
}
💡This code could be shortened but I kept the long version to make it more readable.
4. Draw the walker
To draw shapes we first need to define their styles. We set the fill style to black with 30% of opacity and we disable strokes. Finally we draw a rectangle at the walker position.
draw () {
fill('rgba(0, 0, 0, 0.3)');
stroke(0, 0);
rect(this.x, this.y, cell, cell);
}
5. Handle all walkers each frame
Here is the code that is executed on each frame. We loop through all the walkers and check each if it is outside of the artboard.
If not, we make it move and then we draw it.
walkers.forEach(walker => {
if (!walker.isOut()) {
walker.move();
walker.draw();
}
});
🎈 If you hide the grid, reduce the cell size, and let your code run for a bit, here is the kind of result you can get.

Use a random velocity
In the demo above, the walkers are always walking at the same velocity: one cell per frame.
But what if we assign a velocity in both X & Y axis to our walkers and update that velocity on each frame?
💡Some portion of the code in the snippets below have been omitted and replaced with [...] check the source code of the demo to see the full code
1. Assign default velocity
We will make each walker start with a velocity of 0 in both axes. You could also try setting a random velocity on start for different results.
constructor (x, y) {
[...]
this.velocityX = 0;
this.velocityY = 0;
}
2. Update the velocity
On each frame, we are speeding up or down the velocity of each walker. With this code, we are getting a random value between -0.25 and +0.25. Try with smaller/higher values to generate different results.
velocity () {
this.velocityX += random(-0.25, 0.25);
this.velocityY += random(-0.25, 0.25);
}
3. Move the walker
After updating the velocity, we need to apply that velocity on the walker when we call the move function.
move () {
this.x += this.velocityX;
this.y += this.velocityY;
}
💡We are talking about velocity here instead of speed because a speed doesn't have a direction. When you say that your car can reach 150km/h of speed you don't care about the direction of your car, it's just about how fast it goes. In our case the walkers have a speed per axis so we call them velocities.
4. Draw the walker
Because we removed the grid for this demo I'm drawing circles instead of squares to get a prettier output.
draw () {
fill('rgba(0, 0, 0, 0.2)');
circle(this.x, this.y, 10, 10);
}
5. Handle all walkers each frame
In this portion we only added the update to the velocity before moving our walkers.
walkers.forEach(walker => {
if (!walker.isOut()) {
walker.velocity();
walker.move();
walker.draw();
}
});
🎈 By making all of our walkers come from the center of the demo we get a different look than random snakes coming from everywhere.

Use a noise function
For this next step we are gonna use a Simplex noise function (also known as Perlin noise). If you are not familiar with noise you should probably check out this video by Daniel Shiffman first.
By using noise we are trying to make our walkers follow some sort of flow. Imagine every cell of our previous grid with a grey value from white to black. The more black a cell is, the more that cell will increase the velocity when a walker walks in.

1. Update the velocity
We are now updating the x & y velocity based on the walker coordinates. There are three important things in this code
We are mapping the result of the noise function because the p5.js noise method returns a value between 0 and 1. In our case we want the walkers to walk backwards or forwards so we convert the value we receive from 0 to 1, for -1 to 1.
Jumping from the position [1, 4] to [2, 6] is a big step in a noise field. That's why we have to multiply the coordinates with very small numbers to reduce the leaps in the noise function. (Remember, multiplying by a value < 1 is basically a division)
Notice how we switched the x & y parameters for the velocityY. If we didn't do that, both X & Y velocity would always be the same which would make our walkers walk in diagonals.
velocity () {
this.velocityX += map(noise(this.x * 0.005, this.y * 0.005), 0, 1, -1, 1);
this.velocityY += map(noise(this.y * 0.005, this.x * 0.005), 0, 1, -1, 1);
}
2. Assign noise velocity on creation
By giving each walker random velocities, we will avoid having two walkers that start from the same point having exactly the same end result. Now, since they all have random X Y velocities when they start, their paths will always be a little different.
constructor (x, y) {
[...]
this.velocityX = random(-2, 2);
this.velocityY = random(-2, 2);
}
3. Use the elapsed time
We are tweaking our velocity method a little bit to make it even more random. Now, we have a third parameter, which is the time passed since the beginning of our program. This means that if two walkers with the same initial velocity are created on the same coordinates, their paths will be different, because the noise field has changed with time.
velocity () {
this.velocityX += map(noise(this.x * 0.005, this.y * 0.005, millis() * 0.001), 0, 1, -1, 1);
this.velocityY += map(noise(this.y * 0.005, this.x * 0.005, millis() * 0.001), 0, 1, -1, 1);
}
4. Create many walkers
Now instead of creating a single walker on page load or click, we will create 20 of them to see how their paths will each look similar but different.
const x= random(width);
const y = random(height);
for (let i = 0; i < 20; i++){
walkers.push(new Walker(x, y));
}
Drawing lines
So far, we have only drawn shapes on each frame. If you want to plot this, the result may not be as nice as having long curvy lines.
To do so, we will need to draw a piece of the line on each frame based on every walker velocity.
💡You can click anywhere in the demo to generate an new output
1. Store previous position
In order to draw a line we need two coordinates. We already have the current position of the walker with the x & y variables. But we need to store their previous position to make a line to show the distance they walked.
constructor (x, y) {
[...]
this.px = x;
this.py = y;
}
2. Draw the lines
Now instead of drawing a shape on every frame we are drawing a line that goes from the walker current position to its previous position. Once we draw the line, we can store the current position of the walker to reuse it on the next frame.
draw () {
line(this.x, this.y, this.px, this.py);
this.px = this.x;
this.py = this.y;
}
3. Adding more lines

💡 Try adding more walkers in every batch and enjoy the show!
Be creative
Once you are getting familiar with your algorithm try exploring with different styles. Add some colours, play with the weight of your lines, make your lines even more or less random!

💡 See this live here: https://codepen.io/Mamboleoo/pen/BgZLyZ
Plot your artwork
Once you are happy with the results produced by your code you can use you favorite tool to generate SVG from generative code (Canvas-sketch, Figma plugins, ...).
Remember that your lines should be one long path instead of many tiny lines to avoid your plotter going up & down repeatedly. To do so, store all the positions of your walkers in an array per walker and once the walker is reaching the limit of your scene, draw one path made out of all those coordinates.
You can now render the lines in SVG and plot them!

If you enjoyed this tutorial please share your best drawings with me either on Twitter (@Mamboleoo) or Instagram (@MamboleooLab) and be sure to use the hashtag #generativehut !
All demos are available on this page if you want to play with them.








Mình có lần lướt đọc mấy trao đổi trên mạng thì thấy nhiều người nhắc tới sc88 game khi bàn về các nền tảng giải trí trực tuyến và cách họ tổ chức kho trò chơi trên hệ thống, nên cũng tò mò tìm hiểu thử để xem cách nội dung được trình bày ra sao. Mình không đi sâu vào từng trò chơi cụ thể, mà chủ yếu quan sát cách các danh mục được phân chia như casino, slot, thể thao hay game bài và cách bảng thông tin hiển thị trên giao diện. Cảm giác là khi phần sc88 game được sắp xếp rõ ràng, các mục tách biệt dễ theo dõi và điều hướng mạch lạc…
Gần đây, mình có thấy nhiều người bàn luận về hi88 khi nói đến các nền tảng trực tuyến và cách sắp xếp nội dung của họ. Thế là mình cũng tò mò ghé vào xem thử giao diện và cách họ bố trí các mục thông tin. Mình không đi vào chi tiết từng chức năng, chỉ dành chút thời gian để ngó qua cách các phần được phân chia, thông tin hiển thị ra sao và cách di chuyển giữa các chuyên mục. Mình thấy nếu nội dung được trình bày mạch lạc, các khu vực rõ ràng và bố cục hợp lý thì việc đọc lướt sẽ không bị rối, như vậy là đủ để mình có…
Mình có lần lướt đọc mấy trao đổi trên mạng thì thấy nhiều người nhắc tới go 88 khi bàn về các nền tảng trực tuyến và cách họ tổ chức nội dung tổng thể, nên cũng tò mò mở ra xem thử để quan sát giao diện và cách bố trí trình bày. Mình không đi sâu vào từng chức năng cụ thể, mà chỉ dành ít thời gian xem cách các mục được chia, cách thông tin hiển thị và cách điều hướng giữa các chuyên mục. Cảm giác là nếu phần nội dung được trình bày rõ ràng, các khu vực tách biệt dễ theo dõi và bố cục gọn gàng thì đọc lướt cũng không bị…
Gần đây mình có lướt web và thấy nhiều người bàn luận về sun win, khiến mình cũng khá tò mò. Thế là mình mở trang lên để xem giao diện và cách mà họ sắp xếp nội dung. Mình không đi sâu vào từng tính năng mà chỉ dành chút thời gian để quan sát cách các mục được phân chia, thông tin được hiển thị và sự dễ dàng khi di chuyển giữa các chuyên mục. Nếu nội dung được trình bày một cách rõ ràng, các phần tách biệt và bố cục gọn gàng thì việc đọc qua cũng không bị rối; với mình như vậy là đủ để hiểu những điều cơ bản rồi.
Kèo nhà cái 5 là một cái tên mình thấy nhiều người nhắc đến khi thảo luận về việc theo dõi và so sánh tỷ lệ các trận đấu. Mình cũng tò mò vào xem thử cách mà họ tổ chức thông tin. Mặc dù không đi sâu vào từng số liệu hay loại kèo cụ thể, nhưng mình thấy bảng tỷ lệ được bố trí khá rõ ràng, các mục tách biệt dễ nhìn và cập nhật thông tin liên tục. Nhờ vậy, việc đọc lướt không bị rối mắt, và mình có thể nắm bắt được những thông tin cần thiết trước khi trận đấu diễn ra.