Flow field
Build a grid of direction vectors (from noise, or from an attractor like the mouse), then advect many particles through it and draw their traced paths.
Run it
What it looks like when it goes wrong (4)
- The integration step is larger than one field cell, so each step skips the field it was supposed to follow. The curves stop being smooth and break into straight dashes at odd angles.
- Neighbouring cells decorrelate. A flow field only reads as flow when nearby points agree about direction — past that point it is just noise with extra steps.
- Density without length. The field is invisible because no curve travels far enough to reveal where it is going; you get texture instead of structure.
- The whole canvas samples nearly one value, so every curve points the same way. The result is combed hair — the classic sign the field is under-scaled rather than the algorithm being wrong.
Clean-room implementation — written from the described algorithm, not from source. Reuse policy
What it is
A grid covering the canvas, with an angle stored at each point. Drop a curve somewhere on it, look up the nearest grid angle, take a small step that way, look up again, repeat. Do that from many starting points and you get the sweeping, combed, current-like structure. Flow field is the parent technique here — everything else on this page (a fixed Perlin noise source, a distorted grid, a spaced-out curve set) is that same loop with exactly one piece changed.
How it works
Tyler Hobbs describes the loop directly:
We pick a starting point. We find a corresponding nearby point in the grid. We take the angle from that point in the grid, and take a small step in the direction of that angle. In our new location, we perform another lookup in the grid, and then repeat this process in a loop.
So there are three separable decisions, and only the first is about the field:
- Fill the grid. Any function of position that yields an angle works. Smooth noise is the common choice — Perlin Noise Flow Field is that choice fixed, and is written up as its own page — but noise isn’t required: varun.ca’s first attempt built the grid by rotating each vector toward the mouse location instead, scaled down by the distance from it. Swapping this fill rule changes the output’s character too; the lookup-and-step loop after it doesn’t care where the angle came from.
- Choose starting points. Hobbs treats this as its own design decision with several standard answers, not an afterthought: “All of your curves have to start somewhere.”
- Walk the field and draw each path as a polyline.
Parameters & tuning
Hobbs gives one number by name — “I usually use around 0.5% of the image width for my resolution.” He calls it “resolution” and states it as a percentage of image width, but the excerpt doesn’t say what the resolution is of; nothing here confirms it’s grid spacing, so don’t read that in.
He also states a second number the same way — “that’s usually around 0.1% to 0.5% of the image width” — but the sentence naming what “that’s” refers to isn’t in the excerpt this page has. It reads naturally as step length, since that’s the other per-step quantity the loop needs, but this page won’t assign a source’s number to a parameter it can’t confirm from what it was given. If you need the labelled figure, the source is linked below.
Hobbs also names a parameter directly — num_steps — and the excerpt says
only that “The value of num_steps will affect the texture of the result.”
It doesn’t say what num_steps counts or how the texture changes as you raise
or lower it, so treat it as a knob worth sweeping experimentally rather than a
documented one.
Where it’s been used
Fidenza is Tyler Hobbs’ 2021 Art Blocks series: thick, non-colliding curved rectangles traced through a flow field, coloured from 14 probabilistic palettes. Tyler Hobbs is also the author of the flow-fields essay this page’s algorithm and figures come from.
Variants & neighbours
The variations that matter divide by which of the three decisions they change, which is why the loop above is worth separating out:
- Field construction. Perlin Noise Flow Field fixes the angle source to Perlin noise sampled on a fixed grid. Inserting objects that distort the grid around them perturbs stored angles by proximity to an object, and distorting the grid between rounds of drawing does something similar across successive drawing passes instead.
- Quantising the field. Flow-field angle quantisation snaps each cell’s continuous angle to the nearest multiple of a fixed increment, trading smooth curves for sculpted, angular ones. The traversal loop itself doesn’t change — only how coarse the angle it reads back is.
- What gets drawn. Enforcing a minimum distance between curves stops a curve growing the moment it comes too close to any other already-drawn curve, keeping curves evenly spaced instead of letting them overlap freely. Neighbouring-curve outline blending interpolates between two adjacent curves to build a polygon outline instead of stroking each one.
Go deeper
- Tyler Hobbs — Flow Fields — the essay this page’s algorithm description and resolution figures come from.
Related, in brief
enforce a minimum distance between curves
Stop growing a flow-field curve the moment it comes too close to any other already-drawn curve, to keep curves evenly spaced.
insert objects that distort the grid around them
Embed solid objects in the canvas that locally redirect nearby flow-field vectors, so traced curves wrap around them like fluid around an obstacle.
Inter-pass grid distortion
Perturb a flow field's angle grid slightly between successive drawing passes, so each pass's curves overlap the last without exactly repeating it.
Neighbouring-curve outline blending
Interpolate between two adjacent flow-field curves (with optional easing) to build a smooth polygon outline instead of a plain line stroke.
Connected to
Used in: Fidenza
Used to build: enforce a minimum distance between curves · Inter-pass grid distortion · Neighbouring-curve outline blending
Variants: insert objects that distort the grid around them · Perlin Noise Flow Field
Try it yourself
Hobbs states a number he calls "resolution": "I usually use around 0.5% of the image width for my resolution." Elsewhere in the same essay he gives another number the same way — "that's usually around 0.1% to 0.5% of the image width" — but the sentence naming what that number measures isn't part of the excerpt this page has. What would you guess it's measuring, and how would you find out for certain rather than assume?
Step length is a plausible guess, since it's the other per-step quantity the loop needs — but 'plausible' isn't 'confirmed', and this page states the number as a bare quote instead of a labelled row in a table precisely because assigning it to a parameter the excerpt never names would be an inference dressed as a fact. To settle it for real: read the source essay directly (linked below), where the antecedent is intact.
Replace the stored angles with `angle = atan2(y - h/2, x - w/2)` and run it. Then add a constant to the result: `+ 1.4`.
The first gives a radial burst — every curve flees the centre. Adding roughly a quarter turn rotates every direction off-radial by the same amount and the burst becomes a spiral. The traversal loop never changed; only the angle stored at each grid point did. That is the whole shape of the technique: a field of angles, plus a loop that follows them.