Wisteria Falling
2026 · botanical
The vine does all its arguing at the top; everything below it is surrender.
cascading wisteria racemes hanging from the top edge, lavender on warm grey
painted by DeepSeek V4p5 2.3.2 · brush 2.2.2 · 2026-08-24
// composition: wisteria racemes cascade from a branch along the top edge, heaviest upper-right; the lower-left quarter stays open paper
// layering: pale violet air wash, branch and leaflets, then five racemes back-to-front — grey-green axis first, paired florets shrinking to a bud tip
const PALETTE = [
"#eae6df", // pale warm-grey paper
"#a794c9", // lavender wash
"#8b74b5", // mid violet
"#5a4384", // deep violet accent, rationed
"#7d8672", // grey-green branch and stems
];
function floretPair(x, y, ang, size) {
const px = Math.cos(ang + Math.PI / 2);
const py = Math.sin(ang + Math.PI / 2);
brush.noStroke();
brush.fillBleed(0.42, "out");
brush.fillTexture(0.55, 0.5);
const deep = random() < 0.08;
const cA = deep ? "#5a4384" : random() < 0.55 ? "#a794c9" : "#8b74b5";
const cB = random() < 0.5 ? "#a794c9" : "#8b74b5";
brush.fill(cA, deep ? 178 : 104 + random(-16, 24));
brush.circle(x + px * size * 0.6, y + py * size * 0.6, size * (0.92 + random(-0.12, 0.12)), true);
brush.fill(cB, 92 + random(-14, 26));
brush.circle(x - px * size * 0.56, y - py * size * 0.52, size * (0.78 + random(-0.1, 0.14)), true);
}
function raceme(x0, y0, len, sway, pairs, size0) {
const n = pairs + 3;
const pts = [];
let x = x0;
let y = y0;
for (let i = 0; i <= n; i++) {
const t = i / n;
pts.push([x, y]);
x += sway * (1 - t * 0.55) + random(-2.4, 2.4);
y += (len / n) * (0.72 + 0.5 * t);
}
brush.noFill();
brush.set("2B", "#7d8672", 0.9);
brush.beginShape(0.4);
for (let i = 0; i <= n; i++) brush.vertex(pts[i][0], pts[i][1]);
brush.endShape(false);
for (let i = 2; i < pairs + 2; i++) {
const t = (i - 2) / pairs;
const a = Math.atan2(pts[i + 1][1] - pts[i][1], pts[i + 1][0] - pts[i][0]);
const s = size0 * (1 - 0.72 * t) * (1 + random(-0.09, 0.09));
floretPair(pts[i][0], pts[i][1], a, s);
}
brush.noStroke();
brush.fill("#8b74b5", 122);
brush.circle(pts[n][0], pts[n][1], size0 * 0.24, true);
}
function leaflet(x, y, ang, len) {
brush.noStroke();
brush.fillBleed(0.35, "out");
brush.fillTexture(0.5, 0.55);
brush.fill("#7d8672", 62 + random(-10, 14));
brush.beginShape(0.6);
brush.vertex(x, y);
brush.vertex(x + Math.cos(ang - 0.28) * len * 0.5, y + Math.sin(ang - 0.28) * len * 0.5);
brush.vertex(x + Math.cos(ang) * len, y + Math.sin(ang) * len);
brush.vertex(x + Math.cos(ang + 0.3) * len * 0.52, y + Math.sin(ang + 0.3) * len * 0.52);
brush.endShape(true);
}
function setup() {
createCanvas(800, 800, WEBGL);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background("#eae6df");
// a breath of violet air behind the mass, upper-right
brush.noStroke();
brush.fillBleed(0.6, "out");
brush.fillTexture(0.4, 0.4);
brush.fill("#a794c9", 18);
brush.circle(608, 190, 238, true);
// the branch, entering from the right edge
brush.noFill();
brush.set("charcoal", "#7d8672", 2.3);
brush.beginShape(0.45);
brush.vertex(806, 88);
brush.vertex(688, 56);
brush.vertex(560, 52);
brush.vertex(474, 34);
brush.vertex(414, 40);
brush.endShape(false);
brush.set("2B", "#7d8672", 1.1);
brush.line(742, 74, 700, 96);
brush.line(560, 52, 528, 76);
// leaflet clusters where the racemes hang
leaflet(700, 70, 2.6, 52);
leaflet(716, 88, 1.9, 44);
leaflet(590, 60, 2.9, 48);
leaflet(568, 74, 2.1, 40);
leaflet(480, 48, 2.5, 42);
leaflet(462, 60, 1.7, 36);
leaflet(662, 52, 3.3, 38);
// five racemes, back-to-front, the longest carrying the weight at the right
raceme(766, 118, 300, -0.8, 8, 15);
raceme(486, 44, 236, 0.9, 7, 13);
raceme(566, 58, 328, -0.5, 9, 15);
raceme(700, 92, 458, 0.4, 13, 17);
raceme(636, 66, 388, -1.1, 11, 16);
// one loosed floret, drifting below everything
brush.noStroke();
brush.fillBleed(0.5, "out");
brush.fill("#8b74b5", 66);
brush.circle(618, 636, 7, true);
}