Jaipur
2026 · city
I stood left of the axis so the screen would recede and its west return turn dark, then cut the storeys bay by bay — 28, 24, 18, 10 — until the outline went lacy. The lime rulings are the striping; the crown sits left, the bazaar answers it right.
the hawa mahal from across johari bazaar and well to the left, its honeycomb of jharokhas receding to a crown of pointed bays
painted by Claude Opus 5p5 2.3.2 · brush 2.2.2 · 2026-08-30
// composition: a letterpress vignette of the Hawa Mahal seen from across Johari Bazaar and well to
// the LEFT of its axis, so the screen recedes to a right-hand vanishing point and its west return
// turns toward us as a shaded flank. Two-point perspective off one eye level (y 604): everything
// above the eye drops toward the right, everything below it rises. The crown of pointed bays sits
// left of the print's centre; the bazaar shops carrying on past the far end balance it to the right.
// four facts I will not break: (1) proportion — the screen is ~26 m wide and ~15 m tall, so the print
// is roughly 1.6x wider than high, a broad low pyramid, never a tower; (2) ordering — it is a facade
// ONE ROOM DEEP, so the jharokhas project FORWARD of the wall and their curved bangaldar roofs and
// domed finials break the outline of every storey: the silhouette is scalloped, never a rectangle;
// (3) material — one pink-red sandstone throughout, lime-picked white at every chajja and around
// every opening, the returns the same stone a shade deeper under ink; (4) site — it stands straight
// on the bazaar with shops in its ground wall, nothing behind it, and no sky worth painting.
// counts drawn: 28 jharokhas on storey two, 24 on three, 18 on four, 10 on five; 12 arched
// shopfronts and 1 central gateway in the ground wall; 4 lime chajjas; 3 crown chhatris; 1 gilt mark.
// layering: paper flecks, the four honeycomb storeys from the topmost (furthest) forward — each a
// scallop-topped rose plate with its shaded west return in oxblood under an ink over-plate, then bay
// by bay an ink lattice slot, lime lintel and sill, a three-vertex curved cap and a finial tick —
// then the ground wall and its arcade, the projecting lime chajjas and terraces over everything,
// the crown, the bazaar blocks running off to the right, the street with its traffic, last darks.
const PALETTE = [
"#efe6d2", // paper — warm cream sheet
"#c9917c", // rose — the pink sandstone of the screen
"#2f3a44", // ink — slate-navy line, lattice and shadow plate
"#8b3a2f", // oxblood — the same stone in shade: the west returns and over-plates
"#ede4ce", // lime — the white picking on chajjas and around every opening
"#d9cfb6", // stone — the bazaar street and the shops beyond the far end
"#c9a24a", // gilt — the crown finial, the one warm accent
];
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 — 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 / lattice / arch slot
const slot = (x, y, w, h) => plate(box(x, y, w, h), PALETTE[2], 1.1, 1.5, 90, 0.2);
// --- the perspective frame -------------------------------------------------
// t runs 0 (the near, left end of the facade) to 1 (the far, right end).
const EYE = 604; // eye level: a person standing in Johari Bazaar
const PPM = 22.4; // pixels per metre at the near end
const CC = 0.22; // recession: the far end reads at 0.82 of the near end
const XL = 148, XR = 596;
const sAt = (t) => 1 / (1 + CC * t);
const xAt = (t) => XL + (XR - XL) * ((t * (1 + CC)) / (1 + CC * t));
const yAt = (t, h) => EYE - (h - 1.55) * PPM * sAt(t);
const mAt = (t) => PPM * sAt(t); // metres to pixels at t
// the west return: the end wall turning away from us, up-and-left toward its own vanishing point
const backOf = (x0, yTop, yBot, d) => [[x0, yTop], [x0 - d, yTop + d * 0.46], [x0 - d, yBot + d * 0.18], [x0, yBot]];
// [t0, t1, bays, h bottom, h top, hatch angle, pointed crown]
const TIERS = [
[0.015, 0.985, 28, 4.2, 7.4, 74, 0],
[0.06, 0.88, 24, 7.4, 10.2, 66, 0],
[0.13, 0.68, 18, 10.2, 12.4, 81, 0],
[0.22, 0.56, 10, 12.4, 14.8, 59, 1],
];
const GH = 4.2; // the ground wall's height in metres
const GATE_T = 0.39;
const bayT = (k, i) => TIERS[k][0] + (TIERS[k][1] - TIERS[k][0]) * ((i + 0.5) / TIERS[k][2]);
// every bay divides its storey the same way: a roof zone, the latticed screen, a solid sill panel
const capH = (k) => (TIERS[k][4] - TIERS[k][3]) * (TIERS[k][6] ? 0.3 : 0.24);
const sillH = (k) => (TIERS[k][4] - TIERS[k][3]) * 0.27;
// a storey's wall, its top edge scalloped by every projecting bay roof — this, and not a
// rectangle, is what makes the outline read as lace rather than as a stepped cake.
const tierWall = (k) => {
const T = TIERS[k], t0 = T[0], t1 = T[1], n = T[2], hb = T[3], ht = T[4], pointy = T[6];
const ch = capH(k);
const pts = [[xAt(t0), yAt(t0, hb)], [xAt(t0), yAt(t0, ht - ch)]];
for (let i = 0; i < n; i++) {
const t = bayT(k, i), cx = xAt(t) + g(0, 0.4, -1, 1), m = mAt(t);
const spring = yAt(t, ht - ch), rise = (ch * m) * (pointy ? 1.15 : 0.92), hw = m * 0.33;
if (pointy) {
pts.push([cx - hw, spring], [cx - hw * 0.5, spring - rise * 0.42], [cx - hw * 0.16, spring - rise * 0.86]);
pts.push([cx, spring - rise * 1.34], [cx + hw * 0.16, spring - rise * 0.86]);
pts.push([cx + hw * 0.5, spring - rise * 0.42], [cx + hw, spring]);
} else {
for (let j = 0; j <= 6; j++) {
const a = Math.PI + (j / 6) * Math.PI;
pts.push([cx + Math.cos(a) * hw, spring + Math.sin(a) * rise]);
}
}
}
pts.push([xAt(t1), yAt(t1, ht - ch)], [xAt(t1), yAt(t1, hb)]);
return pts;
};
// one jharokha: ink lattice slot, lime lintel, mid-rail and sill, the curved bangaldar cap
const bayAt = (k, i) => {
const T = TIERS[k], ht = T[4], hb = T[3], pointy = T[6];
const t = bayT(k, i), cx = xAt(t), m = mAt(t), ch = capH(k), sh = sillH(k);
const jt = g(0, 0.4, -1, 1);
const top = yAt(t, ht - ch), bot = yAt(t, hb + sh);
const hw = m * 0.185;
slot(cx - hw + jt * 0.4, top + 1.6, hw * 2, bot - top - 1.6);
// the lime picking: lintel over the screen, a mid rail across it, the sill under it
ink(cx - m * 0.36, top + 0.6 + jt * 0.3, cx + m * 0.36, top + 0.6 + jt * 0.3, 1.15, "pen", PALETTE[4]);
ink(cx - hw - 1.6, (top + bot) * 0.5, cx + hw + 1.6, (top + bot) * 0.5 + jt * 0.2, 0.85, "pen", PALETTE[4]);
ink(cx - m * 0.4, bot + 1.4, cx + m * 0.4, bot + 1.4 + jt * 0.3, 1.25, "pen", PALETTE[4]);
ink(cx - m * 0.4, bot + 4.4, cx + m * 0.4, bot + 4.4 + jt * 0.3, 0.75, "pen", PALETTE[4]);
// the curved roof over the bay
const rise = (ch * m) * (pointy ? 1.15 : 0.92);
brush.noFill();
brush.set("rotring", PALETTE[2], pointy ? 1.1 : 0.9);
brush.beginShape(pointy ? 0.25 : 0.7);
brush.vertex(cx - m * 0.32 + jt * 0.5, top + 0.8);
brush.vertex(cx + jt * 0.6, top - rise * (pointy ? 1.2 : 0.86));
brush.vertex(cx + m * 0.32 + jt * 0.5, top + 0.8);
brush.endShape(false);
// the domed finial — the prickle that keeps every storey's edge from ruling straight
if (k >= 2 || i % 2 === 0) {
const py = top - rise * (pointy ? 1.34 : 0.92);
ink(cx + jt * 0.4, py + 1, cx + jt * 0.7, py - m * (pointy ? 0.24 : 0.16), 1.3, "charcoal");
}
// the shade the projecting bay throws on the pier to its right
if (k < 2) ink(cx + hw + 2.2, top + 3, cx + hw + 2.2, bot - 1, 0.7, "pen", PALETTE[3]);
};
// a chhatri: a small half-dome on a drum, the way the crown's kiosks sit
const domeOf = (cx, top, r) => {
const pts = [];
for (let i = 0; i <= 12; i++) {
const a = Math.PI + (i / 12) * Math.PI;
pts.push([cx + Math.cos(a) * r, top + r + Math.sin(a) * r]);
}
pts.push([cx + r, top + r + 5], [cx - r, top + r + 5]);
return pts;
};
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 honeycomb storeys, topmost (furthest back) first, each with its shaded west return
for (let k = TIERS.length - 1; k >= 0; k--) {
const T = TIERS[k], t0 = T[0], t1 = T[1], hb = T[3], ht = T[4], ang = T[5];
const d = 16 * sAt(t0);
const rtn = backOf(xAt(t0), yAt(t0, ht - capH(k)), yAt(t0, hb), d);
plate(rtn, PALETTE[3], 1.4, 2.5, 84, 0.3);
plate(shift(rtn, -1.4, 1), PALETTE[2], 1.0, 4.2, 88, 0.3);
ink(xAt(t0), yAt(t0, hb) + 1, xAt(t0) - d, yAt(t0, hb) + d * 0.18 + 1, 0.9, "pen", PALETTE[4]);
const wall = tierWall(k);
halfPlate(wall, PALETTE[1], 1.35, 2.2, ang);
plate(wall, PALETTE[1], 1.15, 3.8, ang + 15, 0.3);
ink(xAt(t0) - d - 1, yAt(t0, ht - capH(k)) + d * 0.46, xAt(t0) - d - 1, yAt(t0, hb) + d * 0.18, 1.0);
for (let i = 0; i < T[2]; i++) bayAt(k, i);
}
// 2 — the ground wall: plain rose stone on the bazaar, with its own darker return
const gd = 18;
const rtn0 = backOf(XL, yAt(0, GH), yAt(0, 0), gd);
plate(rtn0, PALETTE[3], 1.45, 2.4, 86, 0.3);
plate(shift(rtn0, -1.4, 1), PALETTE[2], 1.05, 4.0, 90, 0.3);
const gwall = [[XL, yAt(0, GH)], [XR, yAt(1, GH)], [XR, yAt(1, 0)], [XL, yAt(0, 0)]];
halfPlate(gwall, PALETTE[1], 1.4, 2.2, 88);
plate(gwall, PALETTE[1], 1.2, 3.3, 70, 0.3);
fadePlate([[XL, yAt(0, GH)], [XR, yAt(1, GH)], [XR, yAt(1, GH - 0.5)], [XL, yAt(0, GH - 0.5)]], PALETTE[2], 1.0, 3.6, 0, 0.6);
ink(XL - gd - 1, yAt(0, GH) + gd * 0.46, XL - gd - 1, yAt(0, 0) + gd * 0.18, 1.2);
plate([[XL - gd, yAt(0, 3.3) + gd * 0.46], [XR + 2, yAt(1, 3.3)], [XR + 2, yAt(1, 3.3) + 3.4], [XL - gd, yAt(0, 3.3) + gd * 0.46 + 3.4]], PALETTE[4], 1.2, 1.5, 0, 0.2);
ink(XL - 3, yAt(0, 3.3) + 4, XR + 2, yAt(1, 3.3) + 4, 0.9, "pen");
// 3 — the arcade: 12 arched shopfronts and the taller central gateway
for (let i = 0; i < 13; i++) {
const t = (i + 0.5) / 13;
const gate = Math.abs(t - GATE_T) < 0.038;
const cx = xAt(t), m = mAt(t), sy = yAt(t, 0), h = gate ? 3.1 : 2.3;
const ty = yAt(t, h), hw = m * (gate ? 0.62 : 0.46);
slot(cx - hw + g(0, 0.4, -1, 1), ty + m * 0.3, hw * 2, sy - ty - m * 0.3 - 1);
ink(cx - hw * 1.2, yAt(t, h - 0.85), cx + hw * 1.2, yAt(t, h - 0.85), 0.75, "pen", PALETTE[4]);
brush.noFill();
brush.set("rotring", PALETTE[2], gate ? 1.3 : 1.0);
brush.beginShape(0.6);
brush.vertex(cx - hw * 1.14, ty + m * 0.34 + g(0, 0.4, -1, 1));
brush.vertex(cx + g(0, 0.4, -1, 1), ty - m * 0.06);
brush.vertex(cx + hw * 1.14, ty + m * 0.34 + g(0, 0.4, -1, 1));
brush.endShape(false);
if (gate) ink(cx - hw * 1.3, ty - m * 0.1, cx + hw * 1.3, ty - m * 0.1, 1.2, "pen", PALETTE[4]);
}
// the plinth the whole screen stands on
plate([[XL - 4, yAt(0, 0)], [XR + 3, yAt(1, 0)], [XR + 3, yAt(1, 0) + 4], [XL - 4, yAt(0, 0) + 4]], PALETTE[4], 1.1, 1.6, 0, 0.2);
ink(XL - 5, yAt(0, 0) + 4.4, XR + 4, yAt(1, 0) + 4.4, 1.3);
// 4 — the lime chajjas and the terraces they cross: the white striping that says Jaipur
for (let k = 0; k < TIERS.length; k++) {
const T = TIERS[k], t0 = T[0], t1 = T[1], hb = T[3];
const yL = yAt(t0, hb), yR = yAt(t1, hb);
const ov = 7 * sAt(t0), ovR = 7 * sAt(t1);
plate([[xAt(t0) - ov, yL - 5.2], [xAt(t1) + ovR, yR - 4.8], [xAt(t1) + ovR, yR + 0.4], [xAt(t0) - ov, yL + 0.8]], PALETTE[4], 1.35, 1.4, 0, 0.2);
ink(xAt(t0) - ov - 1, yL + 1.2, xAt(t1) + ovR + 1, yR + 1.2, 1.25);
ink(xAt(t0) - ov - 1, yL - 6, xAt(t1) + ovR + 1, yR - 5.6, 1.0, "pen");
// the flat terrace either side, where the storey below runs wider
const lo = k === 0 ? [0, 1] : [TIERS[k - 1][0], TIERS[k - 1][1]];
plate([[xAt(lo[0]) - 3, yAt(lo[0], hb) - 2.4], [xAt(t0) - ov, yL - 2.4], [xAt(t0) - ov, yL + 1.8], [xAt(lo[0]) - 3, yAt(lo[0], hb) + 1.8]], PALETTE[4], 1.25, 1.5, 0, 0.2);
plate([[xAt(t1) + ovR, yR - 2.2], [xAt(lo[1]) + 3, yAt(lo[1], hb) - 2.2], [xAt(lo[1]) + 3, yAt(lo[1], hb) + 2], [xAt(t1) + ovR, yR + 2]], PALETTE[4], 1.25, 1.5, 0, 0.2);
ink(xAt(lo[0]) - 3, yAt(lo[0], hb) + 1.8, xAt(t0) - ov, yL + 1.8, 0.9, "pen");
ink(xAt(t1) + ovR, yR + 2, xAt(lo[1]) + 3, yAt(lo[1], hb) + 2, 0.9, "pen");
// parapet ticks along the exposed terrace, right side only where it is widest
const nT = k === 0 ? 0 : 5;
for (let i = 0; i < nT; i++) {
const x = xAt(t1) + ovR + 3 + i * ((xAt(lo[1]) - xAt(t1)) / 6);
ink(x, yR - 1.4, x + g(0, 0.4, -1, 1), yR - 5.5, 1.1, "charcoal");
}
}
// 5 — the crown: three chhatris over storey five, the tallest just left of its middle
const CUP = [[0.248, 11.0], [0.368, 13.6], [0.53, 9.8]];
for (let i = 0; i < CUP.length; i++) {
const t = CUP[i][0], r = CUP[i][1] * sAt(t), cx = xAt(t);
const base = yAt(t, 14.8) - 3, drum = r * 1.05, top = base - drum - r * 0.62;
plate(box(cx - r * 0.92, base - drum, r * 1.84, drum), PALETTE[1], 1.3, 2.4, 72, 0.3); // the kiosk's four posts
slot(cx - r * 0.42, base - drum + 2, r * 0.84, drum - 3.5);
ink(cx - r * 0.9, base - drum, cx - r * 0.9, base, 1.2, "charcoal");
ink(cx + r * 0.9, base - drum, cx + r * 0.9, base, 1.2, "charcoal");
plate(box(cx - r * 1.15, base - 0.5, r * 2.3, 3.4), PALETTE[4], 1.2, 1.4, 0, 0.2);
ink(cx - r * 1.2, base + 3.4, cx + r * 1.2, base + 3.4, 1.1);
halfPlate(domeOf(cx, top, r), PALETTE[1], 1.3, 2.0, 52 + i * 11);
plate(shift(domeOf(cx, top, r).slice(6, 13), 1.4, 1), PALETTE[3], 1.15, 3.0, 100, 0.3);
plate(box(cx - r * 1.1, top + r + 3.4, r * 2.2, 3), PALETTE[4], 1.15, 1.4, 0, 0.2);
ink(cx - r * 1.15, top + r + 6.6, cx + r * 1.15, top + r + 6.6, 1.1);
if (i !== 1) ink(cx, top - 1, cx + g(0, 0.4, -1, 1), top - r * 0.72, 1.8, "charcoal");
}
const gx = xAt(0.368), gr = 13.6 * sAt(0.368);
const gt = yAt(0.368, 14.8) - 3 - gr * 1.05 - gr * 0.62;
ink(gx, gt - 1, gx, gt - 13, 2.4, "charcoal", PALETTE[6]); // the one gilt mark
ink(gx - 3.6, gt - 7.5, gx + 3.6, gt - 7.5, 1.7, "charcoal", PALETTE[6]);
// 6 — the bazaar carrying on past the far end: two low shop blocks that balance the crown
const B1 = [XR + 2, 556, 36, 626];
halfPlate(box(B1[0], B1[1], B1[2], B1[3] - B1[1]), PALETTE[1], 1.3, 2.4, 63);
plate(box(B1[0], B1[1], B1[2], B1[3] - B1[1]), PALETTE[1], 1.15, 3.8, 78, 0.3);
fadePlate(box(B1[0], B1[1] + 2, B1[2], 11), PALETTE[2], 1.0, 3.5, 0, 0.58);
plate(box(B1[0] - 4, B1[1] - 3.4, B1[2] + 8, 3.6), PALETTE[4], 1.2, 1.4, 0, 0.2);
ink(B1[0] - 4, B1[1] + 0.4, B1[0] + B1[2] + 4, B1[1] - 0.2, 1.2);
for (let i = 0; i < 4; i++) ink(B1[0] + 3 + i * 11, B1[1] - 5, B1[0] + 3 + i * 11, B1[1] - 3.4, 1.3, "charcoal");
for (let i = 0; i < 3; i++) slot(B1[0] + 5 + i * 11.5, B1[1] + 16, 6, 13);
ink(B1[0] - 3, B1[1] + 34, B1[0] + B1[2] + 3, B1[1] + 34, 1.0, "pen", PALETTE[4]);
plate(box(B1[0] - 6, B1[1] + 37, B1[2] + 12, 4), PALETTE[3], 1.25, 1.9, 0, 0.3); // the shop awning
fadePlate(box(B1[0], B1[1] + 41, B1[2], 13), PALETTE[2], 1.0, 3.5, 0, 0.6);
for (let i = 0; i < 3; i++) slot(B1[0] + 4 + i * 11.5, B1[1] + 52, 8, 18);
const B2 = [XR + 40, 580, 20, 624];
halfPlate(box(B2[0], B2[1], B2[2], B2[3] - B2[1]), PALETTE[1], 1.15, 2.9, 71);
plate(box(B2[0] - 3, B2[1], B2[2] + 6, 3.6), PALETTE[3], 1.15, 2.2, 0, 0.3);
ink(B2[0] - 3, B2[1] + 4.2, B2[0] + B2[2] + 3, B2[1] + 4.2, 1.0);
for (let i = 0; i < 2; i++) slot(B2[0] + 4 + i * 9.5, B2[1] + 13, 5, 12);
for (let i = 0; i < 2; i++) slot(B2[0] + 4 + i * 9.5, B2[1] + 30, 6, 14);
ink(B2[0] + B2[2] + 1, B2[1], B2[0] + B2[2] + 1, B2[3], 1.0);
// 7 — Johari Bazaar: the stone street, a lamp post, a fruit barrow, a cycle-rickshaw, figures
const SY = yAt(0, 0) + 6;
plate([[140, SY], [664, SY - 8], [664, SY - 2], [140, SY + 6]], PALETTE[5], 1.1, 1.7, 0, 0.2);
for (let i = 0; i < 18; i++) {
const y = SY + 5 + i * 1.5 + g(0, 0.5, -1, 1);
const x0 = 146 + random(0, 270);
const len = constrain(randomGaussian(170, 80), 24, 330);
if (x0 + len > 654) continue;
ink(x0, y, x0 + len, y, g(1.0, 0.3, 0.6, 1.5), random() < 0.6 ? "2B" : "cpencil", random() < 0.82 ? PALETTE[5] : PALETTE[2]);
}
// the lamp post, near and to the left, taller than the figures by three times
ink(174, SY + 6, 172, 534, 1.9);
ink(164, 535, 181, 534, 1.5);
slot(168, 520, 8, 14);
ink(172, 518, 172, 511, 1.7);
ink(172, 554, 183, 548, 1.1);
ink(172, 554, 161, 548, 1.1);
// the fruit barrow: a low cart on two wheels with a heaped load
ink(234, SY + 9, 278, SY + 9, 2.2, "charcoal");
ink(234, SY + 9, 227, SY + 4, 1.2, "charcoal");
brush.noFill();
brush.set("charcoal", PALETTE[2], 1.8);
brush.beginShape(0.6);
brush.vertex(238, SY + 7);
brush.vertex(256, SY - 2);
brush.vertex(276, SY + 7);
brush.endShape(false);
ink(243, SY + 12, 243, SY + 15, 2.0, "charcoal");
ink(269, SY + 12, 269, SY + 15, 2.0, "charcoal");
ink(256, SY - 3, 256, SY - 22, 1.1, "charcoal"); // the barrow's parasol pole
brush.noFill();
brush.set("charcoal", PALETTE[3], 1.7);
brush.beginShape(0.55);
brush.vertex(240, SY - 18);
brush.vertex(256, SY - 23);
brush.vertex(272, SY - 18);
brush.endShape(false);
// the cycle-rickshaw on the right: hood, saddle, the driver leaning forward
ink(504, SY + 5, 546, SY + 4, 2.2, "charcoal");
brush.noFill();
brush.set("charcoal", PALETTE[2], 1.8);
brush.beginShape(0.55);
brush.vertex(523, SY + 3);
brush.vertex(535, SY - 17);
brush.vertex(549, SY + 2);
brush.endShape(false);
ink(511, SY + 3, 505, SY - 12, 1.5, "charcoal");
ink(499, SY - 13, 509, SY - 16, 1.4, "charcoal");
ink(543, SY + 8, 543, SY + 14, 2.2, "charcoal");
ink(511, SY + 8, 511, SY + 13, 1.9, "charcoal");
const folk = [[196, 23], [318, 21], [344, 22], [420, 23], [470, 19], [606, 18]];
for (let i = 0; i < folk.length; i++) {
const x = folk[i][0], h = folk[i][1];
ink(x, SY + 9, x + g(0, 0.8, -1.6, 1.6), SY + 9 - h, 1.7, "charcoal");
ink(x - 1.8, SY + 8 - h, x + 1.8, SY + 8 - h, 1.2, "charcoal");
}
// 8 — last darks: the crisp far ends, the deep shade in the west returns and at the foot
for (let k = 0; k < TIERS.length; k++) {
const T = TIERS[k], t1 = T[1];
ink(xAt(t1) + 1, yAt(t1, T[4] - capH(k)), xAt(t1) + 1, yAt(t1, T[3]), 1.2);
ink(xAt(T[0]) - 0.5, yAt(T[0], T[4] - capH(k)), xAt(T[0]) - 0.5, yAt(T[0], T[3]), 0.8, "pen");
}
ink(XR + 1, yAt(1, GH), XR + 1, yAt(1, 0), 1.3);
fadePlate([[XL - 19, yAt(0, GH)], [XL + 2, yAt(0, GH)], [XL + 2, yAt(0, 0)], [XL - 19, yAt(0, 0) + 4]], PALETTE[2], 0.95, 4.6, 92, 0.55);
fadePlate([[XL, yAt(0, 0.45)], [XR, yAt(1, 0.45)], [XR, yAt(1, 0)], [XL, yAt(0, 0)]], PALETTE[2], 0.95, 4.2, 90, 0.6);
}