Expanding on Daniel Shiffman’s The Nature of Code with an interactive playground for real data.
Created Mar 17, 2024 -
Last updated: Mar 17, 2024
Seeding 🌱
Creative Coding
I have been rereading Daniel Shiffman’s The Nature of Code and it is my favorite coding book ever. I have been watching Schiffman’s channel “Coding Train” for years on YouTube, and I love the way he brings code to life and makes it fun!
This is a fun sketchpad for playing with the ideas in the chapters of the book.
I’m working on a documentary poetry library for a project for the Processing Foundation, and wanted a way to play around with algorithms for visualization to find more inspiration for poetic computation.
Emergence Atlas is a way of treating a textbook as a landscape. Each chapter of The Nature of Code is a territory: randomness, vectors, forces, oscillation, particles, agents, physics, automata, fractals, evolution, neural networks. An atlas does not reproduce the territory, it simply interprets it. Each sketch here takes a chapter’s central mechanism and lets it run past the point where the exercise usually stops.
Randomness is hardly ever raw, and true randomness is hard to reproduce. Instead, what we think of as randomness is often shaped by Gaussian distributions, Lévy statistics, Perlin fields, and selection pressure. What we might think of as chaos is actually highly ordered processes in beautiful and complex patterns that aren’t immediately decipherable.
Chapter 0: Randomness
This chapter illustrates the random walker and the Gaussian distribution. In my expansion here, I make the walker’s step sizes themselves follow interesting distributions, and let the walk accumulate into a drawing.
Chapter 0’s point is that the distribution of steps changes the picture, not just “random vs not.” A uniform walker wanders evenly. A Gaussian walker stays local most of the time (randomGaussian for step size). A Lévy-style walk is the next distribution, generally keeping small Gaussian steps, but every once in a while (here about 1% probability), drawing a much larger leap. On screen you get dense clusters where the walker lingered and faint lines where it jumped. This illustrates the heavy-tail lesson Shiffman is setting up before introducing Perlin noise. In the sketch each walker also gets its own leap probability, a shared noise() current drags all the dust the same way, and a territory grid makes walkers that cross another’s cluster trade a fraction of their color.
To push it further:
Track how many grid cells each color owns and plot the census over time.
Make leap probability heritable: split a walker when its cluster grows, and mutate the children.
Replace the noise current with real wind data from the Open-Meteo API.
Chapter 1: Vectors
This chapter introduces vector addition using p5.Vector. You can create a vector, subtract to get a direction (point - pole), scale with setMag / magnitude, rotate, and add several vectors into one.
The book uses this for one mover seeking the mouse. Here I do the same thing for five moving poles at once. Each pole contributes a repulsion or attraction vector. I rotate it a bit for swirl, then v.add(...) stacks them. The summed vector is the local “velocity.”
Each frame I drop random points and step them a few times along that velocity (x += v.x), drawing the path. So the sketch illustrates vector addition and magnitude control applied as a field instead of as a single ball. The first pole is mapped to the mouse and the swirl angle is modulated by noise(), so you can steer the field directly and watch calm regions (cool) and turbulent regions (warm) trade places.
To push it further:
Add arrowheads at streamline ends so direction reads at a glance.
Give poles finite charge that decays and respawns, so the field reorganizes itself.
Release chapter 0 walkers into the field and let dust pile up in the calm regions.
Chapter 2: Forces
This chapter is about force accumulation. Each mover has mass. You sum forces into acceleration, then update vel and pos. The book’s gravity example is with one attractor.
Here in my example, every body attracts every other body (n-body): for each pair, dir = b.pos - a.pos. Force scales like mass / r², then a.acc.add(...) and equal-and-opposite on b. I clamp magSq so that close approaches do not explode, and add a weak pull toward the canvas center so that nothing flies off forever. Stroke color is mapped from vel.mag() (slow = cool, fast = warm). The drawing shows a trail of that integration loop. Clicking adds a low-mass comet with tangential velocity, and any close pass strikes a burst of sparks at the midpoint.
To push it further:
Make one body enormously massive and watch others begin to orbit around like planets around stars.
Add drag proportional to velocity squared and see the pattern settle into stable loops.
Record positions and replay the whole dance backwards.
Chapter 3: Oscillation, expanded into a Harmonograph Choir
This chapter covers simple harmonic motion. My expansion is the Victorian harmonograph: a pen driven by two decaying pendulums at once, x by one sine, y by another.
This happens through detuning. If the frequency ratio were exactly 2:3 the pen would retrace the same Lissajous curve forever. Nudge one frequency by a fraction of a percent and the curve precesses slowly, filling in a shimmering braided band. Exponential damping makes each drawing spiral gracefully inward before the pen resets. Three detuned pens drawing at once make a choir singing close harmony. I also added the rotary third pendulum term, and mapped mouse x to the detune amount, so you can sweep from a locked Lissajous figure to a slowly precessing band.
To push it further:
Add mechanical slop: a little phase noise per cycle, like a worn bearing on a real machine.
Map the frequency ratios to actual musical intervals and play the chord while it draws.
Let damping vary with distance from center so the drawing decays from the outside in.
Chapter 4: Particle Systems
Chapter 4 is about managing a particle array plus an emitter: spawning many short-lived objects, applying forces to each one every frame, and removing them when their lifespan hits zero.
Here, three spawn points along the bottom push out new particles with an upward vy and a small Gaussian vx. Each frame I add a constant downward acceleration (gravity) plus a sideways acceleration from noise(x, y, t), so the force field varies in space and time (Chapter 0’s Perlin noise used as wind).
Lifespan decays by a fixed amount per frame; when life <= 0 the particle is spliced out of the array. While it is alive, life scales the fill alpha and circle radius, and the fill color comes from lerpColor between warm and cool, indexed by 1 - life, so each particle cools as it dies. Two additions push it past the book: a dying ember sometimes spawns two smaller children (tuned just under replacement, so lineages flare but always die out), and clicking fires a radial burst of fifty sparks.
To push it further:
Put the child-spawn probability on a slider and hunt for the exact edge of extinction.
Color bursts by launch height for a fuller firework grammar.
Drive the spawn rate from microphone volume with p5.AudioIn.
Chapter 5: Autonomous Agents
This chapter covers Reynolds-style steering and ends with flocking: separation, alignment, and cohesion, each computed from neighbors within a radius, summed into a steering force, with velocity clamped by a max speed.
My sketch adds two things. First, a falcon that actually hunts: it steers toward the nearest boid, eats it on contact, and the flock replenishes from the edges, so the counter in the corner is a small predator-prey model. Any boid near the falcon gets a flee force, and because neighbors react to each other, the panic spreads through the flock the way it does in real starling murmurations. A quarter of the boids are a second tribe with weaker cohesion and a higher speed limit, so the two groups continually shear past each other. Second, each boid counts its neighbors while computing the three rules, and that count sets its color with lerpColor: green for few neighbors, orange for crowded. Boids wrap at the edges, and each one is drawn as a short line from pos back along vel, so heading is visible without rotating a shape.
To push it further:
Replace the O(n²) neighbor loops with a spatial grid so it scales to thousands of boids.
Hatch eaten boids back as eggs on a delay and plot the predator-prey cycle.
Give the falcon stamina, so every sprint has to be paid for with a glide.
Chapter 6: Physics Libraries, expanded into a Soft Loom
This chapter is about physics libraries (Matter.js and Toxiclibs). Instead of importing one, I implement the core integrator those libraries use: verlet integration. Each point stores its previous position instead of a velocity, so the update is next = pos + (pos - prev) * damping + accel.
The cloth is a grid of points connected by distance constraints between horizontal and vertical neighbors. Each frame I apply gravity and a noise(x, y, t) wind to every unpinned point, then run four relaxation passes: for each constraint, measure the current distance and move both endpoints half the error back toward the rest length. Every fourth point in the top row is pinned, which is why the cloth hangs in swags. Line color and weight come from strain, the difference between a constraint’s current length and its rest length, mapped through lerpColor. Constraints that stretch past 2.4 times their rest length are deleted, so the gusts (wind strength surges on a slow noise curve) can tear the cloth, and the mouse can grab and drag any point.
To push it further:
Add a repair mode: click two torn points to stitch the cloth back together.
Connect the points in a ring with an internal pressure force for a soft-body blob.
Color threads by accumulated stress history and try to predict where the next tear starts.
Chapter 7: Cellular Automata, expanded into Bloom Automata
This chapter covers elementary cellular automata and the Game of Life: a grid where each cell’s next state depends only on its eight neighbors (born on 3, survives on 2 or 3), with wraparound edges.
The rules here are standard Life. What I add is memory in the rendering. Alongside the cell array I keep an age array (how many generations a cell has been alive) and a ghost array (set to 1 when a cell dies, multiplied by 0.9 every generation after). Living cells are colored by age with lerpColor, so fresh growth at the chaotic frontier is warm and stable still-lifes drift toward green. Dead cells fade out as gray ghosts, which leaves visible trails behind gliders. It runs at frameRate(15) so the generations are readable. The grid is also split down the middle: the left half runs Conway’s B3/S23, the right half runs HighLife’s B36/S23, and clicking stamps a glider you can send across the rule border.
To push it further:
Reseed a small random patch whenever the population stops changing.
Try continuous cell states with a smooth activation curve, the direction SmoothLife and Lenia take.
Tint the descendants of your stamped gliders and follow your lineage through the world.
Chapter 8: Fractals, expanded into a Wind Grove
This chapter is about recursion: Cantor sets, Koch curves, and the branching tree, where each branch draws itself shorter and then calls itself at spread angles.
I split the recursion into two passes. branch() runs once in setup and builds each tree as data: every node stores a length, a small Gaussian angle offset, and two or three children scaled by random(0.66, 0.76). drawNode() then walks that structure every frame and adds a sway angle from noise(off + depth, t) at each node. The sway is scaled so the trunk barely moves while the twigs move the most, and because each tree has its own noise offset, gusts travel across the grove instead of locking every canopy to the same phase. Stroke weight comes from depth; leaf dots sit on the terminal nodes. Click the canvas to reseed the grove.
To push it further:
Give each tree a different wind timescale so gusts arrive in sequence across the row.
Add a season loop: shed leaf dots as falling particles, then grow a new canopy.
Swap the hand-rolled recursion for an L-system grammar and mutate the rules each run.
Chapter 9: Evolutionary Computing, expanded into Selective Gardens
This chapter builds a genetic algorithm against a fixed target (the evolving-a-phrase example): fitness, selection, crossover, mutation.
My change is that the fitness target moves. Each of twenty flowers has a genome: petal count, radius, hue, and petal squish. The target genome is read from noise(t), so it drifts over time. Every 150 frames I score the population (negative distance between genome and target, field by field), keep the top two unchanged, and fill the rest by picking two parents from the top eight, doing per-gene crossover (random() < 0.5 ? ma[k] : pa[k]), and mutating each gene with some probability. Because the target never stops moving, the population never converges; you can watch it track the drift a few generations behind. Clicking flowers overrides all of that: liked flowers become the fitness function for the next generation, so you can breed the garden toward your own taste.
To push it further:
Run two gardens with different mutation rates on the same clicks and see which one tracks your taste faster.
Add a symmetry gene and watch lopsided mutants occasionally take over.
Plot mean fitness over generations in a corner to see how far behind the target the population runs.
Chapter 10: Neuroevolution, expanded into Neural Lace
The final chapters cover neural networks and neuroevolution: a perceptron, then multilayer networks, then evolving weights instead of training them.
Here the network is a pattern generator instead of a controller, which is the CPPN idea from the NEAT line of work. A small fixed network (4 inputs, 12 tanh hidden units, 2 outputs) gets each grid cell’s x, y, distance from center, and sin(t) as inputs. One output picks the color via lerpColor; the other sets alpha and circle size. Because the network composes smooth functions, the output is a smooth pattern, and every random initialization of the weights gives a different one. Each frame I nudge every input weight with a tiny randomGaussian and multiply by 0.9998 so the weights cannot blow up. That is evolution with no fitness function, just drift. The mouse position is wired in as two extra inputs, so the whole pattern warps under your cursor, and clicking reinitializes the weights into a new species.
To push it further:
Add a fitness function such as contrast or symmetry and hill-climb the weights instead of drifting.
Give the network extra inputs like sin(5x) or atan2(y, x) to get stripes and mandalas.
Breed two networks by averaging weights and render parent, parent, child side by side.
Appendix: Build a Creature
The book closes with a creature design appendix. Instead of introducing a new mechanic, it asks you to assemble the ones you already have into something that reads as alive. Here is mine: a jellyfish built with principles from five of the chapters in the book.
The locomotion is the part I like most (from Chapter 3). A phase variable drives sin(ph), and thrust is only added during the contraction quarter of each cycle, while the bell is squeezing. The rest of the cycle it coasts against water drag (vel.mult(0.965)), which produces the pulse-and-glide rhythm real jellyfish have. The contraction also reshapes the body: the bell narrows, the dome stretches, and the glow layers brighten with the squeeze.
Steering (from Chapter 5) turns the heading at most 0.02 radians per frame toward the nearest plankton, with a small noise() wobble, so it drifts in lazy curves instead of driving straight at food. Seven tentacles hang from the mouth rim, each a constraint chain (from Chapter 6) whose anchor point is recomputed from the bell geometry every frame; a per-link noise() sway keeps them from stacking into straight lines. The plankton are chapter 0 random walkers, and eating one bursts a few particles (from Chapter 4).
No single part makes it feel alive. But the combination feels like it has some kind of free will! The jellyfish thrusts only happens while the bell squeezes. The tentacles always lag a beat behind the body. And the glow rises and falls with the pulse.
To push it further:
Make it HUNGRY: shrink the jellyfish when it has not eaten recently, and grow its belly (head?) with each meal.
Add a second jellyfish with a separation force so the two have to negotiate the same plankton supply.
If the jellyfish touch one other, make them multiply!