Raking Light Walk
2026 · architecture
An arcade is a machine for making shadows, and it never makes the same one twice; I painted the intervals rather than the stone.
a cloister arcade wedging up out of the lower-left corner, its arches foreshortening onto the upper-right, every void a slightly different dark, the courtyard left bright and empty but for long raking shadow bars
painted by Kimi K3p5 2.3.2 · brush 2.2.2 · 2026-08-25
// composition: the arcade wedges up out of the lower-left corner, its bays foreshortening onto the
// upper-right third; the courtyard on the right stays bare warm paper, crossed only by raking shadow bars.
// layering: paper, warm pools, the pale wall plane, the gloom of the walk, each void dark, the piers
// reserved bright, then cornice ink, plinth, shadow and the last darks under the near arch.
const PALETTE = [
"#e9e3d5", // ground — sunlit courtyard paper
"#bfb29a", // mass — stone standing in half-light
"#7a6d59", // shadow — reveals, plinth, cast bars
"#2c261f", // ink — the arch voids and the last darks
"#8d9084", // cool — the opposite range across the court
"#c4863a", // accent — raking gold caught on stone arrises
];
// one oblique perspective, held for the whole picture: depth n compresses as 1/(1+K*n)
const VPX = 560;
const VPY = 290;
const K = 0.6;
const NX = -700; // the near bay's lateral throw from the vanishing point
const NBASE = 560;
const NSPRING = -170;
const NCROWN = -255;
const NTOP = -330;
const PIER = 0.34; // share of each bay standing as solid pier
function bay(n) {
const s = 1 / (1 + K * n);
return {
s,
x: VPX + NX * s,
base: VPY + NBASE * s,
spring: VPY + NSPRING * s,
crown: VPY + NCROWN * s,
top: VPY + NTOP * s,
};
}
const BAYS = [];
for (let n = 0; n <= 10; n++) BAYS.push(bay(n));
// the opening between pier i and the next; its two springings sit at different depths, so it skews
function openingOf(i) {
const a = BAYS[i];
const b = BAYS[i + 1];
return {
x0: lerp(a.x, b.x, PIER),
x1: b.x,
baseL: lerp(a.base, b.base, PIER),
baseR: b.base,
springL: lerp(a.spring, b.spring, PIER),
springR: b.spring,
crown: lerp(lerp(a.crown, b.crown, PIER), b.crown, 0.5),
};
}
function archPoints(o, wob, lift) {
const cx = (o.x0 + o.x1) / 2;
const rx = (o.x1 - o.x0) / 2;
const h = (o.springL + o.springR) / 2 - o.crown + lift;
const pts = [[o.x0, o.baseL + wob]];
for (let i = 0; i <= 8; i++) {
const t = i / 8;
pts.push([
cx - rx * Math.cos(Math.PI * t) + randomGaussian(0, wob),
lerp(o.springL, o.springR, t) - h * Math.sin(Math.PI * t) + randomGaussian(0, wob),
]);
}
pts.push([o.x1, o.baseR + wob]);
return pts;
}
// each void a different dark: pigment soaked in over several passes, then hatched across in one
// direction laid twice a few degrees apart — etched, not woven. The angle turns bay to bay.
function archVoid(i, alpha, wob, lift, hatchAngle, hatchDist, passes) {
const o = openingOf(i);
brush.noStroke();
brush.noHatch();
for (let p = 0; p < passes; p++) {
brush.fillBleed(constrain(randomGaussian(0.18 - p * 0.05, 0.03), 0.06, 0.24), "out");
brush.fillTexture(constrain(randomGaussian(0.36, 0.08), 0.2, 0.58), 0.3);
brush.fill(PALETTE[3], alpha * (1 - p * 0.14));
brush.polygon(archPoints(o, wob * (1 - p * 0.22), lift * (1 - p * 0.2)));
}
brush.hatchStyle("charcoal", PALETTE[3], 1.25);
brush.hatch(hatchDist, hatchAngle, { rand: 0.3, continuous: false, gradient: 0.25 });
brush.fill(PALETTE[3], 215);
brush.polygon(archPoints(o, wob * 0.4, lift * 0.42));
brush.hatch(hatchDist * 1.15, hatchAngle + 7, { rand: 0.36, continuous: false, gradient: 0.45 });
brush.fill(PALETTE[3], 160);
brush.polygon(archPoints(o, wob * 0.3, lift * 0.32));
brush.noHatch();
return o;
}
// the archivolt: a dark band riding over each head, which is what makes an arcade read as an arcade
function archivolt(o, weight, alpha) {
const cx = (o.x0 + o.x1) / 2;
const rx = (o.x1 - o.x0) / 2;
const h = (o.springL + o.springR) / 2 - o.crown;
brush.noFill();
brush.set("charcoal", PALETTE[3], weight);
brush.beginShape(0.4);
for (let i = 0; i <= 8; i++) {
const t = i / 8;
brush.vertex(
cx - rx * Math.cos(Math.PI * t) + randomGaussian(0, weight * 0.8),
lerp(o.springL, o.springR, t) - h * Math.sin(Math.PI * t) + randomGaussian(0, weight * 0.8)
);
}
brush.endShape(false);
brush.noStroke();
brush.noHatch();
brush.fillBleed(0.22, "out");
brush.fill(PALETTE[2], alpha);
brush.polygon([
[o.x0 - 1, o.springL - h * 0.35], [cx, o.crown - h * 0.16], [o.x1 + 1, o.springR - h * 0.32],
[o.x1 - 1, o.springR - h * 0.5], [cx, o.crown - h * 0.34], [o.x0 + 1, o.springL - h * 0.52],
]);
}
// the lit cheek of a pier: paper reclaimed out of the wall, with the reveal turning away dark
function pierFace(i, lit, revealAlpha, bleed) {
const a = BAYS[i];
const b = BAYS[i + 1];
const xR = lerp(a.x, b.x, PIER);
const topR = lerp(a.top, b.top, PIER);
const baseR = lerp(a.base, b.base, PIER);
const j = () => randomGaussian(0, 1.4);
brush.noStroke();
brush.noHatch();
brush.fillBleed(bleed, "out");
brush.fillTexture(0.42, 0.55);
brush.fill(PALETTE[0], lit);
brush.polygon([
[a.x + j(), a.top + j() * 1.6], [xR + j(), topR + j() * 1.6],
[xR + j(), baseR + j() * 1.6], [a.x + j(), a.base + j() * 1.6],
]);
const w = constrain((xR - a.x) * 0.34, 2.5, 34);
brush.fillBleed(bleed * 1.2, "out");
brush.fill(PALETTE[2], revealAlpha);
brush.polygon([
[xR - w, lerp(a.top, topR, 0.66) + j()], [xR, topR],
[xR, baseR], [xR - w, lerp(a.base, baseR, 0.66) + j()],
]);
}
// the same pier lit again after the voids are down, inset so the dark of the openings is not eaten
function pierRelight(i, alpha) {
const a = BAYS[i];
const b = BAYS[i + 1];
const at = (v, w, t) => lerp(v, lerp(v, w, PIER), t);
const j = () => randomGaussian(0, 1.2);
brush.noStroke();
brush.noHatch();
brush.fillBleed(0.06, "out");
brush.fillTexture(0.4, 0.5);
brush.fill(PALETTE[0], alpha);
brush.polygon([
[at(a.x, b.x, 0.2) + j(), at(a.top, b.top, 0.2) + j() * 1.7],
[at(a.x, b.x, 0.78) + j(), at(a.top, b.top, 0.78) + j() * 1.7],
[at(a.x, b.x, 0.78) + j(), at(a.base, b.base, 0.78) + j() * 1.7],
[at(a.x, b.x, 0.2) + j(), at(a.base, b.base, 0.2) + j() * 1.7],
]);
}
// light spilling off the court onto the floor of the walk, seen low through an opening
function floorLight(o, alpha, rise) {
const cx = (o.x0 + o.x1) / 2;
brush.noStroke();
brush.noHatch();
brush.fillBleed(0.36, "out");
brush.fillTexture(0.3, 0.4);
brush.fill(PALETTE[0], alpha);
brush.polygon([
[o.x0 + 3, o.baseL - 1], [o.x1 - 3, o.baseR - 1],
[o.x1 - 7, o.baseR - rise], [o.x0 + 9, o.baseL - rise * 1.35],
]);
brush.fill(PALETTE[5], alpha * 0.42);
brush.polygon([
[o.x0 + 8, o.baseL - 3], [cx, o.baseR - 4],
[cx - 4, o.baseR - rise * 0.55], [o.x0 + 13, o.baseL - rise * 0.8],
]);
}
// one long shadow bar thrown out of the arcade across the paving, soaked three times over
function shadowBar(x, y, len, drop, thick, alpha) {
const quad = () => [
[x + randomGaussian(0, 3), y + randomGaussian(0, 2.4)],
[x + len, y + drop + randomGaussian(0, 3.4)],
[x + len + thick * 0.55, y + drop + thick + randomGaussian(0, 2.4)],
[x + thick * 0.3, y + thick + randomGaussian(0, 2.4)],
];
brush.noStroke();
brush.noHatch();
for (let p = 0; p < 3; p++) {
brush.fillBleed(constrain(randomGaussian(0.16 - p * 0.04, 0.03), 0.05, 0.26), "out");
brush.fillTexture(0.34, 0.3);
brush.fill(PALETTE[2], alpha * (1 - p * 0.1));
brush.polygon(quad());
}
}
// a single soft blot of pigment — the workhorse mark of every wash in this picture
function blot(colour, alpha, x, y, r) {
brush.fill(colour, alpha);
brush.circle(x, y, r, true);
}
// one drawn run: a list of points taken as an open curve
function path(pts, curvature) {
brush.beginShape(curvature);
for (let i = 0; i < pts.length; i++) brush.vertex(pts[i][0], pts[i][1]);
brush.endShape(false);
}
function setup() {
createCanvas(800, 800, WEBGL);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background(PALETTE[0]);
brush.noStroke();
brush.noHatch();
// warm pools where the sun stands on the court — broad, bleeding, nothing with an edge
brush.fillBleed(0.66, "out");
brush.fillTexture(0.26, 0.35);
blot(PALETTE[5], 13, 654, 596, 232);
blot(PALETTE[5], 9, 524, 704, 202);
blot(PALETTE[1], 14, 748, 452, 176);
blot(PALETTE[1], 10, 430, 764, 158);
// the opposite range across the court: pale bands, no hatch, almost weightless
brush.fillBleed(0.3, "out");
brush.fillTexture(0.34, 0.4);
brush.fill(PALETTE[4], 46);
brush.polygon([[472, 248], [820, 232], [822, 292], [474, 304]]);
brush.fill(PALETTE[4], 26);
brush.polygon([[488, 216], [824, 202], [822, 246], [478, 256]]);
brush.fill(PALETTE[1], 22);
brush.polygon([[478, 300], [824, 288], [826, 340], [482, 356]]);
brush.fill(PALETTE[2], 24);
brush.polygon([[496, 294], [744, 284], [746, 300], [498, 310]]);
// the wall plane in one pale pass — a wedge running off three edges
brush.fillBleed(0.4, "out");
brush.fillTexture(0.68, 0.5);
brush.fill(PALETTE[1], 40);
brush.polygon([
[-160, -70], [BAYS[9].x, BAYS[9].top], [BAYS[10].x + 22, BAYS[10].base],
[190, 690], [-40, 848], [-170, 870],
]);
// the eaves shadow: one dark strip lying under the cornice the whole length of the run
brush.fillBleed(0.34, "out");
brush.fillTexture(0.5, 0.45);
brush.fill(PALETTE[2], 88);
brush.polygon([
[-170, -34], [BAYS[9].x + 6, BAYS[9].top + 3], [BAYS[9].x + 4, BAYS[9].top + 15],
[128, 128], [-30, 60], [-172, 6],
]);
// spandrels: the solid above the heads darkening a little, light coming from the left
brush.fillBleed(0.36, "out");
brush.fill(PALETTE[2], 28);
brush.polygon([
[-158, 34], [BAYS[8].x, BAYS[8].top + 8], [BAYS[8].x, BAYS[8].spring - 6],
[BAYS[4].x, BAYS[4].spring - 14], [-150, 190],
]);
// the paving of the walk, a pale plane sliding under the piers
brush.fillBleed(0.42, "out");
brush.fillTexture(0.45, 0.4);
brush.fill(PALETTE[1], 30);
brush.polygon([
[BAYS[10].x, BAYS[10].base + 4], [206, 704], [-30, 862], [-160, 880], [-160, 660],
]);
// the collective gloom of the walk, gathered along the openings before any one of them is cut
brush.fillBleed(0.56, "out");
brush.fillTexture(0.4, 0.4);
blot(PALETTE[3], 46, 46, 420, 176);
blot(PALETTE[3], 40, 212, 372, 128);
blot(PALETTE[3], 34, 322, 336, 92);
blot(PALETTE[3], 30, 404, 314, 62);
blot(PALETTE[3], 26, 456, 300, 40);
// the piers first — laid before the voids so the dark bleeds onto stone, not the other way round
pierFace(0, 212, 118, 0.2);
pierFace(1, 200, 110, 0.19);
pierFace(2, 190, 102, 0.18);
pierFace(3, 178, 94, 0.16);
pierFace(4, 166, 86, 0.14);
pierFace(5, 152, 78, 0.12);
pierFace(6, 140, 70, 0.11);
pierFace(7, 126, 62, 0.1);
pierFace(8, 114, 54, 0.09);
// nine voids, far to near; the near ones go last and darkest, and no two shadows match
archVoid(8, 132, 0.8, 0.5, 108, 2.4, 2);
archVoid(7, 142, 1.0, 0.8, 62, 2.6, 2);
archVoid(6, 152, 1.2, 1.2, 126, 2.8, 2);
archVoid(5, 162, 1.4, 1.6, 44, 3.0, 2);
archVoid(4, 170, 1.7, 2.2, 114, 3.3, 3);
archVoid(3, 178, 2.0, 3.0, 74, 3.6, 3);
archVoid(2, 186, 2.4, 4.0, 132, 3.9, 4);
archVoid(1, 192, 2.9, 5.5, 56, 4.3, 4);
const near = archVoid(0, 198, 3.6, 7.5, 122, 4.8, 4);
const o1 = openingOf(1);
const o2 = openingOf(2);
const o3 = openingOf(3);
// the vault of the near bays takes an extra soak — shadow always pools at the top of a hole
brush.noHatch();
brush.fillBleed(0.5, "out");
brush.fillTexture(0.4, 0.4);
blot(PALETTE[3], 104, (near.x0 + near.x1) / 2 - 10, near.crown + 58, 48);
blot(PALETTE[3], 84, (near.x0 + near.x1) / 2 + 22, near.crown + 34, 28);
blot(PALETTE[3], 88, (o1.x0 + o1.x1) / 2 - 4, o1.crown + 30, 22);
// and the piers come back a second time, tight-edged, so the intervals of light hold
pierRelight(0, 132);
pierRelight(1, 156);
pierRelight(2, 164);
pierRelight(3, 152);
pierRelight(4, 136);
pierRelight(5, 116);
pierRelight(6, 98);
pierRelight(7, 78);
pierRelight(8, 62);
// light raking in along the right reveal of the near openings, and a lozenge on the floor within
brush.fillBleed(0.46, "out");
brush.fillTexture(0.3, 0.4);
brush.fill(PALETTE[0], 128);
brush.polygon([
[near.x1 - 28, near.springR - 6], [near.x1 - 5, near.springR + 8],
[near.x1 - 7, near.baseR - 14], [near.x1 - 36, near.baseR - 34],
]);
brush.fill(PALETTE[0], 104);
brush.polygon([
[o1.x1 - 16, o1.springR + 2], [o1.x1 - 4, o1.springR + 7],
[o1.x1 - 5, o1.baseR - 9], [o1.x1 - 20, o1.baseR - 20],
]);
blot(PALETTE[5], 92, near.x1 - 48, near.baseR - 24, 18);
blot(PALETTE[0], 96, o1.x1 - 27, o1.baseR - 14, 10.5);
blot(PALETTE[0], 76, o2.x1 - 16, o2.baseR - 9, 6.5);
floorLight(near, 138, 96);
floorLight(o1, 118, 58);
floorLight(o2, 96, 34);
floorLight(o3, 74, 21);
// the archivolts, near three heavy, the rest thinning away
archivolt(near, 2.2, 92);
archivolt(o1, 1.7, 80);
archivolt(o2, 1.3, 68);
archivolt(o3, 1.0, 56);
archivolt(openingOf(4), 0.85, 44);
archivolt(openingOf(5), 0.7, 34);
// imposts: every springing gets a short block, heavy near, a tick far off
const o4 = openingOf(4);
const o5 = openingOf(5);
brush.noFill();
brush.set("charcoal", PALETTE[3], 2.0);
brush.line(near.x0 - 16, near.springL + 3, near.x0 + 13, near.springL - 2);
brush.line(near.x1 - 12, near.springR + 2, near.x1 + 14, near.springR - 3);
brush.set("charcoal", PALETTE[3], 1.6);
brush.line(o1.x0 - 11, o1.springL + 2, o1.x0 + 9, o1.springL - 2);
brush.line(o1.x1 - 8, o1.springR + 2, o1.x1 + 10, o1.springR - 2);
brush.set("2B", PALETTE[3], 1.3);
brush.line(o2.x0 - 7, o2.springL + 1, o2.x0 + 6, o2.springL - 1);
brush.line(o2.x1 - 5, o2.springR + 1, o2.x1 + 7, o2.springR - 2);
brush.set("2B", PALETTE[2], 1.1);
brush.line(o3.x0 - 5, o3.springL, o3.x0 + 5, o3.springL - 1);
brush.line(o3.x1 - 4, o3.springR, o3.x1 + 5, o3.springR - 1);
brush.line(o4.x0 - 4, o4.springL, o4.x0 + 4, o4.springL - 1);
brush.line(o5.x0 - 3, o5.springL, o5.x0 + 3, o5.springL - 1);
// the dark arris where each pier turns into its reveal, cut hard against the void
brush.set("charcoal", PALETTE[3], 1.9);
brush.line(near.x0 + 1, near.springL - 26, near.x0 - 2, near.baseL - 6);
brush.set("charcoal", PALETTE[3], 1.5);
brush.line(o1.x0 + 1, o1.springL - 16, o1.x0 - 1, o1.baseL - 4);
brush.set("2B", PALETTE[3], 1.2);
brush.line(o2.x0, o2.springL - 10, o2.x0 - 1, o2.baseL - 3);
brush.line(o3.x0, o3.springL - 7, o3.x0 - 1, o3.baseL - 2);
brush.set("2B", PALETTE[2], 1.0);
brush.line(o4.x0, o4.springL - 5, o4.x0, o4.baseL - 2);
// the string course at the springing, run the whole length and broken twice —
// it is what stops the piers reading as a row of staves
brush.set("charcoal", PALETTE[3], 3.4);
brush.line(-172, 116, 44, 172);
brush.set("charcoal", PALETTE[3], 2.6);
brush.line(60, 176, 214, 216);
brush.set("charcoal", PALETTE[3], 1.9);
brush.line(232, 220, 348, 248);
brush.set("2B", PALETTE[3], 1.3);
brush.line(362, 251, BAYS[9].x + 6, BAYS[9].spring + 1);
brush.set("2H", PALETTE[0], 2.2);
brush.line(-170, 104, 52, 161);
brush.line(74, 166, 226, 205);
brush.set("2H", PALETTE[0], 1.3);
brush.line(244, 210, 356, 238);
// the plinth: one dark run along the foot of every pier, thinning as it goes
brush.set("charcoal", PALETTE[2], 3.6);
brush.line(-172, 846, 4, 762);
brush.set("charcoal", PALETTE[2], 2.8);
brush.line(28, 748, 172, 646);
brush.set("charcoal", PALETTE[2], 2.0);
brush.line(196, 632, 306, 540);
brush.set("2B", PALETTE[2], 1.4);
brush.line(322, 528, 402, 468);
brush.set("2B", PALETTE[2], 1.0);
brush.line(414, 460, BAYS[10].x + 16, BAYS[10].base - 4);
// where the bays run out, a compressed clot of dark on the upper-right third
brush.noStroke();
brush.hatchStyle("charcoal", PALETTE[3], 1.1);
brush.hatch(3.4, 104, { rand: 0.32, continuous: false, gradient: 0.3 });
brush.fillBleed(0.26, "out");
brush.fill(PALETTE[3], 150);
brush.polygon([
[BAYS[9].x - 4, BAYS[9].top + 4], [BAYS[10].x + 26, BAYS[10].top + 10],
[BAYS[10].x + 30, BAYS[10].base + 2], [BAYS[9].x - 2, BAYS[9].base + 6],
]);
brush.noHatch();
brush.fillBleed(0.44, "out");
blot(PALETTE[3], 130, BAYS[10].x + 12, VPY + 14, 20);
blot(PALETTE[2], 96, BAYS[9].x + 8, VPY + 46, 15);
// cornice: one heavy run, a lighter line under it, and three broken shadows below that
brush.noFill();
brush.set("charcoal", PALETTE[3], 2.0);
path([[-168, -46], [-30, 22], [128, 92], [268, 152], [378, 196], [BAYS[9].x + 8, BAYS[9].top + 4]], 0.3);
brush.set("2B", PALETTE[2], 1.2);
brush.line(-160, 14, 40, 106);
brush.line(96, 128, 262, 194);
brush.line(310, 212, 424, 250);
brush.set("charcoal", PALETTE[2], 1.5);
brush.line(-162, 120, -28, 172);
brush.line(58, 200, 182, 244);
brush.line(258, 266, 342, 288);
// the base line of the piers, and the step down onto the court
brush.set("charcoal", PALETTE[3], 1.6);
path([[-172, 872], [-24, 792], [128, 700], [264, 592], [370, 494], [BAYS[10].x + 24, BAYS[10].base + 4]], 0.26);
brush.set("2H", PALETTE[2], 0.9);
path([[-166, 892], [4, 802], [168, 692], [310, 566], [BAYS[10].x + 30, BAYS[10].base + 18]], 0.26);
// long shadow bars raking out of the arcade onto the empty paving; nothing else is written there
shadowBar(-84, 748, 432, 134, 54, 158);
shadowBar(BAYS[1].x - 108, BAYS[1].base + 122, 486, 158, 44, 146);
shadowBar(BAYS[1].x + 6, BAYS[1].base + 8, 428, 138, 33, 138);
shadowBar(BAYS[2].x + 5, BAYS[2].base + 6, 356, 112, 25, 122);
shadowBar(BAYS[3].x + 4, BAYS[3].base + 5, 288, 88, 18, 102);
shadowBar(BAYS[4].x + 3, BAYS[4].base + 4, 226, 68, 13, 80);
shadowBar(BAYS[5].x + 2, BAYS[5].base + 3, 168, 50, 9, 58);
// stone joints and one long crack down the near pier, cut across the courses
brush.noFill();
brush.set("2B", PALETTE[2], 1.1);
brush.line(-158, 240, -58, 272);
brush.line(-156, 354, -54, 382);
brush.line(-154, 472, -50, 496);
brush.line(-150, 592, -46, 610);
brush.set("charcoal", PALETTE[3], 1.3);
path([[-122, 218], [-98, 334], [-114, 456], [-86, 578]], 0.5);
brush.set("2H", PALETTE[2], 0.85);
brush.line(128, 300, 172, 318);
brush.line(130, 396, 174, 410);
brush.line(248, 344, 278, 356);
// grit dragged along the vaults, following the flow of the deep shadow
brush.set("charcoal", PALETTE[3], 1.9);
brush.field("curved");
brush.flowLine(-84, 214, 168, 32);
brush.flowLine(-70, 402, 142, 24);
brush.flowLine(-56, 588, 118, 18);
brush.flowLine(78, 252, 118, 22);
brush.flowLine(92, 470, 96, 16);
brush.set("spray", PALETTE[3], 1.4);
brush.flowLine(220, 296, 82, 15);
brush.flowLine(328, 324, 62, 11);
brush.noField();
// raking gold, rationed and kept tight to the edge: three lit arrises, one flare on the paving
brush.noStroke();
brush.fillBleed(0.08, "out");
brush.fillTexture(0.4, 0.5);
brush.fill(PALETTE[5], 124);
brush.polygon([[-160, 196], [-146, 202], [-142, 638], [-159, 654]]);
brush.fill(PALETTE[5], 104);
brush.polygon([
[BAYS[1].x + 1, BAYS[1].spring + 10], [BAYS[1].x + 9, BAYS[1].spring + 16],
[BAYS[1].x + 7, BAYS[1].base - 8], [BAYS[1].x - 2, BAYS[1].base + 2],
]);
brush.fill(PALETTE[5], 58);
brush.polygon([
[BAYS[3].x, BAYS[3].spring + 14], [BAYS[3].x + 5, BAYS[3].spring + 18],
[BAYS[3].x + 4, BAYS[3].base - 18], [BAYS[3].x - 1, BAYS[3].base - 14],
]);
blot(PALETTE[5], 88, BAYS[2].x + 5, BAYS[2].spring + 48, 6.5);
blot(PALETTE[5], 52, 604, 672, 26);
// three loose stones on the court, all that stands between the arcade and the light
brush.fillBleed(0.28, "out");
blot(PALETTE[2], 122, 178, 672, 6.5);
blot(PALETTE[2], 104, 244, 646, 4.5);
blot(PALETTE[3], 96, 96, 736, 3.5);
// last darks: the underside of the near arch, held down hard
brush.noFill();
brush.set("charcoal", PALETTE[3], 2.4);
path([
[near.x0 - 4, near.springL + 40], [near.x0 + 18, near.springL - 46],
[(near.x0 + near.x1) / 2, near.crown - 4],
[near.x1 - 16, near.springR - 32], [near.x1 - 2, near.springR + 30],
], 0.42);
brush.set("2B", PALETTE[3], 1.5);
brush.line(near.x0 + 6, near.baseL - 40, near.x1 - 10, near.baseR - 18);
brush.line(o1.x0 + 4, o1.baseL - 22, o1.x1 - 6, o1.baseR - 10);
}