home/ modules/ grid-tiling-and-recursive-pattern

Grids, tiling, recursion, and fractal pattern

  • learner can generate parametric tiling patterns from nested loops and wallpaper symmetry groups
  • learner can use recursion and recursive grid subdivision to build self-similar and fractal layouts
  • learner can implement data-driven treemap layouts decoupled from their rendering

Build a parametric pattern system that renders the same data three ways — a symmetry-tiled wallpaper, a recursive fractal subdivision, and a squarified treemap with data-driven styling — from one reusable layout core.

This module builds the pattern engine behind most live visual sets: a layout core that turns one data stream into radically different textures on demand. In a VJ or algorave rig, the audio analysis or a MIDI controller feeds the same numbers every frame — what changes mid-set is the layout interpreting them. Being able to swap a wallpaper tiling for a fractal subdivision or a treemap without rewriting the renderer is the difference between three sketches and one instrument.

The arc starts supported: reproduce a seeded nested-loop grid, leaning on “Nested loops over a grid of tiles are the foundation of parametric tiling patterns” for the loop-plus-randomSeed skeleton. Then constrain it with symmetry — the fact that exactly 17 wallpaper groups tile the plane turns “make it repeat” into a concrete, guaranteed-to-tessellate recipe. Next the grid goes vertical: “Recursive grid subdivision generates fractal-like layouts” is the JIT how-to for splitting cells into sub-grids, while the recursion and self-similarity concepts supply the base-case and exponential-growth guards that keep depth sliders from crashing the sketch live. Finally the treemap leg forces the architectural payoff: the squarified algorithm computes coordinates, a styling callback maps data to colour, and the decoupled-layout principle is what makes one core drive all three renderers.

Required atoms are exactly what the capstone cannot survive without: the tiling procedure, the symmetry constraint, the recursion mechanics with their guards, and the treemap algorithm-styling-decoupling trio. Supporting atoms enrich the road there — 10-PRINT as the minimal-rule inspiration, branching fractals as a sibling recursion form, and classes, custom functions, and 2D arrays as refreshers for structuring the core cleanly.

Walkthrough

Nested loops make a grid; a rule per cell makes a pattern; recursion makes it fractal. Paste each sketch into the p5 web editor and press Run. Each is a complete, standalone sketch.

1 — a nested-loop grid. Two for loops walk a grid of cells — the foundation of every tiling pattern ([[grid-tiling-parametric-pattern]]).

function setup() { createCanvas(400, 400); noStroke(); }
function draw() {
  background(15);
  let n = 8, s = width / n;
  for (let i = 0; i < n; i++)
    for (let j = 0; j < n; j++) {
      fill((i + j) % 2 ? color(80, 180, 255) : color(20, 40, 70));
      rect(i * s, j * s, s, s);
    }
}

2 — a rule per cell (the 10-PRINT move). Put a random choice in each cell — here a diagonal one way or the other. One tiny rule over a grid yields an endless maze ([[wallpaper-group-tiling]]).

function setup() { createCanvas(400, 400); randomSeed(3); }
function draw() {
  background(15); stroke(120, 255, 180); strokeWeight(3);
  let n = 16, s = width / n;
  for (let i = 0; i < n; i++)
    for (let j = 0; j < n; j++) {
      let x = i * s, y = j * s;
      if (random() < 0.5) line(x, y, x + s, y + s);
      else line(x + s, y, x, y + s);
    }
}

3 — symmetry makes it tile. Draw a motif once, then mirror it across the canvas — guaranteed tessellation, the essence of a wallpaper group. Here a quarter is drawn and reflected into four.

function setup() { createCanvas(400, 400); noStroke(); }
function draw() {
  background(12);
  drawQuad();
  push(); translate(width, 0); scale(-1, 1); drawQuad(); pop();
  push(); translate(0, height); scale(1, -1); drawQuad(); pop();
  push(); translate(width, height); scale(-1, -1); drawQuad(); pop();
}
function drawQuad() {
  fill(255, 140, 60);
  for (let i = 0; i < 4; i++) { ellipse(30 + i * 30, 30 + i * 25, 40 - i * 6); }
}

4 — recursion: a function that calls itself. A recursive square subdivides into four smaller squares until a base case stops it. That base case is the guard that keeps a live depth-slider from crashing the sketch ([[processing-recursion]], [[recursive-grid-subdivision]]).

function setup() { createCanvas(400, 400); noFill(); stroke(200, 220, 255); }
function draw() { background(15); subdivide(0, 0, width, 0); }
function subdivide(x, y, s, depth) {
  rect(x, y, s, s);
  if (depth >= 4 || s < 20) return;   // base case — the guard
  let h = s / 2;
  subdivide(x, y, h, depth + 1);
  subdivide(x + h, y, h, depth + 1);
  subdivide(x, y + h, h, depth + 1);
  subdivide(x + h, y + h, h, depth + 1);
}

5 — fractal subdivision with a rule. Only subdivide some cells (a random test), and self-similarity emerges — dense here, sparse there, the same structure at every scale ([[fractal-self-similarity]]).

function setup() { createCanvas(400, 400); noStroke(); colorMode(HSB, 360, 100, 100); randomSeed(9); }
function draw() { background(0, 0, 5); split(0, 0, width, 0); }
function split(x, y, s, depth) {
  if (depth >= 5 || (depth > 1 && random() < 0.4)) {
    fill((depth * 55) % 360, 70, 90);
    rect(x + 2, y + 2, s - 4, s - 4);
    return;
  }
  let h = s / 2;
  split(x, y, h, depth + 1); split(x + h, y, h, depth + 1);
  split(x, y + h, h, depth + 1); split(x + h, y + h, h, depth + 1);
}

6 — one core, data-driven styling (the capstone). The payoff: a recursive subdivision core whose styling is a separate callback mapping depth/position to colour — change the callback and the same layout becomes a different piece, without touching the layout code ([[treemap-styling-decoupling]] if present):

function setup() { createCanvas(400, 400); noStroke(); colorMode(HSB, 360, 100, 100); randomSeed(21); }
function draw() { background(0, 0, 8); tile(0, 0, width, 0); }
// layout core — knows nothing about colour
function tile(x, y, s, depth) {
  if (depth >= 5 || (depth > 1 && random() < 0.45)) { style(x, y, s, depth); return; }
  let h = s / 2;
  tile(x, y, h, depth + 1); tile(x + h, y, h, depth + 1);
  tile(x, y + h, h, depth + 1); tile(x + h, y + h, h, depth + 1);
}
// styling callback — swap this freely
function style(x, y, s, depth) {
  fill((200 + depth * 30 + x * 0.2) % 360, 65, 30 + depth * 14);
  rect(x + 1, y + 1, s - 2, s - 2);
}

What good looks like. A pattern with structure at multiple scales — the eye finds order (the grid/symmetry) and detail (the recursion) at once, not a flat uniform field. If it’s crashing or freezing, your recursion is missing a base case or the depth is too high (that’s why the guard matters live); if it’s boring, the subdivision rule is too uniform — vary the split probability by depth or position. Decoupling layout from styling is what turns one sketch into an instrument. (Skill map: live-visualist Domain A4/B — generative structure and composition.)

Now make it yours. Change the split probability in step 6 for denser or sparser results. Rewrite only the style() callback (try circles, or hue by size). Add a symmetry mirror around the whole recursive tiling. Drive the max depth from mouseX for a live “zoom into detail” control.

Runnable examples

Generated from the context/ instrument corpus by concept (redistributable idioms only). Do not edit — regenerate with gen-module-examples.mjs.

tiling-repeat

osc(10).repeat(3, 3).out()

hydra-0008 · CC0-1.0

tile [4,4] (circle 0 0.3) >> add

punctual-0020 · CC0-1.0

repetition-motif

for(let i=0;i<8;i++) circle(width/2, height/2, 200 - i*20)

p5live-0023 · CC0-1.0

Atoms in this module

Required — these gate the capstone

Nested loops over a grid of tiles are the foundation of parametric tiling patterns in p5.js
Procedure L2 First instrument H
There are exactly 17 distinct symmetry groups for periodically tiling the plane
Fact L2 First instrument HL
Recursive grid subdivision generates fractal-like layouts by splitting cells into sub-grids
Procedure L2 First instrument H
Recursive functions in Processing call themselves to generate self-similar, branching forms
Concept L2 First instrument H
Fractals are shapes that exhibit self-similar structure across multiple scales
Concept L3 Craft H
Recursive fractal structures are coded as objects that instantiate child copies of themselves
Procedure L3 Craft H
The squarified treemap algorithm keeps each cell near-square by filling rows until adding the next item would worsen the aspect ratio
Concept L3 Craft H
Treemap boxes can be styled with callback functions that receive value and index, enabling data-driven colour
Procedure L3 Craft H
Separating treemap layout calculation from rendering makes the algorithm reusable across any graphics library
Principle L3 Craft H

Supporting — enrichment, not gating

The 10-PRINT Commodore 64 one-liner generates complex maze-like patterns from a single coin-flip per character cell
Concept L2 First instrument HL
Recursive draw functions produce fractal branching structures where each call draws one branch and spawns smaller sub-branches
Concept L3 Craft H
Processing classes bundle related fields and methods into reusable, instantiable objects
Concept L2 First instrument H
Custom functions in Processing encapsulate reusable code blocks with parameters and return values
Concept L2 First instrument H
Two-dimensional arrays in Processing store grid data as arrays of arrays
Concept L2 First instrument H
Regular or varied spacing of repeated visual units creates rhythm across the frame even in a still image
Concept L2 First instrument HL