When trying to learn how to create art, it is helpful to study and copy works of the masters. Painters call this exercise the master study. This exercise is also helpful in generative art!
This tutorial is a master study of one of Vera Molnar’s Structure de Quadrilatères using p5.js:
A couple things to keep in mind:
- It is important to remember that a master study is a learning exercise, not something to be passed off as your own art. Give credit where credit is due. This is Vera Molnar’s work.
- This isn’t a pixel-perfect recreation, but rather a generative technique that will create a similar work.
- This tutorial isn’t perfect. There are other (probably better) ways to structure this code. Use it as a learning tool.
- Write the code yourself and play around with the parameters to get a sense of how it works.
- This tutorial assumes you already know how to set up a p5 canvas and the basics of the setup and draw functions. If not, check out the p5js.org Get Started tutorial first.
This is a great piece to learn some of the basics of generative art with. It uses nested iteration, randomization, an 8×8 grid, and a nice color scheme.
Let’s break it down into some manageable chunks:
- Drawing a randomized quadrilateral
- Drawing randomized quadrilaterals in a grid
- Drawing multiple quadrilaterals at each spot in the grid
- Adding color
Drawing a randomized quadrilateral
I like to start with making sure I know how to make each of the basic elements in the sketch before trying to throw them all together. In this piece, one of the basic elements is in the name: Quadrilaterals, the shape with four edges and four vertices.
Taking a look at the Shapes section of the p5 reference, we see that they have a
quad()primitive. What luck! Another way would be to draw four separate connecting lines, or drawing a square and skewing it in various ways.
So let’s draw a basic quadrilateral to get a sense of how they work.
Great! Now let’s add in some randomization. We’re going to use the
noLoop() function so that we only get one quadrilateral each time we hit Play. If it looks like your sketch is glitching, you probably forgot the noLoop.
To add in some randomization, we are going to use the
random() function. While doing this, keep in mind the order of arguments for the quad function: Clockwise from upper left. That will help us set the bounds for what we want
random() to return. For example, for the first (upper left) vertex, we probably want a point somewhere between (0,0) and (50,50). So that would mean the first two arguments of the quad function need to be
random(50), random(50), meaning: “pick a number between 0 and 50 for x1 and a number between 0 and 50 for y1.”
Now that we’ve added in the random() function for each vertex, each time you hit play you should see a different quadrilateral.
Great! Play around with this a bit to grok how it works. Next step: Draw a bunch of these in a grid.
Drawing randomized quadrilaterals in a grid
Iteration might seem like a bit of a jump if you’ve never encountered it before. If not, that is okay–go check out Dan Shiffman’s videos on loops and then come back.
Here I’m setting up a grid, which is an embedded iteration (nested for loops), and then at each spot in that grid I’m drawing a quadrilateral.
You’ll notice a few other things in this code:
- Some variables
- push() and pop()
- push() starts a new drawing state, then pop() resets it. Critical here because we are using translate() to move the starting point for each shape. If we did this without push() and pop(), the translate would be additive, so it would not look like a grid. Comment out push() and pop() to see how it would look.
- translate() moves the starting point for everything that comes after the translate() function. Here we use it to move each shape to its correct location within the grid by using the x and y variables provided by the for() loop multiplied by the size variable, and offset by the spacing.
- If you are having trouble understanding it, play around with translate() on a fresh sketch to get a sense of how it works.
- Different values for random inside the quad function than we used earlier
- This is because we are offsetting in absolute numbers, not relative percentages. 20 pixels on a shape that is 300 pixels wide is a smaller relative amount than 20 pixels on a shape that is 40 pixels wide.
- ❇️ Bonus challenge: figure out how to make the offsets relative!
Great! We are getting somewhere. Now let's do another loop so that we get multiple quadrilaterals in each spot on the grid.
Drawing multiple quadrilaterals at each spot in the grid
Now we need to go back and look at Vera's original piece and make a choice: Do we draw all of the quadrilaterals in each spot before moving on to the next, or do we draw all of the bottom layer, then all of the second layer, etc? The answer to this determines where we put the loop: Inside the grid loop where the quad() function is, or outside the grid loop to draw multiple grids.
Looking at how the lines on Vera's original piece overlap, I think we should do the latter: Drawing multiple grids. This doesn't matter so much when the lines are all black, but it will make a big difference when they are in color.
How many? Let's start with 12 and see how it looks.
Looking pretty good! Go ahead and play around with some of the variables and the inputs to the random() functions to see how it affects the output.
Okay, getting close! Now we need to add some color.
For a single grid that would be easy: Just set up an array of colors and pass that array to random() inside the stroke() function right before the quad() inside the loop. It isn't that simple when we are drawing multiple grids with a loop because that will lead to different colors within a pile of quadrilaterals. That might look cool, but it isn't what Vera's piece looks like.
So what do we do? There are multiple ways to solve this problem, but my preferred way to making an array of color options and using a loop to create an array of color assignments the same size as the grid.
I sampled Vera's original piece and created an array of the HSB version of colors she used with 0.9 opacity (trying to mimic the pen color bleed-through overlap).
Almost there! Here is one of our versions compared with Vera's:
Looks like we are getting very close. I think the final steps here are to:
- Add more quadrilaterals to each stack
- Adjust the stroke weight
- Play with the stroke color opacity
- Dial in the variability of the quadrilateral vertices
I'll let you take it across the finish line. You've got this! 💪