On Grids
Sat Jul 05 2025
Grids form a backbone for so many generative art projects, from providing a structural canvas onto which to create, to providing convenient methods by which to group disparate objects. Learning how to create, use, and express ideas with grids is essential to learning to be creative with code.
Grids as a structural canvas
At their core, a grid is a framework of organized cells (usually square, but things can get exotic - more on that later) that divide space into smaller, uniform (and sometimes non-uniform, again, more on that later) units. Depending on what and how you're coding, these individual units - or cells - can provide various uses in generative art (and beyond).
- Individual units
As an individual unit, a grid cell can provide a discrete area onto which you can draw, providing a method by which to create highly complex appearing patterns with a relatively low cost. Some examples here might include Truchet tiles, Wang tiles or 10 PRINT. - Performance
Grids are very frequently used in programming to provide convenient ways to break down space for the purpose of optimizing in-simulation elements using spatial indexation and fast lookups. Think of how expensive collision-detection is in video games. By breaking the game field down into grids, you can perform collision detection against limited sub-sets of components, only calculating against elements in the neighbourhood. See: Quadtrees, Octrees. - State machines
When you use a grid as a state machine, each cell becomes an individual finite state machine. The state of a cell could be anything relevant to your generative art project: c colour, a numerical value, a boolean, or even a complex object.
The core idea is that the state of a cell at any given iteration is determined by its own previous state and the states of its neighbours. This idea is the very basis of an entire branch of generative systems called cellular automata. Some further reading: Elementary cellular automaton, Conway's Game of Life, Reaction Diffusion. - Predictable determinism
When used within a system that makes use of a hashing function, grids provide a very convenient way of addressing cells in a predictable way. This means that a grid cell, passed to a hashing function, can produce the same random-seeming number every time. This sort of system provides a very convenient way to know the state of a cell without needing a lookup object. This concept forms the very core of a many shader programs.
Alright, that's a lot, and I really hope to get into practical demonstrations of a lot of them, but for right now let's get into a program. Using our previously created Turtleman class (I'll want to plot this later on), we can now program up. basic grid and then iterate on that, making it more complex and interesting!
A basic grid
If you just want to jump straight into the code, please feel free just to jump straight on over to the Codepen example.
TA-DAA!
Alright, I know this doesn't look all that impressive, but bear with me, I promise we'll get to some more interesting stuff. The purpose of this program is to demonstrate some of the fundamental concepts, ideas and functions that go into creating a grid using a program like Turtleman. The methods and approaches will change somewhat, depending on the technology, but the core reasons for programming a grid remain largely the same. Onward!
The grid itself
Alright, the core of the grid is the structure you use to describe your grid. This can be accomplished in many different ways, the use of which should be determined by what you want to achieve, but for our purposes I'm going to chose a straightforward structure that hopefully keeps things clear and simple.
Let's start with our primary configuration variables, these will provide all of the information we need to create a grid,
const width = 1000,
height = 800,
// number of grid columns
gridW = 5,
// Number of grid rows
gridH = 4,
// Width of a cell in pixels
gridWFactor = width / gridW,
// Height of a cell in pixels
gridHFactor = height / gridH;
We're going to create our grid in a 1-dimensional array. You could conceivably do yours in a 2 dimensional array, which can be easier to think of as you have an array representing rows, and then each of those arrays contains an array representing the columns and cells. I prefer 1-dimensional arrays because they're more resilient and, once you get your head around the maths to convert index to rows and columns, and vice-versa, they're very straightforward.
Ok, so to start with, to convert from array index to rows and columns looks like this:
getGridPosition = (index, columns) => {
return {
column: index % columns,
row: Math.floor(index / columns)
};
}
This function takes an index (representing the index in the 1-dimensional array) and an integer (representing the number of columns in the grid) and returns the cells position in the grid. Column is calculated as the modulo of the index to the columns (so if you have 8 columns, and the provided index is 12, the column will be 4). Row is calculated as the floor of the index divided by the number of columns.
Similarly we want a function to turn a grid position into an array index. This can be achieved using something like this:
getGridPosition = (x, y, columns) => {
return y*columns+x;
}
This is just the reverse of the previous function. Please take some time to try to understand these functions, if you need to. They're very powerful and used frequently.
Alright, now we can finally create the grid structure.
const gridCells = [];
for(let i=0;i<gridW*gridH;i++) {
const row = Math.floor(i / gridW);
const col = i % gridW;
const cell = {};
cell.points = [
{ x: col, y: row },
{ x: col + 1, y: row },
{ x: col + 1, y: row + 1 },
{ x: col, y: row + 1 },
];
cell.bounds = {
x: col * gridWFactor,
y: row * gridHFactor,
w: gridWFactor,
h: gridHFactor
}
gridCells.push(cell);
}
So you can see, we create a 1-dimensional array for storing the grid cells, loop through the whole number of cells we have and construct a cell object with:
- Points
The 4 points that make up the cell, clockwise from the top-left. - Bounds
The bounds of the cell, in pixels.
Aside, some drawing functions
Before we get into some more grids, I wanted to take a moment to look at some useful drawing functions. These use the Turtleman API and provide a convenient way to draw some interesting shapes.
const drawCircle = (r, x, y, steps) => {
toy.seth(0);
const TAU = Math.PI * 2;
const step = (TAU * r) / steps;
toy.jump(x-step/2, y-r);
for(let i=0;i<steps;i++) {
toy.forward(step);
toy.right(TAU/steps);
}
}
The circle draws a circle by moving into the position (x,y) at the top of the circle and then drawing forward and turning right by an amount equal to: ( 2π * radius (r)) / number of steps.
const drawSquare = (r, x, y) => {
toy.seth(0);
toy.jump(x - r, y - r);
toy.forward(r * 2);
toy.right(1.5708);
toy.forward(r * 2);
toy.right(1.5708);
toy.forward(r * 2);
toy.right(1.5708);
toy.forward(r * 2);
};
The square moves to the top-left of the square shape as defined by the x, y and radius (width) of the square. Then it walks forward, and turns right (1.57 = 90 degrees) repeating until the square is complete.
const drawTriangle = (r, x, y) => {
toy.seth(0);
toy.jump(x, y - r);
toy.goto(x + r, y + r);
toy.goto(x - r, y + r);
toy.goto(x, y - r);
};
Draws a triangle by first jumping to its top vertex, defined by the x and y coordinates of its centre and its radius. It then uses goto commands to draw lines connecting the top vertex to the bottom-right vertex, then to the bottom-left vertex, and finally back to the starting point.
const drawDiamond = (r, x, y) => {
toy.jump(x, y - r);
toy.right(1.5708 / 2);
toy.goto(x + r, y);
toy.goto(x, y + r);
toy.goto(x - r, y);
toy.goto(x, y - r);
};
The diamond shape is drawn by first jumping to the top-most point of the diamond, as defined by its centre coordinates and r radius. Then, it draws lines connecting the top point to the right point, then to the top point etc.
const drawFlower = (r, x, y, steps) => {
toy.seth(0);
const TAU = Math.PI * 2;
const step = TAU / steps;
for (let i = 0; i <= steps; i++) {
const _r = r + Math.sin(i * step * 10) * 30;
const _x = Math.cos(i * step) * _r + x;
const _y = Math.sin(i * step) * _r + y;
if (i === 0) toy.jump(_x, _y);
else toy.goto(_x, _y);
}
};
The drawFlower function draws a flower-like shape by iterating through a series of steps around a central point, defined by x and y. In each step, it calculates a slightly varied radius using a sine wave, which creates the "petal" effect. It then determines the _x and _y coordinates for that step based on the varied radius and the current angle.
Finally, let's draw the grid!
Alright, the first thing we want to do is just draw the cells as squares so we can see our basic grid!
gridCells.forEach((grid) => {
const { bounds, points } = grid;
// Draw a squre representing each grid item
toy.jump(
bounds.x,
bounds.y
);
toy.goto(
bounds.x + bounds.w,
bounds.y
);
toy.goto(
bounds.x + bounds.w,
bounds.y + bounds.h
);
toy.goto(
bounds.x,
bounds.y + bounds.h
);
toy.goto(
bounds.x,
bounds.y
);
});
And all things being equal, you should see your beautiful grid drawn on screen!
Round up and next steps
Alright, I know that was a lot of reading for such a basic looking thing, but I promise it'll pay off - there are a lot of places you can go from here. Please take a look at the Codepen to see some additional bits, and try some other drawing functions in those cells yourself.
Extra: I'm creating the meta images for these articles using the Turtleman class, you can see this one over on Codepen.