Noon Cuts the Yard
2026 · architecture
Sunlight is the only mason in this yard: it rules a straight edge across the lime and lays the piers down flat, and the well stands in the last of the ground the geometry has not reached.
a whitewashed courtyard at noon, one hard blue shadow ruled diagonally across the lower right, the well head standing alone in the light
painted by DeepSeek V4p5 2.3.2 · brush 2.2.2 · 2026-08-25
// composition: one hard shadow rules a diagonal from the foot of the wall to the lower-left corner;
// the well head stands on the lower-left thirds crossing in full sun, and the limewash between the
// two is left bare. Layering: air, sky band and wall planes, then the ground shade laid in long
// bands running parallel to its own edge, a crisp strip along that edge last of all, then the piers
// thrown flat, the drum, and the darks — arch mouths, well mouth, ruled seams.
const PALETTE = [
"#f1ece1", // ground — limewash paper, the light itself
"#dcd6c7", // mass — warm masonry grey
"#9db4cb", // shadow, pale pass
"#48699a", // shadow, mid pass
"#1d3155", // shadow, deepest — sky, openings, ruled seams
"#bd7a45", // accent — worn terracotta at the well
];
const PAPER = PALETTE[0];
const LIME = PALETTE[1];
const PALE = PALETTE[2];
const BLUE = PALETTE[3];
const DEEP = PALETTE[4];
const OCHRE = PALETTE[5];
// the three directions the yard is built on
const A = { x: 792, y: 239 }; // where the shade meets the foot of the wall
const E = { x: -0.734, y: 0.677 }; // along the edge, running to the lower left
const N = { x: 0.677, y: 0.734 }; // across it, into the shade
const THROW = { x: -0.93, y: 0.37 }; // the way the sun lays a pier down flat
const WELL = { x: 267, y: 500, hw: 82 };
function mixHex(a, b, t) {
let out = "#";
for (let i = 1; i < 6; i += 2) {
const va = parseInt(a.slice(i, i + 2), 16);
const vb = parseInt(b.slice(i, i + 2), 16);
out += ("0" + floor(lerp(va, vb, constrain(t, 0, 1)) + 0.5).toString(16)).slice(-2);
}
return out;
}
// no two marks the same pigment: nudge toward the deep blue, or back toward the warm lime
function jig(c, spread) {
const d = randomGaussian(0, spread);
return d > 0 ? mixHex(c, DEEP, d) : mixHex(c, LIME, -d * 1.4);
}
function baseY(x) {
return 248 - (10 * x) / 806;
}
// how deep into the shade a point lies, and how far along the edge
function depth(x, y) {
return (x - A.x) * N.x + (y - A.y) * N.y;
}
function blob(x, y, r, col, op, bleed) {
brush.noStroke();
brush.fillBleed(bleed, "out");
brush.fillTexture(0.62, 0.5);
brush.fill(col, op);
brush.circle(x, y, r, true);
}
// a plane of shade, kept hard-edged by a low bleed and roughened by hand at every corner
function slab(pts, col, op, bleed, jitter, curv) {
brush.noStroke();
brush.fillBleed(bleed, "out");
brush.fillTexture(0.62, 0.5);
brush.fill(col, op);
brush.beginShape(curv);
for (let i = 0; i < pts.length; i++) {
brush.vertex(pts[i][0] + randomGaussian(0, jitter), pts[i][1] + randomGaussian(0, jitter));
}
brush.endShape(true);
}
// a breath of tone with no edge at all — for air, never for architecture
function air(x, y, rx, ry, col, op) {
brush.noStroke();
brush.fillBleed(0.42, "out");
brush.fillTexture(0.26, 0.3);
brush.fill(col, op);
brush.beginShape(0.9);
for (let i = 0; i < 11; i++) {
const a = i * 32.7;
const k = 1 + randomGaussian(0, 0.13);
brush.vertex(x + cos(a) * rx * k, y + sin(a) * ry * k);
}
brush.endShape(true);
}
// one stroke of the shade, running the way the edge runs: t..t+len along it, d..d+w across
function shadeStroke(d, w, t, len, col, op, bleed) {
const x0 = A.x + N.x * d + E.x * t;
const y0 = A.y + N.y * d + E.y * t;
slab([
[x0, y0],
[x0 + E.x * len, y0 + E.y * len],
[x0 + E.x * len + N.x * w, y0 + E.y * len + N.y * w],
[x0 + N.x * w, y0 + N.y * w],
], col, op, bleed, 2.2, 0.02);
}
// the stretch of a band that actually falls on the paper — no pigment spent past the frame
function runStart(d) {
return max(-64, (A.x + N.x * d - 848) / 0.734);
}
function runEnd(d) {
return min((A.x + N.x * d + 52) / 0.734, (848 - A.y - N.y * d) / 0.677);
}
// what a pier throws across the paving: a slab leaning out of the shade, narrowing as it goes
function pierThrow(t, len, wid, op) {
const ox = A.x + E.x * t;
const oy = A.y + E.y * t;
const px = -THROW.y;
const py = THROW.x;
const far = 0.44 + random(-0.06, 0.06);
const pts = [
[ox + px * wid * 0.5, oy + py * wid * 0.5],
[ox + THROW.x * len + px * wid * 0.5 * far, oy + THROW.y * len + py * wid * 0.5 * far],
[ox + THROW.x * len * 1.08 - px * wid * 0.5 * far, oy + THROW.y * len * 1.08 - py * wid * 0.5 * far],
[ox - px * wid * 0.5, oy - py * wid * 0.5],
];
slab(pts, jig(BLUE, 0.07), op, 0.1, 2, 0.06);
slab(pts, jig(DEEP, 0.05), op * 0.42, 0.45, 6, 0.14);
}
// a horseshoe opening: short piers, then an arc carried just past the springing on both sides
function archPts(cx, hw, hgt, k) {
const by = baseY(cx);
const R = hw * 1.02 * k;
const cy = by - (hgt * k - R);
const pts = [[cx - hw * k * 0.9, by]];
for (let a = 196; a >= -16; a -= 8) {
pts.push([cx + cos(a) * R, cy - sin(a) * R]);
}
pts.push([cx + hw * k * 0.9, by]);
return pts;
}
function archMouth(cx, hw, hgt, op) {
slab(archPts(cx, hw, hgt, 1), jig(DEEP, 0.04), op * 0.8, 0.05, 1.3, 0.06);
slab(archPts(cx, hw, hgt, 0.97), jig(DEEP, 0.03), op, 0.04, 1, 0.06);
// the mouth is not evenly dark: it drains toward the floor of the opening
blob(cx - hw * 0.22, baseY(cx) - hgt * 0.3, hw * 0.6, jig(DEEP, 0.04), 130, 0.24);
// the reveal: the wall's own thickness, caught on the sunward jamb
slab([[cx + hw * 0.86, baseY(cx)], [cx + hw * 1.02, baseY(cx)],
[cx + hw * 1.0, baseY(cx) - hgt * 0.5], [cx + hw * 0.84, baseY(cx) - hgt * 0.54]],
jig(PALE, 0.07), 108, 0.1, 1.3, 0.04);
}
function setup() {
createCanvas(800, 800, WEBGL);
angleMode(DEGREES);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background(PAPER);
// the air of the place: cool along the left return, warm where the sun stands on the paving
air(52, 170, 120, 175, PALE, 30);
air(276, 636, 185, 140, LIME, 24);
air(618, 128, 180, 105, LIME, 26);
air(158, 430, 125, 115, PALE, 14);
// sky over the parapet — a narrow dark strip, stepped once where the coping is built up
const SKY = [
[-30, -30], [830, -30], [830, 32], [700, 35], [566, 39], [438, 42],
[396, 41], [392, 22], [300, 19], [296, 44], [180, 47], [60, 51], [-30, 55],
];
slab(SKY, jig(DEEP, 0.04), 215, 0.05, 1.1, 0.02);
slab(SKY, jig(DEEP, 0.05), 160, 0.12, 2, 0.02);
slab([[-30, -30], [430, -30], [426, 28], [-30, 38]], jig(DEEP, 0.05), 96, 0.18, 2.6, 0.02);
// what the coping throws down the whitewash, and the deeper dip under its raised step
slab([[-30, 58], [180, 50], [296, 46], [438, 44], [566, 41], [700, 37], [830, 34],
[830, 58], [566, 65], [300, 72], [-30, 82]], jig(PALE, 0.07), 116, 0.08, 1.6, 0.03);
slab([[-30, 60], [180, 53], [268, 49], [268, 70], [180, 74], [-30, 80]],
jig(BLUE, 0.06), 66, 0.26, 2.4, 0.03);
// the left return wall, turned away from the sun the whole height of the yard
slab([[-30, 50], [72, 46], [76, 150], [68, 250], [-30, 254]], jig(PALE, 0.08), 140, 0.07, 1.8, 0.03);
slab([[-30, 56], [42, 52], [38, 250], [-30, 252]], jig(BLUE, 0.06), 104, 0.2, 2.4, 0.04);
brush.noFill();
brush.set("2H", mixHex(BLUE, PAPER, 0.28), 1);
brush.line(73, 48, 69, 249);
// one plaster course and a patch of old damp, to keep the empty wall from going flat
brush.set("2H", mixHex(LIME, DEEP, 0.16), 0.7);
brush.beginShape(0.4);
brush.vertex(98, 150);
brush.vertex(302, 145);
brush.vertex(498, 141);
brush.endShape(false);
air(330, 214, 88, 44, PALE, 30);
air(196, 232, 52, 30, PALE, 22);
// ---- the ground shade: a broad pass, then band over band the way the edge runs ----
slab([[A.x, A.y], [178, 830], [830, 830], [830, A.y]], jig(BLUE, 0.04), 235, 0.05, 3, 0.02);
slab([[776, 252], [206, 826], [826, 826], [826, 258]], jig(BLUE, 0.05), 165, 0.09, 3.5, 0.02);
slab([[740, 288], [286, 820], [820, 820], [820, 330]], jig(DEEP, 0.05), 105, 0.14, 4, 0.02);
slab([[826, 452], [826, 826], [418, 826]], jig(DEEP, 0.05), 100, 0.2, 5, 0.03);
slab([[826, 592], [826, 822], [576, 822]], jig(DEEP, 0.04), 92, 0.24, 5, 0.03);
let d = 2;
for (let b = 0; b < 13; b++) {
if (d > 452) break;
const w = max(26, randomGaussian(42, 8));
const far = constrain(map(d, 20, 390, 0, 1), 0, 1);
const hi = runEnd(d + w * 0.5);
let t = runStart(d + w * 0.5) - random(0, 44);
for (let s = 0; s < 10; s++) {
if (t > hi) break;
const len = max(70, randomGaussian(128, 22));
const c = random() < 0.24 + far * 0.5 ? jig(DEEP, 0.05) : jig(BLUE, 0.06);
shadeStroke(d, w, t, min(len, hi - t + 34), c,
max(52, randomGaussian(78 + far * 42, 14)), 0.13);
t += len * (0.84 + random(0, 0.2));
}
d += w * (0.78 + random(0, 0.26));
}
// a second, sparser sweep over the far half, where the shade has nothing left to lose
let dd = 130;
for (let b = 0; b < 8; b++) {
const w = max(24, randomGaussian(38, 7));
const hi = runEnd(dd + w * 0.5);
let t = runStart(dd + w * 0.5) + random(10, 90);
for (let s = 0; s < 4; s++) {
if (t > hi) break;
const len = max(60, randomGaussian(118, 24));
shadeStroke(dd, w, t, min(len, hi - t + 26), jig(DEEP, 0.05),
max(48, randomGaussian(74, 13)), 0.24);
t += len * (1.25 + random(0, 0.5));
}
dd += w * (1.15 + random(0, 0.4));
}
// the corner the shade pools into
for (let gy = 0; gy < 5; gy++) {
for (let gx = 0; gx < 5; gx++) {
if (random() < 0.56) continue;
const x = 570 + gx * 60 + randomGaussian(0, 18);
const y = 540 + gy * 60 + randomGaussian(0, 18);
const r = max(22, randomGaussian(34, 6));
if (depth(x, y) < r * 0.9) continue;
blob(x, y, r, jig(DEEP, 0.05), max(70, randomGaussian(112, 16)), 0.24);
}
}
// the strip that rules the edge, laid last over everything so the line stays straight
for (let k = 0; k < 15; k++) {
shadeStroke(1.1, max(44, randomGaussian(62, 9)), k * 58 + random(-9, 9),
randomGaussian(86, 11),
k % 3 === 1 ? jig(DEEP, 0.05) : jig(BLUE, 0.05),
max(110, randomGaussian(152, 18)), 0.04);
}
// a low seam of shade where the wall stands on the ground
slab([[-30, 247], [200, 244], [470, 240], [740, 237], [830, 236],
[830, 250], [470, 254], [200, 258], [-30, 262]], jig(BLUE, 0.06), 134, 0.12, 1.5, 0.02);
// the arcade: three openings, none the same weight, the last half cut by the frame
archMouth(566, 52, 134, 252);
archMouth(688, 37, 100, 215);
archMouth(780, 29, 80, 165);
// two tongues of shade thrown clear of the edge by the piers, losing themselves as they go
pierThrow(196, 132, 40, 150);
pierThrow(438, 96, 28, 128);
// ---- the well head, alone in the sun on the lower-left crossing ----
// its own throw first, attached to the foot of the drum, so the stone sits on top of it
slab([[186, 600], [126, 638], [112, 664], [186, 668], [268, 646], [330, 620],
[346, 600], [268, 622]], jig(BLUE, 0.06), 162, 0.09, 2, 0.14);
slab([[196, 612], [140, 646], [156, 662], [242, 640], [300, 616]],
jig(DEEP, 0.05), 124, 0.24, 2.8, 0.2);
// the drum: whitewashed, so no more than the paper — read by its left cheek and its overhang
slab([[185, WELL.y], [181, 556], [187, 606], [226, 620], [267, 624],
[310, 619], [345, 604], [349, 552], [349, WELL.y]],
jig(LIME, 0.05), 104, 0.1, 1.8, 0.12);
slab([[184, 504], [180, 560], [187, 606], [228, 620], [244, 616], [226, 556], [224, 504]],
jig(BLUE, 0.07), 176, 0.1, 1.8, 0.12);
slab([[184, 508], [182, 562], [190, 602], [212, 612], [206, 556], [204, 506]],
jig(DEEP, 0.05), 124, 0.28, 2.4, 0.14);
// the rim overhangs: a crescent of shade thrown down the drum's front
slab([[190, 512], [232, 532], [292, 535], [346, 514], [344, 526], [290, 548],
[230, 545], [190, 526]], jig(BLUE, 0.06), 148, 0.1, 1.5, 0.5);
// the rim, an ellipse seen almost edge-on, and the mouth: the deepest note in the picture
const RIM = [];
for (let i = 0; i < 18; i++) {
const a = i * 20;
RIM.push([WELL.x + cos(a) * (WELL.hw + 5), WELL.y + sin(a) * 29 * (1 + randomGaussian(0, 0.04))]);
}
slab(RIM, jig(LIME, 0.06), 118, 0.1, 1.4, 0.9);
const MOUTH = [];
for (let i = 0; i < 16; i++) {
const a = i * 22.5;
MOUTH.push([WELL.x + 2 + cos(a) * 60, WELL.y + 1 + sin(a) * 19 * (1 + randomGaussian(0, 0.05))]);
}
slab(MOUTH, jig(DEEP, 0.04), 235, 0.04, 1, 0.9);
slab(MOUTH, jig(DEEP, 0.03), 190, 0.1, 1.5, 0.9);
slab([[214, 494], [250, 483], [300, 485], [320, 496], [286, 508], [236, 507]],
jig(DEEP, 0.03), 210, 0.08, 1.3, 0.8);
// the drawn edges of the stone: front lip, two coping joints set at uneven intervals
brush.noFill();
brush.set("HB", mixHex(LIME, DEEP, 0.62), 1.1);
brush.beginShape(0.5);
brush.vertex(186, 504);
brush.vertex(230, 526);
brush.vertex(290, 529);
brush.vertex(348, 507);
brush.endShape(false);
brush.set("2H", mixHex(LIME, DEEP, 0.42), 0.8);
brush.beginShape(0.4);
brush.vertex(247, 536);
brush.vertex(242, 580);
brush.vertex(239, 616);
brush.endShape(false);
brush.set("2H", mixHex(LIME, DEEP, 0.3), 0.7);
brush.line(330, 532, 336, 592);
// rationed terracotta: a worn course under the rim, a chip, two flecks out in the light
blob(330, 578, 13, jig(OCHRE, 0.06), 100, 0.4);
blob(302, 600, 8, jig(OCHRE, 0.06), 78, 0.4);
blob(198, 542, 6, jig(OCHRE, 0.05), 64, 0.45);
blob(408, 722, 5.5, jig(OCHRE, 0.05), 58, 0.45);
blob(132, 744, 4.5, jig(OCHRE, 0.05), 46, 0.45);
// ---- ruled seams last: the sun's straight edge, the wall foot, the paving ----
brush.noFill();
brush.set("rotring", mixHex(DEEP, BLUE, 0.35), 1);
brush.line(790, 240, 706, 318);
brush.set("pen", mixHex(DEEP, PALE, 0.4), 0.9);
brush.line(676, 348, 588, 430);
brush.set("2H", mixHex(DEEP, PALE, 0.5), 0.8);
brush.line(520, 494, 436, 572);
brush.line(372, 632, 300, 700);
brush.set("pen", mixHex(DEEP, PAPER, 0.34), 1);
brush.line(-10, 250, 206, 246);
brush.line(238, 245, 512, 241);
brush.line(552, 240, 830, 236);
brush.set("2H", mixHex(DEEP, PAPER, 0.3), 1);
brush.line(-10, 57, 188, 49);
brush.line(224, 47, 294, 45);
brush.line(398, 42, 664, 37);
// paving joints: barely there in the sun, one crossing the shade where it can be seen
brush.set("2H", mixHex(LIME, DEEP, 0.3), 0.75);
brush.beginShape(0.4);
brush.vertex(56, 396);
brush.vertex(220, 392);
brush.vertex(392, 386);
brush.endShape(false);
brush.line(88, 548, 240, 540);
brush.line(46, 672, 208, 702);
brush.set("2H", mixHex(PALE, PAPER, 0.3), 0.9);
brush.beginShape(0.4);
brush.vertex(476, 650);
brush.vertex(650, 626);
brush.vertex(816, 600);
brush.endShape(false);
// last pooling in the far corner, where a shadow always gathers
blob(806, 786, 34, jig(DEEP, 0.04), 132, 0.22);
blob(742, 750, 20, jig(DEEP, 0.04), 110, 0.26);
blob(668, 672, 13, jig(DEEP, 0.05), 88, 0.3);
blob(808, 254, 16, jig(BLUE, 0.05), 100, 0.3);
}