[Introduction to Artistic Creation] Rainy City — The Use of Basic Shapes and Randomness

Generative Inscription
12 min readMar 4, 2024

--

< Rainy City >

In this unit, we’ll start by learning about canvas coordinates, geometric shapes, and loops in p5, trying to compose a piece of art on the canvas using different shapes. Then, we will introduce the concept of randomness in generative art, guiding everyone to practice its application in color, shape, and composition. Designing unique random rules for our work can bring rich variations to our creations!

Then do it — Rainy City

Foreground — oblique raindrop; Medium — small raindrops; Vision — Drizzle

Rainy City — This is an exercise in generative landscape. The abstract and simple geometry constructs three different rainy days in the city, delineating the impression of the city sometimes with slanting wind and drizzle, sometimes with heavy rain.

0. Start the first project from scratch

The basic program architecture of the p5 project consists of setup() and draw(). Let’s find out what they do and when to use them.

setup() : The initialization of the web environment, which is performed every time the web page is reorganized. It is suitable for drawing complex works that end up as fixed pictures. It is commonly used to create generative art, machine plotting, printing and data visualization.

draw() : After executing setup(), it will be repeatedly executed, and the content of the drawing can be updated continuously. It is suitable for works whose state needs to be constantly updated. It is often used to make small games, interactive brushes, animations, dynamic web pages, etc. It can combine mouse, keyboard, camera or music to design different interactive mechanisms.

This exercise will be done in the setup() function at a time, and each time we rearrange it, we will redraw a random city.

1. Draw the basic shape

We can draw different geometries using the p5 function. Start by drawing a circle, cirlce(x,y,d) with (x,y) as the center of the circle, and draw a circle of radius d.


function setup() {
createCanvas(600, 600); // Building the canvas 600x600 px
background(245); // Fill in the background with light gray

circle(400, 300, 200); // Draw a circle of radius 200 at (400,300)
}

function draw() {}

We can use other shape functions besides circles. After practicing basic shapes, we can also use beginShape(), vertex(), endShape() to draw special shapes.

Basic shape: Basic geometric shape, such as circle(), ellipse(), rectangle rect(), arc/sector arc(), triangle().

Special shape: Specify multiple points to draw irregular shapes. Use vertex() to draw straight lines and curveVertex() to draw smooth curves.

Remember that the canvas of p5 uses the upper left corner as the origin of the coordinates (0, 0), increasing the x coordinate to the right and increasing the y coordinate to the bottom. Now change the coordinates to move the position of the circle, or try filling () and stroke() to color the circle and draw a border.

2. Use random() to draw random shapes

Next, we will draw a bunch of random circles on the screen as raindrops. The random() function will be used to generate random values to specify the position and radius of the circle. Let’s preview several uses of the random() function:

random() : returns a decimal between 0 and 1.

random(10) : Returns a decimal greater than 0 and less than 10.

random(5, 10) : Returns a decimal greater than 5 and less than 10.

random([1, 5, 10]) : returns an element randomly from the array [1, 5, 10].

Now let’s draw circles with random positions and radii. Each time you rearrange the program, you draw a different circle.

function setup() {
createCanvas(600, 600);
background(245);

let x = random(width); // The range between 0 and width is random x coordinates
let y = random(height); //Random y coordinates in the range between 0 and height
let r = random(50, 100); // Radius of random
circle(x, y, r); // Draw a circle of radius 200 at (400,300)
}

function draw() {}

3. Use the for loop to generate a large number of shapes

When drawing a large number of shapes, instead of copying and pasting manually, we can use the for loop to generate a large number of shapes at once.

function setup() {
createCanvas(600, 600);
background(245);

for (let i = 0; i < 100; i++) { // Repeat 100 times
let x = random(width);
let y = random(height);
let r = random(50, 100);
circle(x, y, r);
}
}

function draw() {}

4. Randomly select the color in the array

Come and color the shapes! We can start by selecting a set of colors that we like. In addition to using the google Color Picker to select colors one by one, we can also use a website like Color Hunt to refer to the color tickets that have already been matched.

Once you have selected a set of colors, you can store them in an array and use random() to randomly pick a color from the array. An array is a data structure that stores multiple pieces of data in sequence. Declaring an array requires encasing all elements with brackets and separating each element with a comma. As an example, we can use colorArray[3] to obtain dark blue color tickets.

Random circles with background color

Fill the background and circle with random colors. We first declare two arrays, colorArray and bgcArray, to store the shape filling color ticket and the background color ticket respectively, and then use random() to randomly select the color from the array.

let colorArray = ["#E63946", "#F1FAEE", "#A8DADC", "#457B9D", "#1D3557", "#FCA311"]; //My color ticket 
let bgcArray = ["#1f1f26", "#f5f5f5"]; // Background color ticket

function setup() {
createCanvas(600, 600);
background(random(bgcArray)); // The background is randomly colored

for (let i = 0; i < 100; i++) {
let x = random(width);
let y = random(height);
let r = random(50, 100);

let clr = random(colorArray); // Pick a color at random from my color ticket
fill(clr); // I'll fill in the color
noStroke(); // Cancel the outer frame line

circle(x, y, r);
}
}

function draw() {}

5. Use the first, middle and last three different rain scenes to enrich the picture

試著調整程式碼中的圓形大小和數量,看看不同的視覺效果

Then IN THE previous STEP, WE modify the parameters to draw the medium scene — small raindrops. In the creation process, we try to try all the numbers that can be modified to see the different effects, can gradually find out the best parameter range for their own ideal picture oh!

Combine the front, middle and back three different rain scenes

Now let’s combine different geometrically rich images. First, slightly adjust the parameters of the circle to draw the small raindrop in the middle scene, then repeat the small raindrop method, and use the line function line(x,y,x,y+w) to draw the small raindrop in the foreground from (x,y) down a small section. Once again, use the line function line(x,y,x+w,y+w) to draw the oblique raindrop in the close view to the right and down.

let colorArray = ["#E63946", "#F1FAEE", "#A8DADC", "#457B9D", "#1D3557", "#FCA311"];
let bgcArray = ["#1f1f26", "#f5f5f5"];

function setup() {
createCanvas(600, 600);
background(random(bgcArray));


// #1 Middle view - Small raindrops(circle)
for (let i = 0; i < 2000; i++) { // Draw multiple large raindrops (round)

let x = random(width);
let y = random(height);
let r = random(2, 6);

fill(random(colorArray)); // Random color filling
noStroke();

circle(x, y, r);
}


// #2 Vision - Drizzle(line)
for (let i = 0; i < 10000; i++) { // Draw the thin line 10,000 times
let x = random(width); // Random x coordinates
let y = random(height); // Random y coordinates

noFill(); // No color filling
stroke(random(colorArray)); // Random wireframe color
strokeWeight(0.5); // Set the line thickness to 0.5
line(x, y, x, y + 3); // Draw the line down 3 units
}


// #3 Foreground - slanting raindrop(line)
let rainLength = random([50, 200, 600]) // Length of three oblique raindrops
for (let i = 0; i < 100; i++) { // Draw 100 slanting raindrops
let x = random() * width * 2 - width / 2; // Random x coordinates, fill the left and right sides
let y = random(-300, 300); // Random y coordinates, keep the raindrop in the top half
let r = random(10, rainLength); // Random oblique raindrop length

noFill(); // No color filling
stroke(random(colorArray)); // Random wireframe color
strokeWeight(random(0.5, 1)); // Random wire frame thickness
line(x, y, x + r, y + r); // Draw a slash
}

}

function draw() {}

6. Use round() to give rhythm to the picture

What if it sometimes feels like complete randomness makes the picture too chaotic and noisy? Here is a little trick to share with you. We can use the combination of random() and floor() to easily make the random effect on the grid or grid, to give the random a little order, so that the composition has a little more rhythm.

random() returns consecutive decimal numbers between 0 and 1. To make the number one order, we can use floor() to take the whole function (round). floor(random()*5)/5 produces the number that splits the range 0 to 1 into n orders according to the above procedure. You can arrange random elements in a regular way to create an effect similar to drawing on a grid.

Use the rounding function to make a simple effect similar to drawing a house on a grid
let colorArray = ["#E63946", "#F1FAEE", "#A8DADC", "#457B9D", "#1D3557", "#FCA311"];
let bgcArray = ["#1f1f26", "#f5f5f5"];

function setup() {
createCanvas(600, 600);
background(random(bgcArray));


// #4 The house(rectangle)
let skyLine = 200; // Height of the skyline
for (let i = 0; i < 20; i++) { // Draw the rectangle 20 times
let span = 10; // Divide the picture into ten parts

let x = (floor(random() * span) / (span - 1)) * width // The rounded x coordinates
let y = (floor(random() * span) / (span - 1)) * (height - skyLine) + skyLine; // The rounded y coordinate plus the height of the skyline

let w = width / span; // Width of the house
let h = height; // Height of the house

fill(random(colorArray)); // Fill in the random color
noStroke(); // Cancel box line
rect(x, y, w, h); // Draw a rectangle (house)
}


}

function draw() {}

7. Finally adjust the drawing order

Rainy City ; Drawing order: background — building — long view drizzle — middle view drizzle — foreground slant raindrop

Remember that the logic of drawing in p5 is the same as in the real world: the last drawing will overwrite the first drawing, so we can adjust the order of different elements in the screen by changing the order of the program.

// Project: Week#1 Geometry and Randomization-full version (with notes)
// Author: Yun-Chen Lee, yclee@arch.nycu.edu.tw
// Description:
// 1. Know the canvas coordinates
// 2. Draw the base shape
// 3. Draw the fill and box lines
// 4. Use random functions
// 5. Draw the graph repeatedly using a loop
// 6. Use array to store color tickets
// 7. Use the rounding function to create order in the random
// =========================================================================================
// ctrl + "/" : Annotate/unannotate
// ctrl + "s" : Save the file
// Right-click index.html -&gt; Open With Live Server: Use the browser to display the canvas
// Right-click the browser -&gt; Check -&gt; console: View error messages


let colorArray = ["#E63946", "#F1FAEE", "#A8DADC", "#457B9D", "#1D3557", "#FCA311"]; // My color ticket
let bgcArray = ["#1f1f26", "#f5f5f5"]; // Background color ticket

function setup() {

createCanvas(600, 600); // Building the canvas 600x600 px
background(random(bgcArray)); // Fill in the background

// #4 The house(rectangle)
let skyLine = random([0, 200, 400]); // Three types of skyline heights
for (let i = 0; i < 20; i++) { // Draw the rectangle 20 times
let span = 10; // Divide the picture into ten parts
let x = (floor(random() * span) / (span - 1)) * width // The x coordinates are randomly rounded
let y = (floor(random() * span) / (span - 1)) * (height - skyLine) + skyLine; // y coordinates + plus skyline height after random rounding
// let x = random() * width // Random x coordinates
// let y = random() * (height - skyLine) + skyLine; // Random y coordinates plus the height of the skyline
let w = width / span;
let h = height;

fill(random(colorArray)); // Fill in the random color
noStroke(); // Cancel box line
rect(x, y, w, h); // Draw a rectangle (house)
}

// #2 Drizzle of rain(line)
for (let i = 0; i < 10000; i++) { //Draw the thin line 10,000 times
let span = 100; // Divide the picture into 100 parts
let x = (floor(random() * span) / (span - 1)) * width; // The x coordinates are randomly rounded
let y = (floor(random() * span) / (span - 1)) * height; // The y coordinates are randomly rounded
// let x = random(width); // Random x coordinates
// let y = random(height); // Random y coordinates

noFill(); // No color filling
stroke(random(colorArray)); // Random wireframe color
strokeWeight(0.5); // Set the line thickness to 0.5
line(x, y, x, y + 3); // Draw the line down 3 units
}

// #1 Small raindrop(circle)
let count = random([200, 800, 2000]) // The number of three kinds of raindrops
for (let i = 0; i < count; i++) { // Draw multiple large raindrops (round)
let span = 50; // Divide the picture into fifty parts

let x = (floor(random() * span) / (span - 1)) * width; // The x coordinates are randomly rounded
let y = (floor(random() * span) / (span - 1)) * height; // The y coordinates are randomly rounded
// let x = random(width); // Random x coordinates
// let y = random(height); // Random y coordinates
let r = random(2, 6); // The radius of the random circle

fill(random(colorArray)); // Fill in the random color
noStroke(); // Cancel box line
circle(x, y, r); // Draw a circle
}


// #3 Slanting raindrop(line)
let rainLength = random([50, 200, 600]) // Length of three oblique raindrops
for (let i = 0; i < 100; i++) { // Draw 100 slanting raindrops
let x = random() * width * 2 - width / 2; // Random x coordinates, fill the left and right sides
let y = random(-300, 300); // Random y coordinates, keep the raindrop in the top half
let r = random(10, rainLength); // Random oblique raindrop length
noFill(); // No color filling
stroke(random(colorArray)); // Wire frame color
strokeWeight(random(0.5, 1)); // Wire frame thickness
line(x, y, x + r, y + r); // Draw a slash
}

}


function draw() {}

8. Small summary

This creation starts from the imagination of a rainy city and slowly stacks, which combines many basic concepts and techniques of generative art. Let’s review this exercise:

Drawing of basic geometry
The use of random functions
Use loops to repeat tasks
Use arrays to store color ticket combinations
Change the parameters to explore the visual effects
Combine random functions and rounding functions to create the effect of drawing on the grid
Change the drawing order

Using the random() function to control random values, we can create different combinations to design situations, so that each generation is full of features and surprises!

Creative Challenge — an impression of your city

Cases of generated art works related to urban themes

What is your most distinctive impression of a city? Use your imagination to present a city or landscape.

Think about it…
Who are the main characters in the city or landscape? Buildings, trees, people, skylines, vehicles…
What is the viewing Angle? Look up, look up, look down…
What atmosphere does color create? Passionate, uncanny, cold, vibrant…

Little creative steps
Each number can be tried and tweaked, sometimes with unexpected results
Try changing shapes, colors, quantities, sizes, etc., or use shapes that haven’t been used before
Think about the layout and element arrangement of the picture

Reference Materials

The p5 API mentioned in the article
- p5 Basic program architecture:setup()draw()
- p5 Basic geometry:circle()line()ellipse()rect()arc()triangle()
- p5 Draw special shapes:beginShape()endShape()vertex()curveVertex()
- p5 Fill in color:fill()stroke()noFill()noStroke()strokeWeight()background()
- p5 random:random()
- p5 Mathematics:floor()

Basic programming concepts
- Loop and condition judgment:forif-else
- Structure of data:array

Color ticket tool
- google Color picker
- Color Hunt

Examples of works mentioned in the article
-
ttozatto < Ghocities >
- Yazid < Hashed Cities >
- lunarean < City in the Wind >
- RJM_Studios < Cityscapes >

The course materials are for teaching purposes only, not for other commercial purposes.
This course is sponsored by GDAP. For more information, please refer to GDAP Easternad https://www.easternad.xyz/

The course content shall not be reproduced, adapted or edited without the authorization of the author.
Copyright © 2024 Yun-Chen Lee 李芸蓁. All rights reserved.

--

--