Istanbul
2026 · city
I stood off-axis on the water so the south flank runs the width of the sheet and the cascade reads: the broad shallow dome on its ring of forty windows, two great semi-domes, the exedrae stepping down, the buttresses cutting across rose render. Cypresses and a ferry hold the foot.
hagia sophia from the bosphorus, its shallow lead dome cascading into semi-domes between four minarets
painted by Claude Opus 5p5 2.3.2 · brush 2.2.2 · 2026-08-30
// composition: a letterpress vignette of Hagia Sophia seen from the Bosphorus, slightly off-axis to
// the south-east so the long south flank runs across the sheet and the east apse turns away. The
// broad shallow dome sits just left of the print's centre and the cascade falls away from it in the
// silhouette — dome, two great semi-domes, two exedra semi-domes, the low aisle roof — while the
// four minarets stand clear at the corners, the two western ones thicker and shorter, the two
// eastern slender pencils. Below: the garden wall, six cypresses, a sliver of ruled Bosphorus with
// a ferry. No sky. The print runs x 140–662, y 294–644 — wide and low; the cascade is the point.
//
// four non-negotiable facts:
// proportion — the dome is very broad and very shallow: it spans about 3/8 of the building's
// length and rises only half its own radius; the flank wall under it is a low two-storey band,
// so the dome and its half-domes own most of the height.
// ordering — nothing rises above the dome but the minarets, and the mass steps DOWN and OUT
// from it: dome on its window drum → a great semi-dome springing from each great arch and
// descending outward → smaller exedra semi-domes descending again → the aisle roof. The four
// great buttresses stand in FRONT of the flank and cut across it.
// material — warm rose-red lime render over brick, lead-grey sheeting on every dome and minaret
// cap, pale stone for the buttress masses and the minaret shafts. Never white marble.
// site — it stands low on the first hill over the water, much wider than it is tall, with
// the garden wall and cypresses at its foot and the strait immediately below.
// counts drawn: 20 of the drum's 40 windows; 12 tympanum windows (7 lower, 5 upper); 2 great
// semi-domes; 2 exedra semi-domes; 6 semi-dome lunette windows; 4 great buttresses; 4 minarets,
// one balcony ring and one conical cap each; 13 gallery windows; 9 ground windows; 6 cypresses.
// layering: paper flecks, the two rear minarets, the exedrae, the great semi-domes, the tympanum,
// the drum and dome, the long rose flank in front of them, the stone buttresses over that, the
// two near minarets, the garden wall and cypresses, the strait, the last darks, one gilt alem.
const PALETTE = [
"#efe6d2", // paper — warm cream sheet
"#c98f74", // rose — the warm red lime render of the flanks
"#2f3a44", // ink — slate-navy line and shadow plate
"#5d7f88", // lead — the domes, the minaret caps, the strait
"#d9cfb6", // stone — buttress masses, minaret shafts, the garden wall
"#a8452c", // brick — the over-plate where the render turns into shade
"#7a8c6a", // moss — the cypresses along the garden wall
"#c9a24a", // gilt — the alem on the dome, the one warm accent
];
const BASE = 596; // the building's foot / top of the garden terrace
const AISLE = 524; // aisle roof line — the flank wall is only this deep
const TYMP = 446; // the drum cornice, and the crown of the two great semi-domes
const DRUM_TOP = 406; // the dome springs here
const DX = 388; // the dome's centre — a little left of the print's centre
const DRX = 66; // half the drum's width
const g = (m, sd, lo, hi) => constrain(randomGaussian(m, sd), lo, hi);
const shift = (pts, dx, dy) => pts.map((p) => [p[0] + dx, p[1] + dy]);
const box = (x, y, w, h) => [[x, y], [x + w, y], [x + w, y + h], [x, y + h]];
// a solid grainy plate — the letterpress solid. noStroke: a plate never carries the last pen's outline.
const plate = (pts, col, w, dist, ang, rnd) => {
brush.noStroke();
brush.noFill();
brush.hatchStyle("charcoal", col, w);
brush.hatch(dist, ang, { rand: rnd, continuous: false, gradient: 0 });
brush.polygon(pts);
brush.noHatch();
};
// a half-tone plate — two crossed cpencil passes, paper breathing through
const halfPlate = (pts, col, w, dist, ang) => {
brush.noStroke();
brush.noFill();
brush.hatchStyle("cpencil", col, w);
brush.hatch(dist, ang, { rand: 0.25, continuous: false, gradient: 0 });
brush.polygon(pts);
brush.hatch(dist * 1.12, ang - 84, { rand: 0.25, continuous: false, gradient: 0 });
brush.polygon(pts);
brush.noHatch();
};
// a fading plate — gradient hatch, dense at one edge, open at the other
const fadePlate = (pts, col, w, dist, ang, grad) => {
brush.noStroke();
brush.noFill();
brush.hatchStyle("charcoal", col, w);
brush.hatch(dist, ang, { rand: 0.3, continuous: false, gradient: grad });
brush.polygon(pts);
brush.noHatch();
};
const ink = (x1, y1, x2, y2, w, name, col) => {
brush.noFill();
brush.set(name || "rotring", col || PALETTE[2], w || 1.0);
brush.line(x1, y1, x2, y2);
};
// a dark window / arch slot
const slot = (x, y, w, h) => plate(box(x, y, w, h), PALETTE[2], 1.1, 1.5, 90, 0.2);
// the band under a slice of a dome's curve — `from`/`to` in units of PI (1 = left springing,
// 1.5 = crown, 2 = right springing), closed down to the springing line so it is a solid mass
const arcBand = (cx, base, rx, ry, from, to) => {
const pts = [];
for (let i = 0; i <= 8; i++) {
const a = Math.PI * (from + (i / 8) * (to - from));
pts.push([cx + Math.cos(a) * rx, base + Math.sin(a) * ry]);
}
pts.push([cx + Math.cos(Math.PI * to) * rx, base]);
pts.push([cx + Math.cos(Math.PI * from) * rx, base]);
return pts;
};
// the ruled curve over a slice of dome, drawn as short segments
const arcRule = (cx, base, rx, ry, from, to, w, name, col) => {
for (let i = 0; i < 14; i++) {
const a1 = Math.PI * (from + (i / 14) * (to - from));
const a2 = Math.PI * (from + ((i + 1) / 14) * (to - from));
ink(cx + Math.cos(a1) * rx, base + Math.sin(a1) * ry,
cx + Math.cos(a2) * rx, base + Math.sin(a2) * ry, w, name, col);
}
};
// a lead shell: the half toward the light in half-tone, the half turning away as a solid plate,
// with an ink over-plate misregistered over its outer edge
const leadShell = (cx, base, rx, ry, wt, from, to) => {
const mid = (from + to) / 2;
halfPlate(arcBand(cx, base, rx, ry, from, mid), PALETTE[3], wt, 2.1, 64);
plate(arcBand(cx, base, rx, ry, mid, to), PALETTE[3], wt + 0.1, 2.3, 22, 0.3);
plate(shift(arcBand(cx, base, rx, ry, mid + (to - mid) * 0.42, to), 1.5, 1), PALETTE[2], 1.0, 3.6, 28, 0.3);
};
// a great buttress: a tapering stone pier standing in front of the flank, stepped twice
const buttressAt = (cx, halfLo, halfHi, topY) => {
const L = [[cx - halfLo, BASE], [cx, BASE], [cx, topY], [cx - halfHi, topY]];
const R = [[cx, BASE], [cx + halfLo, BASE], [cx + halfHi, topY], [cx, topY]];
halfPlate(L, PALETTE[4], 1.35, 2.1, 68);
plate(R, PALETTE[4], 1.45, 2.2, 90, 0.3);
plate(shift([[cx + halfLo * 0.4, BASE], [cx + halfLo, BASE], [cx + halfHi, topY],
[cx + halfHi * 0.4, topY]], 1.5, 1), PALETTE[2], 1.15, 2.9, 92, 0.3);
// the shadow the pier throws onto the wall beside it
fadePlate([[cx + halfLo, BASE], [cx + halfLo + 13, BASE], [cx + halfHi + 11, topY + 6],
[cx + halfHi, topY + 6]], PALETTE[2], 1.0, 3.4, 90, 0.75);
ink(cx - halfLo, BASE, cx - halfHi, topY, 1.0);
ink(cx + halfLo, BASE, cx + halfHi, topY, 1.3);
const t1 = (BASE - 528) / (BASE - topY);
ink(cx - lerp(halfLo, halfHi, t1) - 2, 528, cx + lerp(halfLo, halfHi, t1) + 2, 528, 1.05);
const t2 = (BASE - 486) / (BASE - topY);
if (topY < 486) ink(cx - lerp(halfLo, halfHi, t2), 486, cx + lerp(halfLo, halfHi, t2), 486, 0.8, "pen");
ink(cx - halfHi - 2.5, topY, cx + halfHi + 2.5, topY, 1.25);
};
// a minaret: stone shaft, one balcony ring, a lead conical cap and a finial
const minaretAt = (x, tip, footY, hw, capH) => {
const yt = tip + capH; // where the cone meets the shaft
const lit = box(x - hw, yt, hw * 0.95, footY - yt);
const shd = box(x - hw * 0.05, yt, hw * 1.05, footY - yt);
halfPlate(lit, PALETTE[4], 1.3, 2.1, 72);
plate(shd, PALETTE[4], 1.4, 2.3, 90, 0.3);
plate(shift(box(x + hw * 0.4, yt, hw * 0.6, footY - yt), 1.2, 1), PALETTE[2], 1.0, 3.4, 92, 0.3);
ink(x - hw, yt + 2, x - hw, footY, 1.0);
ink(x + hw, yt + 2, x + hw, footY, 1.3);
ink(x - hw * 0.32, yt + 6, x - hw * 0.32, footY - 4, 0.7, "pen");
// the şerefe — a ring corbelled out of the shaft, dark under its lip
const by = yt + (footY - yt) * 0.3;
plate(box(x - hw * 1.75, by, hw * 3.5, 5), PALETTE[2], 1.15, 1.6, 0, 0.2);
ink(x - hw * 1.95, by + 5.6, x + hw * 1.95, by + 5.6, 1.15);
ink(x - hw * 1.9, by - 0.8, x + hw * 1.9, by - 0.8, 0.8, "pen");
// the külah — a lead cone
const cone = [[x, yt], [x + hw * 1.2, yt], [x, tip]];
halfPlate([[x - hw * 1.2, yt], [x, yt], [x, tip]], PALETTE[3], 1.2, 2.0, 74);
plate(cone, PALETTE[3], 1.4, 2.1, 104, 0.3);
plate(shift(cone, 1, 1), PALETTE[2], 1.0, 3.6, 108, 0.3);
ink(x - hw * 1.2, yt, x, tip, 0.9);
ink(x + hw * 1.2, yt, x, tip, 0.9);
ink(x, tip, x + g(0, 0.4, -1, 1), tip - 8, 1.6, "charcoal");
};
// a cypress — a dark spindle with a darker shaded half
const cypressAt = (x, y, h) => {
const pts = [[x - 5.6, y], [x - 4.2, y - h * 0.34], [x - 2.4, y - h * 0.72], [x, y - h],
[x + 2.4, y - h * 0.72], [x + 4.2, y - h * 0.34], [x + 5.6, y]];
plate(pts, PALETTE[6], 1.5, 1.7, 44, 0.3);
plate(shift(pts.slice(3).concat([[x, y]]), 1, 1), PALETTE[2], 1.3, 2.4, 50, 0.35);
ink(x, y, x + g(0, 0.5, -1, 1), y + 5, 1.6, "charcoal");
};
function setup() {
createCanvas(800, 800, WEBGL);
angleMode(DEGREES);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background(PALETTE[0]);
// 0 — paper flecks: faint warm specks so the sheet is not dead, never dark
for (let i = 0; i < 220; i++) {
const x = random(10, 790), y = random(10, 790);
ink(x, y, x + g(1.4, 0.5, 0.5, 2.6), y + g(0, 0.4, -1, 1), g(0.45, 0.1, 0.3, 0.6), "2H",
random() < 0.7 ? "#bfb39a" : PALETTE[1]);
}
// 1 — the two rear minarets, drawn first so the mass buries their feet
minaretAt(276, 352, 520, 8.8, 24); // north-west — thick and short
minaretAt(500, 304, 516, 4.9, 31); // north-east — a slender pencil
// 2 — the outermost step of the cascade: an exedra semi-dome at each end, descending outward
plate(box(226, 492, 34, AISLE - 492), PALETTE[1], 1.45, 2.3, 88, 0.3);
leadShell(260, 512, 36, 20, 1.15, 1, 1.5);
arcRule(260, 512, 36, 20, 1, 1.5, 0.9, "pen");
plate(box(516, 492, 36, AISLE - 492), PALETTE[1], 1.5, 2.3, 86, 0.3);
plate(shift(box(536, 492, 16, AISLE - 492), 1.15, 1), PALETTE[5], 1.1, 3.8, 94, 0.3);
plate(shift(box(528, 492, 24, AISLE - 492), 1.5, 1), PALETTE[2], 1.0, 3.9, 86, 0.3);
leadShell(516, 512, 36, 20, 1.15, 1.5, 2);
arcRule(516, 512, 36, 20, 1.5, 2, 0.9, "pen");
// 3 — the great semi-domes: each springs from a great arch at the dome's edge and falls outward
halfPlate(box(262, 478, 60, AISLE - 478), PALETTE[1], 1.35, 2.2, 73);
leadShell(322, 478, 62, 32, 1.35, 1, 1.5);
arcRule(322, 478, 62, 32, 1, 1.5, 1.15);
plate(box(454, 478, 62, AISLE - 478), PALETTE[1], 1.5, 2.3, 90, 0.3);
plate(shift(box(494, 478, 22, AISLE - 478), 1.5, 1), PALETTE[5], 1.15, 3.8, 94, 0.3);
plate(shift(box(480, 478, 36, AISLE - 478), 1.5, 1), PALETTE[2], 1.05, 3.8, 86, 0.3);
leadShell(454, 478, 62, 32, 1.35, 1.5, 2);
arcRule(454, 478, 62, 32, 1.5, 2, 1.15);
// six lunette windows in the walls the semi-domes stand on
for (let i = 0; i < 3; i++) slot(266 + i * 14 + g(0, 0.3, -0.8, 0.8), 490, 4.5, 17);
for (let i = 0; i < 3; i++) slot(488 + i * 14 + g(0, 0.3, -0.8, 0.8), 490, 4.5, 17);
ink(258, 478, 326, 478, 0.85, "pen");
ink(450, 478, 520, 478, 0.85, "pen");
// 4 — the great south arch and the tympanum recessed under it, twelve windows in it
const ACX = 388, ACY = 522, ARX = 62, ARY = 76; // the stilted arch: its crown is the drum cornice
halfPlate(arcBand(ACX, ACY, ARX, ARY, 1, 1.5), PALETTE[1], 1.3, 2.4, 76);
plate(arcBand(ACX, ACY, ARX, ARY, 1.5, 2), PALETTE[1], 1.45, 2.4, 88, 0.3);
plate(shift(arcBand(ACX, ACY, ARX, ARY, 1.72, 2), 1.5, 1), PALETTE[5], 1.15, 3.6, 94, 0.3);
fadePlate(arcBand(ACX, ACY, ARX, ARY, 1, 2), PALETTE[2], 1.0, 3.6, 90, 0.5); // the arch's own shade
arcRule(ACX, ACY, ARX, ARY, 1, 2, 1.25); // the arch ring
arcRule(ACX, ACY - 2, ARX - 5, ARY - 5, 1, 2, 0.75, "pen"); // its inner order, misregistered
for (let i = 0; i < 7; i++) slot(340 + i * 16 + g(0, 0.35, -1, 1), 486, 6, 30); // lower row
for (let i = 0; i < 5; i++) slot(361 + i * 12 + g(0, 0.3, -0.8, 0.8), 458, 5, 22); // upper row
// 5 — the drum, ringed with windows, and the great shallow dome over it
halfPlate(box(DX - DRX, DRUM_TOP, DRX * 0.95, TYMP - DRUM_TOP), PALETTE[4], 1.3, 2.2, 70);
plate(box(DX - 4, DRUM_TOP, DRX + 4, TYMP - DRUM_TOP), PALETTE[4], 1.4, 2.4, 90, 0.3);
plate(shift(box(DX + 26, DRUM_TOP, DRX - 26, TYMP - DRUM_TOP), 1.5, 1), PALETTE[2], 1.0, 3.6, 92, 0.3);
// 20 of the drum's 40 windows — crowding toward the ends because the drum is round
for (let i = 0; i < 20; i++) {
const a = (-78 + (i / 19) * 156) * Math.PI / 180;
const x = DX + Math.sin(a) * (DRX - 4.5);
slot(x - 1.5 + g(0, 0.25, -0.7, 0.7), 413, 3.1, 24);
}
ink(DX - DRX - 7, TYMP, DX + DRX + 7, TYMP, 1.45); // the cornice the drum stands on
ink(DX - DRX - 4, DRUM_TOP + 1, DX + DRX + 4, DRUM_TOP + 1, 1.3); // the dome's own cornice
fadePlate(box(DX - DRX, DRUM_TOP + 2, DRX * 2, 7), PALETTE[2], 0.95, 3.4, 0, 0.6);
// the dome itself — shallow: it rises 36 over a half-span of 72
leadShell(DX, DRUM_TOP, 76, 34, 1.5, 1, 2);
arcRule(DX, DRUM_TOP, 76, 34, 1, 2, 1.45);
arcRule(DX, DRUM_TOP - 1.5, 74, 32.5, 1, 2, 0.8, "pen");
// seven of the forty ribs, faint on the lead
for (let i = 0; i < 7; i++) {
const a = Math.PI + ((i + 0.5) / 7) * Math.PI;
ink(DX + Math.cos(a) * 72, DRUM_TOP + Math.sin(a) * 32,
DX + Math.cos(a) * 25, DRUM_TOP + Math.sin(a) * 11, 0.7, "pen", PALETTE[2]);
}
// 6 — the long low flank in front of everything: two storeys of rose render, counted in bays
halfPlate(box(214, AISLE, 176, BASE - AISLE), PALETTE[1], 1.35, 2.3, 71);
plate(box(384, AISLE, 186, BASE - AISLE), PALETTE[1], 1.5, 2.3, 90, 0.3);
plate(shift(box(496, AISLE + 1, 74, BASE - AISLE - 1), 1.5, 1), PALETTE[5], 1.15, 3.8, 94, 0.3);
plate(shift(box(468, AISLE + 1, 102, BASE - AISLE - 1), 1.5, 1), PALETTE[2], 1.05, 3.8, 86, 0.3);
fadePlate(box(214, AISLE + 1, 356, 10), PALETTE[2], 0.95, 3.6, 0, 0.7); // shadow under the eaves
ink(210, AISLE, 574, AISLE, 1.45); // the aisle-roof cornice
ink(214, 560, 570, 560, 0.95); // the string course between the storeys
const galleryBays = [[249, 3, 18], [336, 7, 16.6], [490, 3, 18]];
for (let b = 0; b < galleryBays.length; b++) {
const bay = galleryBays[b];
for (let i = 0; i < bay[1]; i++) slot(bay[0] + i * bay[2] + g(0, 0.35, -1, 1), 532, 7, 24);
}
const groundBays = [[252, 2, 24], [342, 5, 24], [492, 2, 24]];
for (let b = 0; b < groundBays.length; b++) {
const bay = groundBays[b];
for (let i = 0; i < bay[1]; i++) slot(bay[0] + i * bay[2] + g(0, 0.4, -1, 1), 570, 9, 20);
}
// the east apse, turning away off-axis
plate([[570, 528], [592, 536], [596, BASE], [570, BASE]], PALETTE[1], 1.45, 2.3, 86, 0.3);
plate(shift([[582, 531], [592, 536], [596, BASE], [582, BASE]], 1.5, 1), PALETTE[5], 1.2, 3.3, 92, 0.3);
leadShell(582, 531, 15, 9, 1.1, 1, 2);
arcRule(582, 531, 15, 9, 1, 2, 0.8, "pen");
for (let i = 0; i < 2; i++) slot(575 + i * 10, 550, 4.5, 17);
ink(570, 528, 570, BASE, 0.95);
// 7 — the four great buttresses, standing in front of the flank and cutting it
buttressAt(226, 19, 13, 498);
buttressAt(310, 19, 14, 470);
buttressAt(466, 19, 14, 470);
buttressAt(552, 19, 13, 498);
// 8 — the two near minarets, in front of everything
minaretAt(194, 358, BASE, 9.6, 25); // south-west — thick and short
minaretAt(584, 296, BASE, 5.3, 33); // south-east — the tallest, a slender pencil
// 9 — the garden wall, the cypresses, a few figures for scale
plate(box(160, BASE - 2, 490, 5), PALETTE[4], 1.15, 1.6, 0, 0.2);
ink(158, BASE + 3, 652, BASE + 3, 1.3);
for (let i = 0; i < 27; i++) ink(166 + i * 18.2, BASE - 2, 166 + i * 18.2, BASE - 9, 0.7, "pen");
cypressAt(172, BASE - 1, 60);
cypressAt(192, BASE + 1, 44);
cypressAt(608, BASE - 1, 52);
cypressAt(628, BASE + 1, 64);
cypressAt(646, BASE, 42);
cypressAt(150, BASE + 1, 36);
ink(354, BASE + 2, 354, BASE - 6, 1.6, "charcoal");
ink(359, BASE + 2, 359, BASE - 5, 1.4, "charcoal");
ink(430, BASE + 2, 430, BASE - 6, 1.6, "charcoal");
// 10 — the strait: a ruled plate thinning downward, broken dashes, the dome's reflection
fadePlate([[152, BASE + 8], [652, BASE + 8], [664, BASE + 46], [140, BASE + 46]], PALETTE[3], 1.2, 3.8, 0, 0.75);
brush.noStroke(); brush.noFill();
brush.hatchStyle("cpencil", PALETTE[3], 1.3);
brush.hatch(4.2, 0, { rand: 0.12, continuous: false, gradient: 0.5 });
brush.polygon([[176, BASE + 8], [634, BASE + 8], [640, BASE + 28], [170, BASE + 28]]);
brush.noHatch();
for (let i = 0; i < 46; i++) {
const y = BASE + 10 + i * 0.78 + g(0, 0.5, -1, 1);
const x0 = 136 + random(0, 340);
const len = constrain(randomGaussian(120, 62), 16, 340);
if (x0 + len > 706) continue;
ink(x0, y, x0 + len, y, g(1.25, 0.3, 0.7, 1.9), random() < 0.65 ? "2B" : "cpencil",
random() < 0.78 ? PALETTE[3] : PALETTE[2]);
}
for (let i = 0; i < 14; i++) {
const y = BASE + 9 + i * 2.4;
ink(DX - 38 + g(0, 3, -6, 6), y, DX + 38 - g(0, 3, -6, 6), y, g(1.1, 0.25, 0.7, 1.6), "2B", PALETTE[4]);
}
// a ferry running east, black — a hull dash, a nick for the bow, a stack
ink(448, BASE + 33, 508, BASE + 33, 3.0, "charcoal");
ink(508, BASE + 33, 516, BASE + 28, 1.5, "charcoal");
ink(478, BASE + 30, 478, BASE + 20, 1.9, "charcoal");
ink(460, BASE + 29, 500, BASE + 29, 1.5, "charcoal");
ink(446, BASE + 33, 441, BASE + 29, 1.3, "charcoal");
ink(400, BASE + 36, 442, BASE + 35, 1.1, "2B", PALETTE[4]);
// 11 — the last darks: crisp ends on the masses, the deep shade at the flank's east end
ink(214, AISLE, 214, BASE, 1.0);
ink(570, AISLE, 570, BASE, 1.1);
ink(326, TYMP, 326, AISLE, 0.9);
ink(450, TYMP, 450, AISLE, 1.2);
fadePlate(box(500, BASE - 34, 70, 34), PALETTE[2], 1.0, 3.4, 90, 0.7);
fadePlate(box(210, BASE - 24, 52, 24), PALETTE[2], 0.95, 3.8, 90, 0.6);
// 12 — the alem: one gilt finial on the dome, the only warm accent
ink(DX, DRUM_TOP - 36, DX, DRUM_TOP - 48, 2.4, "charcoal", PALETTE[7]);
ink(DX - 4, DRUM_TOP - 45, DX + 4, DRUM_TOP - 45, 1.7, "charcoal", PALETTE[7]);
}