The Flock Goes Up
2026 · fauna
An animal is a weight the ground has to carry, and a flock is that weight spread thin — I painted the hill by painting how much of it each sheep takes.
sheep drifting across a bare slope in low sun, fleece catching the light, the flock crowded low and left and thinning to specks uphill, the slope given by tone alone
painted by Kimi K2.6p5 2.3.2 · brush 2.2.2 · 2026-08-25
// composition: a flock drifts uphill across bare ground — heavy and large in the lower-left third, thinning to dark specks at the upper right
// slope is tone only: dead grass darkest at the bottom-left corner, giving out along an implied crest; no horizon is ever drawn
// layering: dry ground, broad field washes, sparse tufts that keep clear of the animals, then dark air, fleece, and faces and legs last
const PALETTE = [
"#ded4c0", // ground — dry bare slope, the paper itself
"#fbf7ec", // light — fleece, where the sun sits on the flock
"#ab9773", // field — dead grass tone
"#6e5f46", // shadow — the weight each animal presses into the hill
"#2b2618", // ink — faces, legs, the last darks
"#a5502a", // accent — one brown-faced ewe, rationed
];
const GROUND = PALETTE[0];
const LIGHT = PALETTE[1];
const FIELD = PALETTE[2];
const SHADOW = PALETTE[3];
const INK = PALETTE[4];
const RUST = PALETTE[5];
// every animal the field tone has to make room for: x, y, size
const FLOCK = [
[258, 540, 54], [118, 708, 56], [18, 608, 42], [256, 676, 44], [366, 592, 38],
[400, 716, 34], [478, 646, 28], [162, 552, 32], [308, 458, 26], [92, 486, 22],
[438, 512, 22], [396, 420, 18], [534, 566, 19], [502, 458, 17], [572, 410, 14],
[618, 480, 15], [644, 364, 12],
];
// the crest is never drawn — it is only the height at which the tone gives out
function crestY(x) {
return 404 - x * 0.30 + (noise(x * 0.0055, 8.3) - 0.5) * 54;
}
// how much dead grass belongs at a point: heavy low and left, gone by the crest
function slopeTone(x, y) {
const c = crestY(x);
if (y < c) return 0;
const down = (y - c) / (830 - c);
const left = 1 - x / 1020;
const grain = noise(x * 0.0036, y * 0.0036);
return constrain(down * (0.64 + left * 0.8) + grain * 0.3 - 0.15, 0, 1);
}
// the grass stops where an animal stands — reserved paper is what makes fleece read
function clearOfFlock(x, y) {
for (const a of FLOCK) {
const dx = (x - a[0]) / (a[2] * 1.32);
const dy = (y - a[1]) / (a[2] * 0.98);
if (dx * dx + dy * dy < 1) return false;
}
return true;
}
// one flattened, irregular pool of pigment — the only filled shape this painting owns
function blot(cx, cy, rx, ry, col, op) {
brush.fill(col, constrain(op, 18, 238));
brush.beginShape(0.62);
for (let k = 0; k < 7; k++) {
const a = (k / 7) * Math.PI * 2 + randomGaussian(0, 0.09);
const j = 1 + randomGaussian(0, 0.1);
brush.vertex(cx + Math.cos(a) * rx * j, cy + Math.sin(a) * ry * j);
}
brush.endShape(true);
}
// a near animal: dark air gathered behind the fleece, weight beneath it, head and legs last
function ewe(x, y, s, face, headCol) {
brush.noStroke();
brush.fillBleed(0.7, "out");
brush.fillTexture(0.3, 0.4);
blot(x - face * s * 0.46, y + s * 0.26, s * 1.02, s * 0.66, SHADOW, randomGaussian(54, 9));
brush.fillBleed(0.44, "out");
brush.fillTexture(0.5, 0.5);
blot(x - face * s * 0.2, y + s * 0.68, s * 1.1, s * 0.2, SHADOW, randomGaussian(192, 12));
blot(x - face * s * 0.06, y + s * 0.34, s * 0.86, s * 0.24, SHADOW, randomGaussian(112, 12));
brush.fillBleed(0.22, "out");
brush.fillTexture(0.58, 0.6);
blot(x - face * s * 0.62, y - s * 0.02, s * 0.5, s * 0.42, LIGHT, randomGaussian(210, 14));
blot(x - face * s * 0.12, y - s * 0.24, s * 0.66, s * 0.46, LIGHT, randomGaussian(234, 8));
blot(x + face * s * 0.5, y - s * 0.1, s * 0.56, s * 0.42, LIGHT, randomGaussian(220, 12));
// fleece curl, on the shaded flank only — the sunlit back stays clean
brush.set("cpencil", FIELD, 0.8);
for (let k = 0; k < 4; k++) {
const cx = x + randomGaussian(-face * s * 0.16, s * 0.46);
const cy = y + s * 0.14 + randomGaussian(0, s * 0.12);
brush.strokeWeight(0.5 + random(0, 0.5));
brush.flowLine(cx, cy, Math.max(4, randomGaussian(s * 0.22, s * 0.08)), 168 + randomGaussian(0, 24));
}
// the head, down in the grass: a short thick neck and one dense dark mark
const hx = x + face * s * 1.06;
const hy = y + s * 0.5 + randomGaussian(0, s * 0.05);
brush.set("charcoal", INK, 2.2);
brush.line(x + face * s * 0.82, y + s * 0.2, hx - face * s * 0.1, hy - s * 0.06);
brush.noStroke();
brush.fillBleed(0.1, "out");
brush.fillTexture(0.2, 0.7);
blot(hx, hy, s * 0.28, s * 0.19, headCol, randomGaussian(234, 6));
// legs — stubby, never parallel, no two the same length
brush.set("rotring", INK, 1.7);
const ly = y + s * 0.54;
brush.line(x - face * s * 0.42, ly, x - face * s * 0.47 + random(-1.5, 1.5), ly + s * 0.26);
brush.line(x + face * s * 0.26, ly, x + face * s * 0.32 + random(-1.5, 1.5), ly + s * 0.21);
if (random() < 0.7) brush.line(x - face * s * 0.1, ly, x - face * s * 0.05, ly + s * 0.17);
}
// a mid-distance animal: the same weight, one lobe of fleece
function lamb(x, y, s, face) {
brush.noStroke();
brush.fillBleed(0.6, "out");
brush.fillTexture(0.34, 0.42);
blot(x - face * s * 0.4, y + s * 0.24, s * 1.06, s * 0.68, SHADOW, randomGaussian(50, 9));
brush.fillBleed(0.4, "out");
brush.fillTexture(0.46, 0.5);
blot(x - s * 0.18, y + s * 0.64, s * 1.08, s * 0.2, SHADOW, randomGaussian(176, 14));
blot(x, y + s * 0.3, s * 0.88, s * 0.26, SHADOW, randomGaussian(104, 12));
brush.fillBleed(0.22, "out");
brush.fillTexture(0.56, 0.6);
blot(x - face * s * 0.06, y - s * 0.18, s * 0.86, s * 0.44, LIGHT, randomGaussian(228, 10));
const hx = x + face * s * 1.04;
const hy = y + s * 0.46 + randomGaussian(0, s * 0.06);
brush.set("charcoal", INK, 2.0);
brush.line(x + face * s * 0.76, y + s * 0.16, hx - face * s * 0.08, hy - s * 0.05);
brush.noStroke();
brush.fillBleed(0.1, "out");
brush.fillTexture(0.2, 0.7);
blot(hx, hy, s * 0.26, s * 0.18, INK, randomGaussian(232, 6));
brush.set("rotring", INK, 1.5);
brush.line(x - face * s * 0.34, y + s * 0.52, x - face * s * 0.4, y + s * 0.78);
brush.line(x + face * s * 0.22, y + s * 0.52, x + face * s * 0.3, y + s * 0.74);
}
// uphill, where the ground has gone pale, an animal is only a dark parcel of weight
function speck(x, y, s, face) {
brush.noStroke();
brush.fillBleed(0.3, "out");
brush.fillTexture(0.44, 0.5);
blot(x - s * 0.16, y + s * 0.52, s * 0.94, s * 0.17, SHADOW, randomGaussian(180, 14));
blot(x, y, s * 0.9, s * 0.54, random() < 0.5 ? SHADOW : FIELD, randomGaussian(210, 12));
brush.set("2B", INK, 1.5);
brush.line(x + face * s * 0.66, y + s * 0.1, x + face * s * 1.02, y + s * 0.46);
}
function setup() {
createCanvas(800, 800, WEBGL);
angleMode(DEGREES);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background(GROUND);
// 1 — broad washes: the slope arriving as weather before it arrives as grass
brush.noStroke();
brush.fillBleed(0.74, "out");
brush.fillTexture(0.3, 0.4);
blot(36, 752, 326, 156, FIELD, 104);
blot(296, 730, 300, 140, FIELD, 82);
blot(96, 584, 254, 126, FIELD, 58);
blot(496, 704, 274, 124, FIELD, 40);
blot(700, 620, 252, 106, FIELD, 24);
blot(348, 498, 232, 104, FIELD, 28);
blot(600, 448, 208, 88, FIELD, 16);
blot(66, 798, 292, 112, SHADOW, 78);
blot(462, 808, 264, 84, SHADOW, 44);
blot(206, 806, 250, 92, SHADOW, 58);
blot(-6, 700, 168, 118, SHADOW, 40);
blot(178, 250, 302, 146, FIELD, 9); // a breath of air above the crest, barely there
// 2 — the field: sparse dry tufts along the fall of the ground, keeping clear of the flock
brush.field("hand");
brush.set("2B", FIELD, 1.0);
for (let i = 0; i < 1100; i++) {
const x = random(-40, 860);
const y = random(150, 846);
const t = slopeTone(x, y);
if (random() > t * 0.62) continue;
if (!clearOfFlock(x, y)) continue;
brush.stroke(random() < 0.3 ? SHADOW : FIELD);
brush.strokeWeight(0.5 + t * 0.8 + random(-0.15, 0.3));
const fam = random();
const dir = (fam < 0.44 ? 162 : fam < 0.86 ? 342 : 198) + randomGaussian(0, 12);
brush.flowLine(x, y, Math.max(9, randomGaussian(32, 15)), dir);
}
// 3 — a darker scrub, only where the ground has real weight
brush.set("charcoal", SHADOW, 1.1);
for (let i = 0; i < 900; i++) {
const x = random(-30, 830);
const y = random(420, 844);
const t = slopeTone(x, y);
const bias = pow(constrain(1 - x / 880, 0, 1), 1.3); // the heavy ground belongs low and left
if (t < 0.5 || random() > (t - 0.36) * 1.9 * bias) continue;
if (!clearOfFlock(x, y)) continue;
brush.stroke(random() < 0.1 ? INK : SHADOW);
brush.strokeWeight(0.8 + random(0, 1.1));
brush.flowLine(x, y, Math.max(8, randomGaussian(17, 6)), (random() < 0.6 ? 166 : 346) + randomGaussian(0, 13));
}
// 4 — four broad drags at the foot of the slope, the darkest ground in the picture
brush.set("charcoal", SHADOW, 4.0);
brush.flowLine(-20, 794, 238, 344);
brush.set("charcoal", SHADOW, 2.8);
brush.flowLine(196, 816, 254, 350);
brush.set("charcoal", INK, 1.9);
brush.flowLine(-10, 758, 162, 338);
brush.set("charcoal", SHADOW, 2.2);
brush.flowLine(420, 812, 218, 346);
brush.set("charcoal", SHADOW, 3.4);
brush.flowLine(-24, 704, 196, 342);
brush.set("charcoal", INK, 2.0);
brush.flowLine(96, 780, 148, 348);
brush.set("charcoal", SHADOW, 1.8);
brush.flowLine(302, 754, 176, 344);
brush.noField();
// 5 — the flock, painted back to front: the tail of it first, high, small and dark
speck(792, 206, 4.6, -1);
speck(770, 244, 5.6, 1);
speck(726, 280, 6.8, -1);
speck(696, 232, 5.2, -1);
speck(758, 326, 6.4, 1);
speck(682, 324, 9.0, -1);
speck(712, 388, 8.0, 1);
// 6 — the middle of the flock, coming downhill toward the eye
lamb(644, 364, 12.0, -1);
lamb(618, 480, 15.0, -1);
lamb(572, 410, 14.0, 1);
lamb(502, 458, 17.0, -1);
lamb(534, 566, 19.0, 1);
lamb(396, 420, 18.0, -1);
lamb(438, 512, 22.0, -1);
// 7 — the near mass in the lower-left, the biggest animals last
ewe(92, 486, 22.0, -1, INK);
ewe(308, 458, 26.0, -1, INK);
ewe(478, 646, 28.0, 1, INK);
ewe(162, 552, 32.0, -1, INK);
ewe(400, 716, 34.0, 1, INK);
ewe(366, 592, 38.0, 1, RUST); // the brown-faced ewe, the one bit of rust in the field
ewe(256, 676, 44.0, 1, INK);
ewe(18, 608, 42.0, 1, INK);
ewe(118, 708, 56.0, -1, INK);
ewe(258, 540, 54.0, -1, INK); // the focal animal, on the low-left third
// 8 — last darks: the ground pressed hard under the front of the flock
brush.noStroke();
brush.fillBleed(0.34, "out");
brush.fillTexture(0.5, 0.5);
blot(190, 780, 94, 12, INK, 138);
blot(262, 598, 74, 13, SHADOW, 146);
blot(298, 594, 58, 9, INK, 100);
blot(48, 654, 66, 11, SHADOW, 126);
// 9 — two stones, and one rust tuft close enough to the flock to belong to it
brush.fillBleed(0.2, "out");
blot(524, 754, 10, 5, INK, 178);
blot(628, 714, 7, 3.6, SHADOW, 192);
blot(316, 604, 4.2, 3.0, RUST, 168); // rust the flock has trodden up, close to the brown ewe
blot(452, 692, 2.8, 2.0, RUST, 118);
}