a pinned specimen moth, wings spread, dusty browns with one blue eye-spot
painted by GPT-6 Astrap5 2.3.2 · brush 2.2.2 · 2026-08-24
// composition: a specimen moth pinned high on the sheet, wings spread, a soft shadow under it; the lower third stays bare paper
// layering: paper breath, close shadow, wing washes, branching veins and dusty scales, one blue ocellus, fur and the pin last
const PALETTE = [
"#f1ecdf", // cream paper
"#c9b592", // pale wing wash
"#96795a", // dusty brown
"#54432f", // deep brown ink
"#4e6a94", // eye-spot blue, the only accent
"#a49c8c", // shadow grey
];
const BX = 396;
const BY = 316;
function hindwing(s, jit) {
brush.noStroke();
brush.fillBleed(0.42, "out");
brush.fillTexture(0.55, 0.55);
brush.fill("#c9b592", 130);
brush.beginShape(0.55);
brush.vertex(BX + s * 8, BY + 2);
brush.vertex(BX + s * (72 + random(-4, 4)), BY + 16 + jit);
brush.vertex(BX + s * (98 + random(-4, 4)), BY + 52 + jit);
brush.vertex(BX + s * 58, BY + (88 + random(-4, 4)));
brush.vertex(BX + s * 14, BY + 62);
brush.endShape(true);
brush.fill("#96795a", 125);
brush.beginShape(0.55);
brush.vertex(BX + s * 20, BY + 22);
brush.vertex(BX + s * (78 + random(-4, 4)), BY + 34 + jit);
brush.vertex(BX + s * 74, BY + (66 + random(-5, 5)));
brush.vertex(BX + s * 28, BY + 58);
brush.endShape(true);
// deepening pass near the body so the hindwing roots hold weight
brush.fill("#96795a", 150);
brush.beginShape(0.55);
brush.vertex(BX + s * 12, BY + 8);
brush.vertex(BX + s * (42 + random(-3, 3)), BY + 20 + jit);
brush.vertex(BX + s * 40, BY + (52 + random(-4, 4)));
brush.vertex(BX + s * 16, BY + 50);
brush.endShape(true);
}
function forewing(s, lift, es) {
const ax = BX + s * (158 + random(-6, 6));
const ay = BY - (66 + lift + random(-4, 4));
brush.noStroke();
brush.fillBleed(0.4, "out");
brush.fillTexture(0.5, 0.55);
// base wash
brush.fill("#c9b592", 140);
brush.beginShape(0.5);
brush.vertex(BX + s * 10, BY - 26);
brush.vertex(BX + s * 76, BY - (56 + lift));
brush.vertex(ax, ay);
brush.vertex(BX + s * (146 + random(-5, 5)), BY + 6 + random(-3, 3));
brush.vertex(BX + s * 52, BY + 20);
brush.endShape(true);
// dusty mid wash toward the outer edge
brush.fill("#96795a", 145);
brush.beginShape(0.5);
brush.vertex(BX + s * 36, BY - (18 + lift * 0.4));
brush.vertex(BX + s * 90, BY - (46 + lift));
brush.vertex(ax - s * 8, ay + 9);
brush.vertex(BX + s * 132, BY + 4);
brush.vertex(BX + s * 50, BY + 14);
brush.endShape(true);
// deepening pass at the wing root, nearest the body
brush.fill("#96795a", 165);
brush.beginShape(0.5);
brush.vertex(BX + s * 12, BY - 22);
brush.vertex(BX + s * (52 + random(-4, 4)), BY - (34 + lift * 0.5));
brush.vertex(BX + s * 60, BY + 8);
brush.vertex(BX + s * 44, BY + 16);
brush.endShape(true);
// two darker bands crossing the wing
brush.fill("#54432f", 155);
brush.beginShape(0.4);
brush.vertex(BX + s * (64 + random(-4, 4)), BY - (40 + lift * 0.6));
brush.vertex(BX + s * 82, BY - (46 + lift * 0.7));
brush.vertex(BX + s * (108 + random(-4, 4)), BY + 4);
brush.vertex(BX + s * 92, BY + 9);
brush.endShape(true);
brush.fill("#54432f", 140);
brush.beginShape(0.4);
brush.vertex(BX + s * (112 + random(-4, 4)), BY - (52 + lift * 0.8));
brush.vertex(BX + s * 126, BY - (55 + lift * 0.85));
brush.vertex(BX + s * (142 + random(-4, 4)), BY + 1);
brush.vertex(BX + s * 128, BY + 5);
brush.endShape(true);
// Fine radial veins unite the washes into a single folded membrane.
brush.noFill();
const rays = [[73, -51], [110, -59], [148, -61], [143, -24], [131, 3], [94, 12]];
for (let i = 0; i < rays.length; i++) {
const [u, v] = rays[i];
brush.set(i < 3 ? "pen" : "cpencil", "#54432f", i < 3 ? 0.72 : 1.05);
brush.beginShape(0.32);
brush.vertex(BX + s * 17, BY - 19, 0.85);
brush.vertex(BX + s * (u * 0.54), BY + v * 0.48 - lift * 0.5, 0.65);
brush.vertex(BX + s * u, BY + v - lift * 0.7, 0.25);
brush.endShape(false);
}
// Short scales follow a narrowing wing section, with interrupted density.
for (let i = 0; i < 115; i++) {
const u = constrain(randomGaussian(88, 36), 25, 143);
const top = -23 - u * 0.27 - lift * 0.65;
const bottom = 18 - u * 0.09;
const v = constrain(randomGaussian((top + bottom) * 0.5, 15), top + 3, bottom - 3);
if (random() < 0.2) continue;
brush.set("cpencil", i % 4 === 0 ? "#f1ecdf" : "#96795a", constrain(randomGaussian(1.1, 0.2), 0.65, 1.5));
const x = BX + s * u, y = BY + v;
brush.line(x, y, x + s * constrain(randomGaussian(3.4, 1.1), 1.4, 6), y + 1.8);
}
brush.noFill();
brush.set("2B", "#96795a", 0.85);
brush.beginShape(0.25);
brush.vertex(BX + s * 80, BY - 56 - lift, 0.2);
brush.vertex(ax, ay, 0.7);
brush.vertex(BX + s * 145, BY - 18, 0.3);
brush.endShape(false);
// One blue eye; its opposite is an older, muted ochre marking.
brush.noStroke();
brush.fillBleed(0.2, "out");
const ex = BX + s * (100 + random(-3, 3));
const ey = BY - (24 + lift * 0.4) + random(-3, 3);
brush.fill("#54432f", 200);
brush.circle(ex, ey, es, true);
brush.fill(s < 0 ? "#4e6a94" : "#c9b592", 225);
brush.circle(ex + s, ey - 1, es * 0.55, true);
brush.fill("#c9b592", 150);
brush.circle(ex - es * 0.2, ey - es * 0.28, es * 0.2, true);
}
function setup() {
createCanvas(800, 800, WEBGL);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background("#f1ecdf");
brush.noStroke();
brush.fillBleed(0.7, "out");
brush.fillTexture(0.35, 0.4);
brush.fill("#c9b592", 14);
brush.rect(70, 86, 658, 470);
// the shadow the specimen throws on the sheet
brush.noStroke();
brush.fillBleed(0.7, "out");
brush.fillTexture(0.35, 0.4);
brush.fill("#a49c8c", 25);
brush.beginShape(0.7);
brush.vertex(BX - 138, BY - 20);
brush.vertex(BX + 22, BY - 44);
brush.vertex(BX + 176, BY - 12);
brush.vertex(BX + 122, BY + 78);
brush.vertex(BX + 6, BY + 104);
brush.vertex(BX - 108, BY + 66);
brush.endShape(true);
brush.fill("#a49c8c", 18);
brush.circle(BX + 26, BY + 40, 96, true);
// hindwings first, tucked behind
hindwing(-1, 3);
hindwing(1, -2);
// forewings — the pair does not quite match
forewing(-1, 5, 16.5);
forewing(1, 0, 12.5);
// body: thorax and tapering abdomen
brush.noStroke();
brush.fillBleed(0.35, "out");
brush.fillTexture(0.65, 0.6);
brush.fill("#54432f", 150);
brush.beginShape(0.5);
brush.vertex(BX - 9, BY - 28);
brush.vertex(BX + 9, BY - 26);
brush.vertex(BX + 7, BY + 24);
brush.vertex(BX + 2, BY + 48);
brush.vertex(BX - 5, BY + 26);
brush.endShape(true);
brush.fill("#96795a", 110);
brush.beginShape(0.5);
brush.vertex(BX - 7, BY - 26);
brush.vertex(BX + 7, BY - 24);
brush.vertex(BX + 4, BY - 2);
brush.vertex(BX - 5, BY - 2);
brush.endShape(true);
brush.fill("#54432f", 172);
brush.circle(BX + 1, BY - 33, 7, true);
// Segmented abdomen and a dense, softly feathered thorax.
brush.noFill();
for (let i = 0; i < 8; i++) {
const y = BY + i * 5;
const r = 6.5 - i * 0.55;
brush.set("pen", "#54432f", 0.85);
brush.beginShape(0.4);
brush.vertex(BX - r, y, 0.3);
brush.vertex(BX, y + 2, 0.8);
brush.vertex(BX + r, y, 0.3);
brush.endShape(false);
}
for (let i = 0; i < 42; i++) {
const x = BX + constrain(randomGaussian(0, 5), -9, 9);
const y = BY + constrain(randomGaussian(-16, 8), -29, -1);
brush.set("2H", i % 3 === 0 ? "#c9b592" : "#54432f", 0.65);
brush.line(x, y, x + (x - BX) * 0.38, y + 4);
}
// fur
brush.set("charcoal", "#54432f", 0.8);
brush.line(BX - 8, BY - 18, BX - 15, BY - 22);
brush.line(BX + 8, BY - 14, BX + 16, BY - 18);
brush.line(BX - 7, BY - 4, BX - 13, BY - 5);
brush.line(BX + 7, BY + 2, BX + 14, BY + 2);
brush.line(BX - 5, BY + 14, BX - 11, BY + 17);
brush.line(BX + 5, BY + 20, BX + 10, BY + 24);
brush.line(BX + 1, BY + 46, BX + 3, BY + 54);
// antennae — feathered, and not twins
brush.noFill();
brush.set("pen", "#54432f", 0.9);
brush.beginShape(0.5);
brush.vertex(BX - 3, BY - 36);
brush.vertex(BX - 24, BY - 62);
brush.vertex(BX - 34, BY - 90);
brush.endShape(false);
brush.beginShape(0.5);
brush.vertex(BX + 5, BY - 36);
brush.vertex(BX + 30, BY - 68);
brush.vertex(BX + 36, BY - 96);
brush.endShape(false);
brush.set("2H", "#54432f", 0.6);
brush.line(BX - 20, BY - 58, BX - 26, BY - 54);
brush.line(BX - 27, BY - 70, BX - 33, BY - 66);
brush.line(BX - 31, BY - 82, BX - 37, BY - 79);
brush.line(BX + 25, BY - 62, BX + 31, BY - 58);
brush.line(BX + 32, BY - 78, BX + 38, BY - 75);
brush.line(BX + 35, BY - 90, BX + 41, BY - 88);
// the pin
brush.set("rotring", "#54432f", 1);
brush.line(BX + 3, BY - 12, BX + 8, BY - 58);
brush.noStroke();
brush.fill("#a49c8c", 145);
brush.circle(BX + 9, BY - 61, 4.5, true);
}