river eddies drawn in ink, one copper leaf caught in the turning
painted by GPT-6 Astrap5 2.3.2 · brush 2.2.2 · 2026-08-24
// composition: large eddy lower-left, medium eddy upper-right, small eddy between; current lines thread the three together, upper-left stays open water
// layering: open water, elliptical ribbons of pooled pigment, broken current filaments, one copper leaf.
const PALETTE = [
"#e4e7e0", // ground: pale green-grey water
"#262b28", // ink: near-black green
"#5c665e", // faded ink for outer rings
"#c3ccc0", // deeper water wash
"#b0682f", // accent: copper leaf
];
// one concentric arc that drifts inward as it turns — hand-drawn spiral fragment
function spiralArc(cx, cy, r0, a0, span, drift, dir, w, col) {
brush.noFill();
brush.set("cpencil", col, w * 1.35);
brush.beginShape(0.55);
const steps = Math.max(4, Math.floor(Math.abs(span) / 0.22));
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const a = a0 + dir * span * t;
const r = r0 - drift * span * t + randomGaussian(0, r0 * 0.012);
const wob = noise(cx * 0.01 + a * 0.7, cy * 0.01) * r0 * 0.05;
brush.vertex(cx + Math.cos(a) * (r + wob), cy + Math.sin(a) * (r + wob) * 0.72);
}
brush.endShape(false);
}
// A turning ribbon deposits pigment on the outside and leaves its inner water open.
const eddyRibbon = (cx, cy, radius, start, sweep, dir, breadth) => {
const pts = [];
for (let edge = 0; edge < 2; edge++) {
for (let k = 0; k <= 14; k++) {
const t = edge === 0 ? k / 14 : 1 - k / 14;
const a = start + dir * sweep * t;
const r = radius * (1 - t * 0.3) - edge * breadth * Math.sin(Math.PI * t);
pts.push([cx + Math.cos(a) * r, cy + Math.sin(a) * r * 0.72]);
}
}
brush.noStroke();
brush.fillBleed(0.5, "out");
brush.fillTexture(0.54, 0.42);
brush.fill(PALETTE[2], 92);
brush.polygon(pts);
};
function vortex(cx, cy, rMax, nArcs, dir) {
for (let i = 0; i < 6; i++) {
eddyRibbon(cx, cy, rMax * (0.94 - i * 0.11), i * 0.62, 3.6, dir, rMax * 0.1);
}
for (let i = 0; i < nArcs; i++) {
const band = 1 - i / nArcs; // 1 = outermost
const r0 = rMax * (0.14 + 0.86 * band) * (1 + randomGaussian(0, 0.04));
const a0 = random(0, Math.PI * 2);
const span = random(1.6, 3.4) * (0.6 + 0.4 * band);
const drift = r0 * random(0.05, 0.11);
const outer = band > 0.55;
const col = outer ? "#5c665e" : "#262b28";
const w = outer ? random(0.7, 1.1) : random(1.0, 1.8);
spiralArc(cx, cy, r0, a0, span, drift, dir, w, col);
}
}
function current(pts, w, col) {
brush.noFill();
brush.set("cpencil", col, w * 1.35);
brush.beginShape(0.65);
for (const p of pts) {
brush.vertex(p[0] + randomGaussian(0, 3), p[1] + randomGaussian(0, 3));
}
brush.endShape(false);
}
function setup() {
createCanvas(800, 800, WEBGL);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background("#e4e7e0");
// two pale water washes, offset, so the paper breathes underneath
brush.noStroke();
brush.fillBleed(0.4, "out");
brush.fillTexture(0.5, 0.5);
brush.fill("#c3ccc0", 34);
brush.circle(300, 540, 300, true);
brush.fill("#c3ccc0", 26);
brush.circle(560, 250, 190, true);
// three vortices, sizes very unequal
vortex(268, 552, 232, 30, 1); // large, lower-left, turning one way
vortex(582, 202, 116, 17, -1); // medium, upper-right, counter-turning
vortex(452, 392, 54, 9, 1); // small, caught between
// current lines threading the eddies together
current([[62, 700], [180, 662], [318, 690], [470, 720]], 1.0, "#5c665e");
current([[430, 452], [452, 500], [420, 560], [352, 610]], 0.9, "#262b28");
current([[492, 360], [540, 312], [560, 268]], 0.9, "#262b28");
current([[610, 330], [590, 400], [530, 448], [498, 430]], 0.8, "#5c665e");
current([[700, 120], [740, 200], [760, 300]], 0.7, "#5c665e");
current([[120, 380], [180, 430], [216, 490]], 0.8, "#5c665e");
// 1% disorder: one stray arc fragment escaping far right, off the system
spiralArc(760, 560, 70, 2.2, 1.4, 8, 1, 0.8, "#5c665e");
// the copper leaf, caught on a ring of the largest eddy
const la = 0.7; // angle on the big eddy
const lr = 128;
const lx = 268 + Math.cos(la) * lr;
const ly = 552 + Math.sin(la) * lr * 0.72;
push();
translate(lx, ly);
rotate(la + Math.PI / 2 + 0.3); // tangent to the current
brush.noStroke();
brush.fillBleed(0.35, "out");
brush.fillTexture(0.6, 0.6);
brush.fill("#b0682f", 165);
brush.beginShape(0.7);
brush.vertex(-26, 0);
brush.vertex(-8, -11);
brush.vertex(20, -4);
brush.vertex(28, 2);
brush.vertex(6, 12);
brush.endShape(true);
brush.fill("#b0682f", 110);
brush.beginShape(0.7);
brush.vertex(-20, -2);
brush.vertex(2, -8);
brush.vertex(22, 0);
brush.vertex(0, 8);
brush.endShape(true);
brush.set("pen", "#7a4620", 0.6);
brush.line(-24, 1, 26, 1);
pop();
}