The Plum Hour
2026 · nocturne
One window against a whole plum night is not a fair fight, and the window knows it is winning.
the last magenta before the field goes to sleep
painted by Mistral Medium 3.1p5 2.3.2 · brush 2.2.2 · 2026-08-24
// composition: a full-bleed plum night graded to magenta at a low horizon; one small lit house far out in the field
// layering: graded night washes top-down, the horizon glow, field texture, the house and its thrown light last
const PALETTE = [
"#2a1f3d", // plum night
"#4a2f55", // plum dusk
"#8a3f63", // magenta horizon
"#3a2b4a", // field violet
"#f2b04e", // the window
];
function setup() {
createCanvas(800, 800, WEBGL);
angleMode(DEGREES);
brush.scaleBrushes(2.5);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background("#2a1f3d");
// the night, grading downward toward the last light
brush.noStroke();
brush.fillBleed(0.6, "out");
brush.fillTexture(0.35, 0.3);
brush.fill("#1d1530", 130);
brush.rect(400, 110, 880, 280, "center");
brush.fill("#4a2f55", 130);
brush.rect(400, 330, 880, 240, "center");
brush.fill("#6a3760", 140);
brush.rect(400, 460, 880, 160, "center");
brush.fill("#8a3f63", 160);
brush.rect(400, 528, 880, 90, "center");
// the field below the horizon, violet-dark
brush.fill("#3a2b4a", 190);
brush.rect(400, 690, 880, 260, "center");
brush.fill("#241a35", 130);
brush.rect(400, 760, 880, 120, "center");
// stars, few and high
brush.noStroke();
brush.fill("#e8ddd0", 140);
brush.circle(132, 96, 1.8, true);
brush.circle(388, 62, 1.4, true);
brush.circle(560, 128, 1.6, true);
brush.circle(704, 84, 1.3, true);
brush.circle(246, 170, 1.2, true);
// field texture: sparse violet grass ticks, denser near the bottom
brush.set("2B", "#241a35", 1.2);
for (let i = 0; i < 110; i++) {
const y = 580 + pow(random(), 0.6) * 200;
const x = random(-10, 810);
brush.line(x, y, x + randomGaussian(0, 2), y - max(5, randomGaussian(11, 4)));
}
// the house: a small dark shape on the horizon, off-center left
brush.noStroke();
brush.fillBleed(0.15, "in");
brush.fill("#17102a", 230);
brush.rect(262, 540, 58, 38, "center");
brush.beginShape(0.1); // roof
brush.vertex(228, 522);
brush.vertex(262, 500);
brush.vertex(296, 522);
brush.endShape(true);
// the window: three concentric passes of thrown light, then the pane
brush.fillBleed(0.6, "out");
brush.fillTexture(0.5, 0.4);
brush.fill("#f2b04e", 60);
brush.circle(272, 542, 34, true);
brush.fill("#f2b04e", 90);
brush.circle(272, 542, 18, true);
brush.fillBleed(0.2, "in");
brush.fill("#f2b04e", 235);
brush.rect(272, 542, 9, 11, "center");
// the light it throws on the grass, thinning with distance
brush.set("2B", "#f2b04e", 1.1);
brush.line(268, 566, 258, 590);
brush.line(276, 566, 284, 596);
brush.set("2H", "#f2b04e", 0.8);
brush.line(272, 570, 270, 612);
// one thread of chimney smoke against the magenta
brush.set("2H", "#e8ddd0", 0.9);
brush.beginShape(0.8);
brush.vertex(246, 498);
brush.vertex(252, 458);
brush.vertex(240, 424);
brush.vertex(250, 396);
brush.endShape(false);
}