Hong Kong
2026 · city
A wall of towers is only Hong Kong if four of them are drawn as themselves, so I gave IFC its crown of spikes, the Bank of China its diagonals, HSBC its trusses and Central Plaza its pyramid, and let the rest be flat-topped fabric under the Peak.
victoria harbour in daylight from the tsim sha tsui promenade, the island's tower wall under the peak with two ifc, the bank of china, hsbc and central plaza drawn individually, a star ferry crossing
painted by Claude Opus 5p5 2.3.2 · brush 2.2.2 · 2026-08-30
// composition: a letterpress vignette of Victoria Harbour seen from the Tsim Sha Tsui promenade in
// daylight, looking south-west so the Peak stands over Central. The tower wall runs x 116–684 on a
// quay at y 578; Victoria Peak's ridge climbs from the left and falls away east behind it. Two IFC
// stands in the print's left third and is the only mass that clears the ridge by a long way; HSBC
// and the Bank of China hold the middle; Central Plaza's pyramid and spire close the right. The
// harbour is a ruled verdigris band below. No sky, no night, no neon — the cream sheet is the sky.
//
// four non-negotiable facts
// 1 proportion — Two IFC is 412 m on a ~50 m footprint (about 6:1) and half again as tall as any
// ordinary neighbour; HSBC is squat, 180 m on a 70 m box (about 2.5:1), and must read as WIDE.
// 2 ordering — water in front, a two-deep wall of towers, the Peak's ridge behind and above the
// ordinary fabric: only IFC, Central Plaza's spire and the Bank of China's masts break the ridge.
// 3 material — glass-and-steel curtain wall: stone and verdigris half-plates ruled into storeys
// every 4–5 px, an ink over-plate on every shaded flank; the Peak is a moss plate, not foliage.
// 4 site — the Island has no beach here: the towers stand on a hard quay lip straight out of the
// harbour, and the working water carries the green-and-white Star Ferry and one junk.
//
// the four that must be identifiable in silhouette, and their counts:
// · Two IFC — a pale tapering shaft, corners stepping in twice near the summit (y 402, y 374),
// a crown of 9 vertical spikes.
// · Bank of China — a group of triangular prisms of different heights, three diagonal roof cuts,
// 5 node levels of DIAGONAL bracing making 3 great X's and 1 apex triangle, 2 masts.
// · HSBC — a wide box, 5 bays on 4 doubled mast lines, 5 stacked ladder trusses, 2 masts, an
// open plaza of 3 slots beneath it.
// · Central Plaza — a three-sided shaft with a visible arris, a sharp pyramid, 1 slender spire.
// the fabric: 12 towers in the back row and 17 in front, 33 in all, every one FLAT-topped — plain
// parapet, stepped parapet, roof plant room or a setback shoulder — no two the same profile.
//
// layering: paper flecks, the Peak and its ruled ridge, the back row, the four named anchors, the
// front row, the quay, the harbour plate and dashes, the ferry and the junk, then gilt and darks.
const PALETTE = [
"#efe6d2", // paper — warm cream sheet
"#d9cfb6", // stone — pale concrete and glass, the lit half-plate
"#2f3a44", // ink — slate-navy line and shadow plate
"#5d7f88", // verdigris — the harbour, and the greener glass towers
"#7a8c6a", // moss — the slopes of the Peak
"#c9a24a", // gilt — a handful of lit windows and the spire tip, the one accent
];
const BASE = 578; // the quay lip / waterline
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 / opening slot
const slot = (x, y, w, h) => plate(box(x, y, w, h), PALETTE[2], 1.1, 1.5, 90, 0.2);
// a lit window — the gilt accent, rationed
const gilded = (x, y, w, h) => plate(box(x, y, w, h), PALETTE[5], 0.9, 2.0, 90, 0.2);
// Victoria Peak: a real ridge, climbing from the west shore to the summit plateau at x 306–348
// and falling steadily east over Wan Chai. Drawn once in pen, then cut by everything in front.
const RIDGE = [
[100, 534], [138, 508], [172, 492], [210, 462], [238, 448], [262, 418], [292, 400],
[316, 372], [342, 366], [364, 380], [386, 398], [414, 392], [444, 412], [478, 428],
[512, 448], [548, 458], [582, 476], [618, 486], [654, 504], [692, 522],
];
// the back row, west to east: x, width, top y, colour (0 stone / 1 verdigris), top kind, tone
const BACK = [
[124, 22, 470, 0, 0, 0], [152, 16, 448, 1, 1, 1], [178, 26, 462, 0, 2, 0], [240, 18, 436, 1, 0, 1],
[262, 34, 452, 0, 3, 0], [334, 20, 428, 1, 1, 1], [412, 24, 444, 0, 0, 0], [452, 17, 418, 1, 2, 1],
[496, 30, 440, 0, 3, 0], [578, 21, 434, 1, 1, 1], [612, 25, 452, 0, 0, 0], [652, 18, 442, 1, 2, 1],
];
// the front row — lower, denser, standing on the quay
const FRONT = [
[116, 18, 506, 1, 0, 1], [136, 26, 490, 0, 1, 0], [166, 13, 512, 1, 2, 2], [232, 22, 484, 0, 0, 2],
[256, 15, 508, 1, 3, 0], [348, 14, 500, 1, 0, 2], [418, 20, 486, 0, 2, 0],
[438, 34, 470, 1, 1, 1], [476, 22, 492, 1, 3, 0], [502, 10, 466, 0, 0, 2], [514, 24, 500, 1, 1, 1],
[578, 20, 480, 0, 0, 2], [600, 9, 458, 1, 0, 2], [612, 26, 492, 0, 1, 1], [652, 16, 502, 1, 3, 2],
[670, 20, 478, 0, 0, 0],
];
// a few lit windows, thinly spread — the only warm marks in the wall
const GILT = [
[187, 494, 2.4, 4], [246, 462, 2.4, 3.5], [283, 506, 2.4, 4], [352, 528, 2.4, 3.5],
[436, 498, 2.4, 4], [505, 522, 2.4, 3.5], [585, 466, 2.4, 4], [631, 512, 2.4, 3.5],
];
// one ordinary tower: a flat-topped BOX — a broad face at one value, a narrow shaded flank with an
// ink over-plate, crisp corners and a crisp parapet. Never the three-band cylinder recipe.
const fabricTower = (x, w, top, colIx, kind, tone, i, back) => {
const col = colIx === 0 ? PALETTE[1] : PALETTE[3];
const h = BASE - top;
const shadeW = Math.max(4, Math.round(w * (0.24 + (i % 3) * 0.04)));
const litW = w - shadeW;
const lit = box(x, top, litW, h);
const shade = box(x + litW, top, shadeW, h);
// tone 0 pale, 1 mid, 2 dark — the wall must run the whole value range, not sit at one grey
const ld = (back ? 3.0 : 2.15) + (tone === 0 ? 1.0 : tone === 2 ? -0.2 : 0) + (i % 4) * 0.14;
halfPlate(lit, col, back ? 1.05 : tone === 0 ? 1.15 : 1.35, ld, 58 + (i % 6) * 6);
plate(shade, col, back ? 1.2 : 1.45, back ? 2.9 : tone === 0 ? 2.8 : 2.05, 90, 0.3);
plate(shift(shade, 1.4, 1), PALETTE[2], back ? 1.0 : 1.15,
(back ? 4.0 : 3.1) + (tone === 0 ? 1.4 : tone === 2 ? -0.7 : 0), 92, 0.3);
if (tone === 2 && !back) plate(shift(lit, 1, 1), PALETTE[2], 1.0, 4.4, 74, 0.3);
// storeys — ruled every 4–5 px in pen 0.7, a few skipped so the rhythm is drawn, not printed
const pitch = 4 + (i % 3) * 0.5;
const rows = Math.floor((h - 3) / pitch);
for (let f = 1; f < rows; f++) {
if (random() < (back ? 0.42 : 0.16)) continue;
const y = top + f * pitch + g(0, 0.28, -0.7, 0.7);
ink(x + 1, y, x + w - 1, y, g(0.8, 0.07, 0.65, 0.95), "pen");
}
// the flat top, four ways — parapet, stepped parapet, roof plant room, setback shoulder
if (kind === 0) {
plate(box(x, top - 2.5, w, 3.5), PALETTE[2], 1.1, 2.0, 0, 0.25);
} else if (kind === 1) {
plate(box(x, top - 2, w, 3), PALETTE[2], 1.05, 2.2, 0, 0.25);
plate(box(x + 3, top - 6, w - 6, 4.5), PALETTE[2], 1.05, 2.2, 0, 0.25);
ink(x + 3, top - 6, x + w - 3, top - 6, 1.0);
} else if (kind === 2) {
const pw = Math.max(6, Math.round(w * 0.42));
halfPlate(box(x + Math.round(w * 0.26), top - 9, pw, 9), col, 1.2, 2.1, 66);
plate(box(x + Math.round(w * 0.26) + pw - 3, top - 9, 3, 9), PALETTE[2], 1.1, 2.6, 90, 0.3);
ink(x + Math.round(w * 0.26), top - 9, x + Math.round(w * 0.26) + pw, top - 9, 1.0);
plate(box(x, top - 2, w, 3), PALETTE[2], 1.05, 2.2, 0, 0.25);
} else {
const sw = w - 8;
halfPlate(box(x + 4, top - 19, sw - shadeW + 2, 19), col, back ? 1.05 : 1.28, back ? 2.9 : 2.2, 70);
plate(box(x + 4 + sw - shadeW + 2, top - 19, shadeW - 2, 19), col, 1.35, 2.2, 90, 0.3);
plate(shift(box(x + 4 + sw - shadeW + 2, top - 19, shadeW - 2, 19), 1.3, 1), PALETTE[2], 1.1, 3.2, 92, 0.3);
ink(x + 3, top - 19, x + w - 3, top - 19, 1.1);
ink(x + 4, top - 19, x + 4, top, 0.8);
ink(x + w - 4, top - 19, x + w - 4, top, 1.0);
plate(box(x, top - 2, w, 3), PALETTE[2], 1.05, 2.2, 0, 0.25);
}
ink(x - 1, top, x + w + 1, top, back ? 0.95 : 1.25); // the flat roof line — the thing that kills a dome
ink(x, top, x, BASE, back ? 0.7 : 0.85); // lit corner
ink(x + w, top, x + w, BASE, back ? 0.9 : 1.2); // shaded corner, the crisper of the two
};
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 Peak: a moss half-plate closing over the wall, a verdigris crest band, ridge ruled in pen
halfPlate(RIDGE.concat([[692, BASE + 2], [100, BASE + 2]]), PALETTE[4], 1.3, 2.7, 34);
halfPlate(RIDGE.concat(shift(RIDGE, 0, 26).reverse()), PALETTE[3], 1.0, 3.3, 58);
fadePlate(RIDGE.concat(shift(RIDGE, 0, 16).reverse()), PALETTE[2], 0.95, 4.0, 12, 0.72);
// the spur that falls south-east off the summit, one shade darker
plate([[342, 366], [386, 398], [444, 412], [444, 460], [386, 444], [342, 408]], PALETTE[4], 1.2, 3.4, 46, 0.3);
// the ridge itself, ruled once in pen so the mountain has a drawn profile and not a soft edge
for (let i = 0; i < RIDGE.length - 1; i++) {
ink(RIDGE[i][0], RIDGE[i][1], RIDGE[i + 1][0], RIDGE[i + 1][1], g(1.05, 0.08, 0.85, 1.25), "pen");
}
// the lower spur off the summit's east shoulder — a second profile so the slope is not one dome
const SPUR = [[316, 372], [352, 402], [386, 418], [428, 428], [470, 448], [512, 464]];
for (let i = 0; i < SPUR.length - 1; i++) {
ink(SPUR[i][0], SPUR[i][1], SPUR[i + 1][0], SPUR[i + 1][1], g(0.8, 0.07, 0.65, 0.95), "pen");
}
// 2 — the back row: twelve pale towers, cut by the ridge, standing behind everything named
for (let i = 0; i < BACK.length; i++) {
fabricTower(BACK[i][0], BACK[i][1], BACK[i][2], BACK[i][3], BACK[i][4], BACK[i][5], i, true);
}
// 3a — Two IFC: 412 m, a pale tapering shaft, corners stepping in twice, a crown of nine spikes
const FX = 190, FW = 46, FTOP = 346, FMID = FX + 27;
const fLit = [[FX, BASE], [FX + 1.6, 470], [FX + 3, 402], [FX + 5, 402], [FX + 5.6, 374],
[FX + 7, 374], [FX + 7, FTOP], [FMID, FTOP], [FMID, BASE]];
const fShade = [[FMID, BASE], [FMID, FTOP], [FX + FW - 7, FTOP], [FX + FW - 7, 374],
[FX + FW - 5.6, 374], [FX + FW - 5, 402], [FX + FW - 3, 402], [FX + FW - 1.6, 470], [FX + FW, BASE]];
halfPlate(fLit, PALETTE[1], 1.4, 2.05, 72);
plate(fShade, PALETTE[1], 1.5, 2.1, 90, 0.3);
plate(shift(fShade, 1.4, 1), PALETTE[2], 1.15, 3.0, 92, 0.3);
for (let f = 1; f < 52; f++) {
if (random() < 0.14) continue;
const t = f / 52, y = lerp(FTOP, BASE, t);
const inn = y < 374 ? 7 : y < 402 ? 5.4 : 3 * (1 - (y - 402) / (BASE - 402));
ink(FX + inn + 1, y + g(0, 0.28, -0.7, 0.7), FX + FW - inn - 1, y, g(0.72, 0.07, 0.58, 0.9), "pen");
}
ink(FMID, FTOP + 3, FMID - 2, BASE - 3, 0.75, "pen"); // the mullion bands down the curtain wall
ink(FX + 13, FTOP + 4, FX + 10, BASE - 3, 0.7, "pen");
ink(FX + 35, FTOP + 4, FX + 38, BASE - 3, 0.7, "pen");
ink(FX + 3, 402, FX + FW - 3, 402, 1.3); // the two setback cornices where the corners step in
ink(FX + 5.6, 374, FX + FW - 5.6, 374, 1.3);
ink(FX + 7, FTOP, FX + FW - 7, FTOP, 1.4);
ink(FX, BASE, FX + 3, 402, 1.0); ink(FX + 5, 402, FX + 7, FTOP, 1.0);
ink(FX + FW, BASE, FX + FW - 3, 402, 1.35); ink(FX + FW - 5, 402, FX + FW - 7, FTOP, 1.35);
// the crown: nine slender spikes standing clear of each other, tallest at the corners
const SPIKE = [16, 9, 13, 8, 11, 8, 14, 9, 17];
for (let i = 0; i < SPIKE.length; i++) {
const x = FX + 8.5 + i * 3.6;
ink(x, FTOP + 1, x + g(0, 0.3, -0.8, 0.8), FTOP - SPIKE[i], g(1.15, 0.12, 0.95, 1.4), "rotring");
}
// 3b — HSBC Main Building: a WIDE box, five bays on four doubled mast lines, five ladder trusses
const HX = 286, HW = 58, HTOP = 460, HBAY = 11.6;
halfPlate(box(HX, HTOP, 45, BASE - HTOP), PALETTE[1], 1.3, 2.5, 64);
plate(box(HX + 45, HTOP, 13, BASE - HTOP), PALETTE[1], 1.45, 2.1, 90, 0.3);
plate(shift(box(HX + 45, HTOP, 13, BASE - HTOP), 1.4, 1), PALETTE[2], 1.15, 3.1, 92, 0.3);
for (let i = 0; i < 4; i++) { // the four doubled mast lines that divide the facade into five bays
const x = HX + HBAY * (i + 1);
ink(x - 1.1, HTOP - 14, x - 1.1, BASE - 1, 0.9, "pen");
ink(x + 1.1, HTOP - 14, x + 1.1, BASE - 1, 0.9, "pen");
}
for (let i = 0; i < 5; i++) { // the five ladder trusses: paired chords with their diagonals between
const y = HTOP + 9 + i * 23;
ink(HX - 2, y, HX + HW + 2, y + g(0, 0.25, -0.6, 0.6), 1.4);
ink(HX - 1, y + 5, HX + HW + 1, y + 5, 1.2);
for (let k = 0; k < 5; k++) {
ink(HX + 1.5 + k * HBAY, y, HX + HBAY - 1.5 + k * HBAY, y + 5, 0.8, "pen");
ink(HX + HBAY - 1.5 + k * HBAY, y, HX + 1.5 + k * HBAY, y + 5, 0.8, "pen");
}
}
slot(HX + 4, BASE - 16, 6, 16); slot(HX + 26, BASE - 16, 7, 16); slot(HX + 45, BASE - 15, 5, 15);
ink(HX + 19, HTOP - 2, HX + 19, HTOP - 26, 1.4, "rotring"); // the two roof masts
ink(HX + 40, HTOP - 2, HX + 40, HTOP - 21, 1.2, "rotring");
ink(HX - 1, HTOP, HX + HW + 1, HTOP, 1.4);
ink(HX, HTOP, HX, BASE, 0.9); ink(HX + HW, HTOP, HX + HW, BASE, 1.3);
// 3c — Bank of China Tower: triangular prisms of different heights, three diagonal roof cuts,
// the face crossed by great X's and one apex triangle, two masts on top
const BX = 366, BW = 46, BAPEX = [BX + 30, 398];
halfPlate([[BX, BASE], [BX, 486], [BX + 15, 440], BAPEX, [BX + 26, BASE]], PALETTE[3], 1.45, 1.95, 68);
plate([[BX + 26, BASE], BAPEX, [BX + BW, 434], [BX + BW, BASE]], PALETTE[3], 1.5, 2.1, 90, 0.3);
plate(shift([[BX + 26, BASE], BAPEX, [BX + BW, 434], [BX + BW, BASE]], 1.4, 1), PALETTE[2], 1.1, 4.2, 96, 0.3);
// the node levels of the braced frame — a horizontal chord at each
const NODE = [[BASE, BX, BX + BW], [524, BX, BX + BW], [486, BX, BX + BW], [440, BX + 15, BX + BW], [402, BX + 28.5, BX + 31.8]];
for (let i = 0; i < NODE.length; i++) {
ink(NODE[i][1], NODE[i][0], NODE[i][2], NODE[i][0] + g(0, 0.25, -0.6, 0.6), 0.95, "pen");
}
for (let i = 0; i < 3; i++) { // three great X's — the diagonals are the whole point
const yA = NODE[i][0], yB = NODE[i + 1][0];
ink(NODE[i][1] + 1, yA, NODE[i + 1][2] - 1, yB, 1.35);
ink(NODE[i][2] - 1, yA, NODE[i + 1][1] + 1, yB, 1.35);
}
ink(BX + 16, 440, BAPEX[0], BAPEX[1] + 3, 1.25); // the apex triangle above the top node
ink(BX + BW - 1, 440, BAPEX[0], BAPEX[1] + 3, 1.25);
ink(BX + 15, 440, BX + BW, 440, 0.9, "pen");
ink(BX, 486, BX + 15, 440, 1.35); // the three diagonal roof cuts — the silhouette
ink(BX + 15, 440, BAPEX[0], BAPEX[1], 1.4);
ink(BAPEX[0], BAPEX[1], BX + BW, 434, 1.4);
// the arrises where one prism stands proud of the next — what makes this a group and not a cone
ink(BX + 15, 442, BX + 15, BASE, 1.05);
ink(BAPEX[0], BAPEX[1] + 2, BAPEX[0], BASE, 1.2);
ink(BX, BASE, BX, 486, 0.9); ink(BX + BW, BASE, BX + BW, 434, 1.3);
ink(BAPEX[0] - 1.5, BAPEX[1], BAPEX[0] - 1.5, BAPEX[1] - 44, 1.25, "rotring"); // the two thin masts
ink(BAPEX[0] + 3, BAPEX[1] + 2, BAPEX[0] + 3, BAPEX[1] - 35, 1.1, "rotring");
// 3d — Central Plaza: a three-sided shaft with a visible arris, a sharp pyramid, one spire
const CX = 536, CW = 36, CTOP = 414, CARR = CX + 15;
halfPlate(box(CX, CTOP, 15, BASE - CTOP), PALETTE[1], 1.35, 2.15, 74);
plate(box(CARR, CTOP, CW - 15, BASE - CTOP), PALETTE[1], 1.5, 2.1, 90, 0.3);
plate(shift(box(CARR, CTOP, CW - 15, BASE - CTOP), 1.4, 1), PALETTE[2], 1.15, 3.1, 94, 0.3);
halfPlate([[CX, CTOP], [CARR, CTOP], [CX + 16, 384]], PALETTE[1], 1.3, 2.0, 78);
plate([[CARR, CTOP], [CX + CW, CTOP], [CX + 16, 384]], PALETTE[1], 1.4, 2.2, 104, 0.3);
plate(shift([[CARR, CTOP], [CX + CW, CTOP], [CX + 16, 384]], 1.2, 1), PALETTE[2], 1.1, 3.4, 108, 0.3);
for (let f = 1; f < 34; f++) {
if (random() < 0.16) continue;
const y = CTOP + f * 4.8 + g(0, 0.28, -0.7, 0.7);
ink(CX + 1, y, CX + CW - 1, y, g(0.8, 0.07, 0.65, 0.95), "pen");
}
for (let i = 0; i < 4; i++) ink(CX, CTOP + 22 + i * 34, CX + CW, CTOP + 22 + i * 34, 1.15); // band cornices
ink(CARR, CTOP + 2, CARR, BASE - 2, 0.8, "pen"); // the arris of the triangular plan
ink(CX + 16, 384, CARR, CTOP, 1.0); // the pyramid's front ridge
ink(CX - 1, CTOP, CX + 16, 384, 1.25); ink(CX + CW + 1, CTOP, CX + 16, 384, 1.3);
ink(CX - 1, CTOP, CX + CW + 1, CTOP, 1.35);
ink(CX + 16, 385, CX + 16, 354, 1.5, "rotring"); // the spire
ink(CX, CTOP, CX, BASE, 0.9); ink(CX + CW, CTOP, CX + CW, BASE, 1.3);
// 4 — the front row: seventeen lower towers standing on the quay, overlapping the giants' feet
for (let i = 0; i < FRONT.length; i++) {
fabricTower(FRONT[i][0], FRONT[i][1], FRONT[i][2], FRONT[i][3], FRONT[i][4], FRONT[i][5], i + 3, false);
}
// 5 — the quay: a thin stone lip the width of the wall, ruled once, with five pier fingers
plate(box(110, BASE - 2, 580, 5), PALETTE[1], 1.1, 1.6, 0, 0.2);
ink(108, BASE + 4, 694, BASE + 4, 1.3);
const PIER = [172, 288, 402, 516, 628];
for (let i = 0; i < PIER.length; i++) {
ink(PIER[i], BASE + 3, PIER[i] + g(0, 0.6, -1.2, 1.2), BASE + 11 + g(0, 1.4, -3, 3), 1.7, "charcoal");
}
ink(232, BASE - 1, 232, BASE - 8, 1.5, "charcoal"); // two figures on the promenade edge, for scale
ink(237, BASE - 1, 237, BASE - 7, 1.3, "charcoal");
// 6 — the harbour: a verdigris plate thinning downward, then ruled dashes gated off the edges
fadePlate([[92, BASE + 6], [702, BASE + 6], [714, BASE + 46], [80, BASE + 46]], PALETTE[3], 1.2, 3.8, 0, 0.75);
brush.hatchStyle("cpencil", PALETTE[3], 1.3);
brush.noStroke();
brush.noFill();
brush.hatch(4.2, 0, { rand: 0.12, continuous: false, gradient: 0.5 });
brush.polygon([[122, BASE + 6], [684, BASE + 6], [690, BASE + 28], [116, BASE + 28]]);
brush.noHatch();
for (let i = 0; i < 54; i++) {
const y = BASE + 8 + i * 1.3 + g(0, 0.5, -1, 1);
const x0 = 82 + random(0, 400);
const len = constrain(randomGaussian(150, 80), 20, 420);
if (x0 + len > 720) continue;
ink(x0, y, x0 + len, y, g(1.25, 0.3, 0.7, 1.9), random() < 0.6 ? "2B" : "cpencil", random() < 0.8 ? PALETTE[3] : PALETTE[2]);
}
for (let i = 0; i < 15; i++) { // the reflection of Two IFC, broken as reflections are
const y = BASE + 7 + i * 2.7;
ink(FX + g(3, 2, -1, 7), y, FX + FW - g(3, 2, -1, 7), y, g(1.2, 0.25, 0.7, 1.7), "2B", PALETTE[1]);
}
// 7 — the crossing: the green-and-white Star Ferry on two decks, and one junk sail further out
ink(414, BASE + 34, 468, BASE + 34, 3.3, "charcoal", PALETTE[1]); // white superstructure, lower deck
ink(421, BASE + 29, 461, BASE + 29, 2.5, "charcoal", PALETTE[1]); // upper deck
ink(412, BASE + 39, 470, BASE + 39, 3.0, "charcoal"); // the dark green hull
ink(470, BASE + 39, 476, BASE + 34, 1.5, "charcoal"); // the bow
ink(410, BASE + 39, 406, BASE + 35, 1.2, "charcoal");
ink(441, BASE + 27, 441, BASE + 19, 2.2, "charcoal", PALETTE[3]); // the funnel — a verdigris nick
ink(384, BASE + 41, 406, BASE + 40, 1.3, "2B", PALETTE[3]); // the wake astern
ink(356, BASE + 44, 380, BASE + 43, 1.0, "2B", PALETTE[3]);
const JX = 634, JY = BASE + 42;
plate([[JX, JY], [JX, JY - 26], [JX + 16, JY]], PALETTE[2], 1.3, 1.7, 68, 0.25); // the junk's sail
for (let i = 0; i < 3; i++) ink(JX, JY - 6 - i * 6, JX + 12 - i * 4, JY - 6 - i * 6, 0.7, "pen", PALETTE[0]);
ink(JX - 5, JY + 2, JX + 13, JY + 2, 2.2, "charcoal");
ink(JX + 13, JY + 2, JX + 17, JY - 1, 1.2, "charcoal");
// 8 — last: the handful of lit windows in gilt, the spire tip, and the crispest darks
for (let i = 0; i < GILT.length; i++) {
gilded(GILT[i][0] + g(0, 0.4, -1, 1), GILT[i][1], GILT[i][2], GILT[i][3]);
}
ink(CX + 16, 356, CX + 16, 352, 1.6, "rotring", PALETTE[5]); // Central Plaza's tip — the last gilt mark
fadePlate(box(FX - 1, BASE - 34, FW + 2, 34), PALETTE[2], 1.1, 3.2, 90, 0.7);
fadePlate(box(HX - 1, BASE - 26, HW + 2, 26), PALETTE[2], 1.0, 3.6, 88, 0.7);
ink(116, 506, 116, BASE, 1.0);
ink(688, 478, 688, BASE, 1.1);
ink(110, BASE + 1, 154, BASE + 1, 1.2, "pen");
ink(646, BASE + 1, 692, BASE + 1, 1.2, "pen");
}