Sydney
2026 · city
I printed Bennelong Point as a small letterpress plate: two groups of four vaults nesting and leaning the same way, their tiled faces pale and their open ends dark, on a pink granite podium with its steps ruled in. The bridge is one low arch behind.
the opera house from the water off circular quay — two nested groups of tiled shells on a pink granite podium, the harbour bridge small and low behind
painted by Claude Opus 5p5 2.3.2 · brush 2.2.2 · 2026-08-30
// composition: a letterpress vignette of the Sydney Opera House seen from the water off Circular Quay,
// looking north-east up Bennelong Point. The Concert Hall group stands left of the print's centre and
// nearest; the Opera Theatre group sits behind and to its right, smaller and higher-based; the three
// small Bennelong restaurant shells step up at the front left; the Harbour Bridge's steel arch is small
// and low behind to the left. A broad pink-granite podium carries the lot; ruled harbour below; no sky.
// four non-negotiable facts — (1) proportion: the complex is roughly 2.5x as wide as its tallest shell
// is high, and each shell is a touch taller than its own footprint is long; (2) ordering: within a group
// the shells nest and step UP away from the viewer, all leaning the same way, each vault's dark inner
// face showing only in the slot ahead of it and at the open glazed end; (3) material: pale chevron-tiled
// shells (stone half-plate, pen rulings on the curve), dark glass where they open, pink Tarana granite
// podium; (4) site: it stands in the water on a point, not on a street — harbour in front, bridge behind.
// counts drawn: 4 shells in the Concert Hall group, 4 in the Opera Theatre group, 3 Bennelong shells,
// 3 rib courses per large shell, 4 glazing mullions per open end, 9 treads on the monumental steps,
// 4 granite courses along the podium, 11 hangers and 2 pylons on the bridge, 8 people, 1 ferry.
// layering: paper flecks, the bridge, the podium and steps, the far group, the near group, the small
// shells, the ruled harbour, the ferry, the last crisp darks, and the people on top.
const PALETTE = [
"#efe6d2", // paper — warm cream sheet
"#d9cfb6", // stone — the tiled shells, the pale plate
"#2f3a44", // ink — slate-navy line, glazing and shadow plate
"#5d7f88", // verdigris — harbour water and the bridge steel
"#c9a58e", // rose — the pink granite podium and its steps
"#7a8c6a", // moss — the green ferry hull
"#c9a24a", // gilt — the ferry's yellow band, the one warm accent
];
const BASE = 556; // podium top under the near group
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 rev = (pts) => pts.slice().reverse();
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 — 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 ruled curve — a shell arris, a rib course, a bridge chord
const curveInk = (pts, w, name, col) => {
brush.noFill();
brush.set(name || "pen", col || PALETTE[2], w || 0.8);
brush.beginShape(0.5);
for (let i = 0; i < pts.length; i++) brush.vertex(pts[i][0], pts[i][1]);
brush.endShape(false);
};
// quadratic sampling — every curve in the piece is one of these
const bez = (p0, c, p1, n) => {
const out = [];
for (let i = 0; i <= n; i++) {
const t = i / n, u = 1 - t;
out.push([
u * u * p0[0] + 2 * u * t * c[0] + t * t * p1[0],
u * u * p0[1] + 2 * u * t * c[1] + t * t * p1[1],
]);
}
return out;
};
// one vaulted shell: a steep arris rising from the left and leaning over into the point,
// then a fat convex sweep back down to the base — the spherical-segment profile.
const sailOf = (x0, x1, base, ay) => {
const w = x1 - x0, h = base - ay, ax = x0 + w * 0.63;
const lead = bez([x0, base], [x0 + w * 0.05, ay + h * 0.19], [ax, ay], 10);
const trail = bez([ax, ay], [ax + (x1 - ax) * 0.62 + w * 0.1, ay + h * 0.46], [x1, base], 10);
return { ax: ax, ay: ay, x0: x0, x1: x1, base: base, w: w, h: h, lead: lead, trail: trail, outer: lead.concat(trail.slice(1)) };
};
// draw one shell: the pale chevron-tiled skin at open spacing (the palest mass in the print),
// a thin shadow where it laps the shell behind, the dark glazed mouth it opens onto with a
// crisp rotring edge, the ruled arris, and a few rib lines hugging the leading edge.
const sailDraw = (s, voidW, leadCut, ribs, ang, lap) => {
const vl = s.lead.slice(0, leadCut);
halfPlate(s.outer, PALETTE[1], 1.0, 3.4, ang);
if (lap) plate(s.trail.concat(rev(shift(s.trail, 3.4, 0.8))), PALETTE[2], 1.0, 2.6, ang + 30, 0.3);
// the glazed mouth — widest where it meets the podium, closing as it climbs the arris
const far = vl.map((p, i) => {
const t = i / (vl.length - 1);
return [p[0] - voidW * pow(1 - t, 0.85), p[1] + 1.6 * (1 - t)];
});
plate(vl.concat(rev(far)), PALETTE[2], 1.35, 1.25, 86, 0.16);
curveInk(far, 1.0, "rotring", PALETTE[2]); // the mouth's crisp far edge
curveInk(s.lead, 1.2, "rotring", PALETTE[2]);
curveInk(s.trail, 0.8, "pen", PALETTE[2]);
for (let j = 1; j <= ribs; j++) {
const f = 0.1 * j;
curveInk(
bez(
[s.x0 + s.w * f + g(0, 0.4, -0.9, 0.9), s.base],
[s.x0 + s.w * (f + 0.04), s.ay + s.h * 0.32],
[s.ax - j * 1.6, s.ay + j * 3.0],
8,
),
0.7,
"pen",
PALETTE[2],
);
}
};
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[4]);
}
// 1 — the Harbour Bridge behind to the left: one steel arch, its deck, hangers, two pylons
const upper = bez([116, 552], [204, 464], [292, 552], 14);
const lower = bez([116, 552], [204, 484], [292, 552], 14);
halfPlate(box(120, 516, 12, 36), PALETTE[1], 1.1, 2.4, 72); // south pylon
halfPlate(box(276, 518, 12, 34), PALETTE[1], 1.1, 2.4, 72); // north pylon
ink(118, 516, 133, 516, 1.0); ink(274, 518, 289, 518, 1.0);
for (let i = 1; i < 14; i++) ink(upper[i][0], upper[i][1], lower[i][0], lower[i][1], 0.55, "pen", PALETTE[2]); // web
curveInk(upper, 1.15, "rotring", PALETTE[2]);
curveInk(lower, 0.9, "rotring", PALETTE[2]);
ink(114, 542, 298, 542, 1.1); // the deck
ink(114, 545, 298, 545, 0.7, "pen");
for (let i = 1; i < 14; i++) {
if (lower[i][1] < 539) ink(lower[i][0], lower[i][1], lower[i][0] + g(0, 0.3, -0.7, 0.7), 542, 0.6, "pen");
}
// 2 — the podium: a broad pink-granite plinth, four ruled courses, the monumental steps at the south end
const PDL = 162, PDR = 690;
const podium = [[PDL, 560], [PDR, 546], [PDR, 584], [PDL, 598]];
halfPlate(podium, PALETTE[4], 1.4, 2.0, 66);
halfPlate(podium, PALETTE[1], 0.95, 4.2, 14);
halfPlate([[PDL, 560], [PDR, 546], [PDR, 560], [PDL, 574]], PALETTE[1], 1.2, 2.4, 8);
fadePlate([[PDL, 584], [PDR, 570], [PDR, 584], [PDL, 598]], PALETTE[2], 0.9, 4.4, 0, 0.45);
ink(PDL - 5, 560, PDR + 4, 546, 1.3);
for (let i = 1; i <= 4; i++) ink(PDL + 2, 560 + i * 7.4, PDR - 2, 546 + i * 7.4, i === 2 ? 0.9 : 0.7, "pen");
const stair = [[154, 592], [250, 558], [282, 562], [186, 598]];
halfPlate(stair, PALETTE[4], 1.25, 2.2, 40);
plate(shift(stair, 1.5, 1), PALETTE[2], 1.0, 3.6, 44, 0.3);
for (let i = 0; i < 9; i++) {
const t = i / 8;
ink(154 + t * 96, 592 - t * 34, 186 + t * 96, 598 - t * 36, 0.7, "pen");
}
ink(154, 592, 250, 558, 1.0); ink(186, 598, 282, 562, 0.9);
// 3 — the Opera Theatre group: four nested shells, behind and right, base a little higher
const gb = [
sailOf(486, 572, 546, 488),
sailOf(514, 618, 546, 462),
sailOf(546, 654, 546, 438),
sailOf(576, 690, 546, 414),
];
for (let i = 3; i >= 0; i--) sailDraw(gb[i], i === 0 ? 24 : 13, 8, i >= 2 ? 3 : 2, 58 + i * 9, i < 3);
for (let k = 0; k < 4; k++) ink(486 - 16 + k * 4.4, 546, 486 - 16 + k * 4.4, 546 - (12 + k * 6), 0.7, "pen", PALETTE[1]); // glazing mullions
// 4 — the Concert Hall group: four nested shells, larger, nearer, left of the print's centre
const ga = [
sailOf(258, 372, BASE, 474),
sailOf(292, 436, BASE, 440),
sailOf(328, 498, BASE, 404),
sailOf(364, 566, BASE, 366),
];
for (let i = 3; i >= 0; i--) sailDraw(ga[i], i === 0 ? 28 : 15, 8, i >= 2 ? 3 : 2, 66 + i * 8, i < 3);
for (let k = 0; k < 4; k++) ink(258 - 20 + k * 5.2, BASE, 258 - 20 + k * 5.2, BASE - (15 + k * 7), 0.7, "pen", PALETTE[1]); // glazing mullions
// 5 — the three small Bennelong restaurant shells, low at the front left
const gc = [sailOf(192, 238, 559, 532), sailOf(212, 264, 559, 521), sailOf(232, 290, 559, 510)];
for (let i = 2; i >= 0; i--) sailDraw(gc[i], i === 0 ? 11 : 7, 8, 1, 72 + i * 7, i < 2);
// 6 — people on the forecourt, unsentimental ticks for scale
// (people are drawn last, in section 9, so no plate can swallow them)
// 7 — the harbour: a ruled plate that thins downward, broken dashes, the shells' reflection
fadePlate([[146, 598], [692, 584], [704, 624], [134, 634]], 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([[172, 598], [674, 585], [680, 608], [166, 620]]);
brush.noHatch();
for (let i = 0; i < 52; i++) {
const y = 600 + i * 1.5 + g(0, 0.5, -1, 1);
const x0 = 132 + random(0, 340);
const len = constrain(randomGaussian(126, 70), 18, 380);
if (x0 + len > 704) 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 < 13; i++) {
const y = 599 + i * 1.6;
ink(300 + g(0, 6, -12, 12), y, 500 - g(0, 8, -14, 20), y, g(1.1, 0.25, 0.7, 1.5), "2B", PALETTE[1]);
}
// the green-and-yellow ferry, low and to the right: a charcoal dash with one gilt nick
ink(554, 615, 606, 615, 3.0, "charcoal", PALETTE[5]);
ink(576, 611, 594, 611, 1.9, "charcoal", PALETTE[6]);
ink(606, 615, 612, 610, 1.6, "charcoal", PALETTE[5]);
ink(572, 611, 572, 603, 1.8, "charcoal", PALETTE[2]);
// 8 — last darks: crisp ends on the podium, the deep contact shadow at its foot, the waterline
ink(PDL, 560, PDL - 2, 598, 1.0);
ink(PDR, 546, PDR + 1, 584, 1.1);
ink(PDL - 6, 598, PDR + 6, 584, 1.3);
// 9 — people, unsentimental ticks for scale: two at the head of the steps, four on the
// forecourt at the near shell's foot, two along the quay to the right
const deck = (x) => 560 - (x - PDL) / 528 * 14;
const folk = [
[200, 592 - (200 - 154) * 34 / 96], [226, 592 - (226 - 154) * 34 / 96],
[300, deck(300) + 10], [318, deck(318) + 9], [337, deck(337) + 10], [357, deck(357) + 9],
[520, deck(520) + 10], [604, deck(604) + 9],
];
for (let i = 0; i < folk.length; i++) {
const x = folk[i][0], y = folk[i][1];
ink(x, y, x + g(0, 0.5, -1.2, 1.2), y - g(8.2, 0.6, 7, 9), g(1.6, 0.08, 1.4, 1.7), "charcoal");
}
}