Make generative art by clustering small squares into patterns
Pixel art is an iconic style spawned from the necessity of working within low-res constraints. Much of the pixel art consumed today is hand drawn on specialized canvases (Pixilart.com). Pixel art can also be developed using a more proceedural approach. That's what this tutorial is all about.
The following examples use p5js but the concepts could easily be adapted to a vanilla JS solution based on Canvas, SVG, or even raw HTML/CSS.
Most of the boilerplate is just basic HTML and CSS. Note that you don't have to setup a canvas element in index.html. p5js is smart enough to automatically insert a <canvas> element inside the <main> tag.
sketch.js - It can be a little confusing the first time you dive into p5js. You don't need to initialize anything or even call your setup function to kick things off. Most of the heavy lifting is happening behind the scenes. In 4 lines of very readable code, we are off and running!
With a centered 300x300 black canvas, we're ready to add our first grid.
Step 2 and we already have a grid on the canvas! Let's walk through the code.
Lines 1-8 Sets constants. I believe it's good to be a little verbose in naming. It should be obvious what each variable is used for.
Line 13: noStroke(); This prevents p5js from adding a border to the cells (rectangles) that make up the grid.
Line 15: fill(CELL_COLOR); After setting a fill, every subsequent shape you add to the canvas will automatically take that color. This means we can set the color once for 400 grid cells.
Lines 16-17 Adds 20 columns and 20 rows to the grid. Note that the row loop should be inside the column loop to make sure we add 20 row cells for every column cell.
Lines 18-19 Sets the number of pixels from the left/top edge of the canvas to the left/top edge of the grid cell. Note each cell is offset by the padding. The top left cell will be 50x50 from the top left corner of the canvas.
Line 20 Sets the width and height of each grid cell. By making this 2 less than the CELL_SIZE, we get a 2 pixel gap between cells. For no gap, omit this line and use CELL_SIZE instead of size on the next line.
We're off to a great start. But a grid composed of cells that are all the same color isn't very visually interesting. We need a better way to define colors or attributes of individual cells.
Not much has changed visually, but we've reworked how cells are rendered.
Line 3 The array of colors will be referenced in the renderCells() function to determine a cell's color.
Lines 5-6 We create a one dimensional array of 400 elements to define the attributes for each cell. The first 20 elements are the first row, the next 20 are the second row, and so on. Because we only care about the color, we use a simple number rather than an object which could be used for more complicated grids. Every cell is initially set to 0 (dark grey). Then the first cell is set to 1 (yellow).
Line 16 This defines the number of pixels from the left of the canvas aligning with the right edge of the grid. While rendering, once a row hits this edge, we start a new row.
Line 17 Since we're working with a one dimensional array for cells, we only need a single loop.
Line 18 We set the color of the cell by referencing the COLORS array with the value of the cell.
Lines 22-26 Once we render a cell, we need to shift the left value 1 cell width to the right. If we hit the leftEnd limit, we reset the left and move the top 1 cell height down. Thinking about this as a typewriter can help in understanding the logic.
One disadvantage of working with a one dimensional array for managing cells is figuring out the position of a cell. Cell #20 is in the top right corner, but cell #21 is near the top left corner. That can get really confusing. What we really need is an X, Y coordinates system to make things more manageable.
Now we're getting somewhere. We can point to a cell in the grid using X, Y coordinates and define its color. Making generative art is only a few steps away!
Lines 7-9 We create a loop from 0-19 and set all the cells with X and Y equal to that number to 1 (yellow).
Lines 13-15 Finding the index based on the X & Y might seem tricky, but it isn't too crazy. Imagine we have X=3 and Y=1. The cell is in the 2nd row (Y=0 is the first row). We know the index has to be between 20-39. So we start at 20 (1*20) and just add the X value...so the index is 23. Still confusing? Think about a smaller grid. Here is a 4x4 grid with indexes as an example.
Now that we can find a cell based on an X Y coordinate, the next step is to find neighboring cells. This can be really useful for a variety of reasons. But for this tutorial, we're going to start with a single yellow cell and make its neighboring cells also yellow.
This code is a bit verbose but it demonstrates how you can find cells relative to other cells. You can always clean things up by creating a separate file for utility functions.
Lines 7-11 We start by changing the cell at X=5, Y=5 to 1 (yellow). Then we get an array of all the neighboring cells and set their values to 1.
Lines 16-34 These are the 4 utility functions to find indexes of adjacent cells.
Lines 36-43 We also have a helper function that calls all of the adjacent cell functions and puts them into an array. The filter removes neighbors that have a value of -1 "off-grid".
Art is tricky and highly subjective so I'm not going to get too crazy here. I just want to show how you could use some of the techniques above to make something a little more visually interesting.
I'm not sure I'd call this "art" because I wouldn't personally mint it as an NFT. This is really just a starting point. Generative art requires many cycles of experimentation and iteration. Take this code and try something crazy. Maybe you'll find a new style.
If you have questions please join our Generative Art community on Twitter. You can also check out some of the art I've made usign similar techniques on fx(hash). If you want to get notified when I release the next tutorial, subscribe to the Rasterly Newsletter.