San Francisco
2026 · city
I stood on the Marin bluff and made the towers carry their real load: stepped Art Deco crowns, X-braced portals, cable saddles, suspenders and the stiffening truss beneath traffic. International Orange holds the span over the cold ruled water of the Gate.
the golden gate bridge from battery spencer — the near tower tall on the left, the roadway falling toward the city over ruled gate water
painted by Grok 4.6p5 2.3.2 · brush 2.2.2 · 2026-08-30
// composition: a letterpress vignette of the Golden Gate Bridge from Battery Spencer on the Marin
// headlands, looking along the deck toward the city. The near tower stands tall on the left third;
// the roadway falls away to the right to the far tower, a little over half its height; San Francisco
// is a row of pale slots on the far bank. The Marin bluff we stand on holds the lower-left corner and
// the Gate is ruled verdigris water between. No fog and no sky — the print sits on bare cream paper.
//
// four facts that cannot bend:
// 1 proportion — a tower is 227 m tall and the deck 67 m above the water, so the roadway crosses
// each tower at 30% of its height; the legs stand 27.4 m apart, making a tower ~6x taller than
// wide. This is a slender pair of masts, never a squat gateway.
// 2 ordering — each main cable rides OVER the saddle on the tower top and sweeps down until it all
// but touches the deck at mid-span; suspenders hang from cable down to deck and never cross above.
// 3 material — riveted steel in international orange, Art Deco legs that step back as they rise,
// joined by horizontal struts with X-bracing between. Not stone, not white, not a wash.
// 4 site — we stand on the Marin bluff (foreground, lower left); the towers stand in the tidal
// channel of the Gate; San Francisco is a low far bank on the right, not a mountain.
//
// counts drawn: 2 towers x 2 legs; 4 horizontal struts per tower with 3 X-braced bays between them
// and one more brace below the deck; 5 setback steps per near leg; 2 main cables per span (the near
// one strong, the far one a thread); 40 suspenders across the main span (the real 50-ft rhythm is
// ~84 pairs — the print takes every other one), 13 on the left side span, 9 on the right; 44 truss
// panels under the roadway; 9 city slots.
//
// layering: paper flecks, the far bank and its city slots, the Gate's ruled water, the Marin bluff,
// the cables, the suspenders, the deck and its truss, the far tower, then the near tower in orange
// plates with an ink over-plate on its shade leg, its bracing, the last darks and one gilt beacon.
const PALETTE = [
"#efe6d2", // paper — warm cream sheet
"#c2562e", // orange — international orange, the tower plates
"#2f3a44", // ink — slate-navy line and shadow plate
"#5d7f88", // verdigris — the Gate's water, the far bank, the city slots
"#8b3a2f", // oxblood — the shade leg, the cables, the deck truss
"#7a8c6a", // moss — the Marin bluff's scrub
"#d4b483", // sand — the bluff's dry grass, the warm half-plate
"#c9a24a", // gilt — the tower beacon, 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 — 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);
// --- the bridge's measured frame -------------------------------------------------
// NEAR: 227 m over 312 px. deck at 30% of the tower's height, legs 27.4 m apart.
const NEAR = { cx: 212, top: 262, deck: 482, foot: 574, w: 56, leg: 15 };
const FAR = { cx: 556, top: 324, deck: 452, foot: 505, w: 32, leg: 9 };
const SAG = 166; // main-span cable sag: it comes within 5 px of the roadway at mid-span
const deckYAt = (x) => 490 - (x - 118) * 0.08545;
const cableY = (t, y0, y1, sag) => y0 + (y1 - y0) * t + sag * 4 * t * (1 - t);
// leg edges at a height: the legs step inward as they rise, and the tower narrows with them
const legEdge = (T, y, side) => {
const t = constrain((y - T.top) / (T.foot - T.top), 0, 1);
const hw = lerp(T.w * 0.45, T.w * 0.5, t);
const lw = lerp(T.leg * 0.78, T.leg, t);
const outer = T.cx + side * hw;
return { outer, inner: outer - side * lw, lw };
};
const legPts = (T, side, y0, y1) => {
const a = legEdge(T, y0, side), b = legEdge(T, y1, side);
return [[a.outer, y0], [a.inner, y0], [b.inner, y1], [b.outer, y1]];
};
const drawCable = (x0, y0, x1, y1, sag, segs, w, name, col, dx, dy) => {
let px = x0 + dx, py = y0 + dy;
for (let i = 1; i <= segs; i++) {
const t = i / segs;
const x = lerp(x0, x1, t) + dx, y = cableY(t, y0, y1, sag) + dy;
ink(px, py, x, y, w, name, col);
px = x; py = y;
}
};
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[6]);
}
// 1 — the far bank: the San Francisco shore, a pale verdigris half-plate, and the city on it
halfPlate([[558, 480], [682, 468], [686, 502], [552, 510]], PALETTE[3], 1.0, 3.2, 66);
ink(556, 480, 682, 468, 0.8, "pen", PALETTE[3]);
const CITY = [[592, 13], [601, 21], [610, 9], [619, 27], [628, 15], [638, 31], [648, 11], [657, 19], [666, 8]];
for (let i = 0; i < CITY.length; i++) {
const bx = CITY[i][0], h = CITY[i][1];
plate(box(bx, 484 - h, 6, h), PALETTE[3], 1.0, 2.0, 90, 0.25);
}
// one pointed tower in the row so the skyline is a skyline, not a comb
plate([[634, 484 - 31], [638, 484 - 44], [642, 484 - 31]], PALETTE[3], 1.0, 1.8, 90, 0.25);
ink(590, 484, 674, 481, 0.9, "pen", PALETTE[2]);
// 2 — the Gate: ruled water, thinning as it recedes, ending in broken dashes
fadePlate([[200, 558], [684, 486], [692, 524], [214, 626]], 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([[236, 556], [664, 494], [670, 518], [246, 600]]);
brush.noHatch();
for (let i = 0; i < 46; i++) {
const y = 500 + i * 2.7 + g(0, 0.6, -1.4, 1.4);
const x0 = 220 + random(0, 380);
const len = constrain(randomGaussian(120, 66), 16, 330);
if (x0 + len > 706) continue;
if (y > deckYAt(x0) + 190) continue;
ink(x0, y, x0 + len, y, g(1.2, 0.3, 0.7, 1.8), random() < 0.65 ? "2B" : "cpencil", random() < 0.78 ? PALETTE[3] : PALETTE[2]);
}
// the near tower's reflection, orange, broken
for (let i = 0; i < 14; i++) {
const y = NEAR.foot + 3 + i * 2.4;
ink(NEAR.cx - 26 + g(2, 2, -2, 6), y, NEAR.cx + 26 - g(2, 2, -2, 6), y, g(1.1, 0.25, 0.6, 1.6), "2B", PALETTE[1]);
}
// one small boat crossing the channel, unsentimental
ink(458, 552, 480, 551, 2.4, "charcoal");
ink(480, 551, 484, 547, 1.3, "charcoal");
// 3 — the Marin bluff we stand on: a moss/sand half-plate falling away to the right
const BLUFF = [[122, 654], [122, 530], [168, 546], [214, 588], [258, 612], [300, 638], [322, 654]];
halfPlate(BLUFF, PALETTE[6], 1.3, 2.3, 44);
halfPlate(BLUFF, PALETTE[5], 1.25, 2.6, 128);
plate(shift([[122, 654], [122, 596], [176, 606], [232, 630], [286, 648], [322, 654]], 1.5, 1), PALETTE[2], 1.1, 3.4, 38, 0.3);
ink(122, 530, 168, 546, 1.1, "pen"); ink(168, 546, 214, 588, 1.1, "pen");
ink(214, 588, 258, 612, 1.0, "pen"); ink(258, 612, 322, 654, 0.9, "pen");
for (let i = 0; i < 9; i++) {
const x = 134 + i * 20, y = 548 + i * 11 + g(0, 3, -6, 6);
ink(x, y + 5, x + g(0, 1.2, -2.4, 2.4), y - g(7, 2, 4, 11), g(1.7, 0.3, 1.2, 2.3), "charcoal", PALETTE[2]);
}
// 4 — the two main cables per span: over the saddles, down to the deck at mid-span
const NS = NEAR.top + 5, FS = FAR.top + 4;
drawCable(146, 540, NEAR.cx, NS, 20, 22, 1.5, "rotring", PALETTE[4], 0, 0);
drawCable(146, 540, NEAR.cx, NS, 20, 22, 0.8, "pen", PALETTE[4], 3, -4);
drawCable(NEAR.cx, NS, FAR.cx, FS, SAG, 34, 1.7, "rotring", PALETTE[4], 0, 0);
drawCable(NEAR.cx, NS, FAR.cx, FS, SAG, 34, 0.9, "pen", PALETTE[4], 4, -5);
drawCable(NEAR.cx, NS, FAR.cx, FS, SAG, 34, 0.9, "2B", PALETTE[2], 0.6, 1.2);
drawCable(FAR.cx, FS, 658, 468, 12, 18, 1.1, "rotring", PALETTE[4], 0, 0);
drawCable(FAR.cx, FS, 658, 468, 12, 18, 0.6, "pen", PALETTE[4], 2, -3);
// the anchorages the cables die into
plate(box(134, 536, 24, 14), PALETTE[2], 1.2, 1.8, 90, 0.25);
plate(box(648, 466, 18, 11), PALETTE[2], 1.1, 2.0, 90, 0.25);
// 5 — the suspenders: pen 0.7, regular, long at the towers and vanishing at mid-span
for (let i = 0; i < 40; i++) {
const t = (i + 0.5) / 40;
const x = lerp(NEAR.cx, FAR.cx, t);
const y = cableY(t, NS, FS, SAG);
const dy = deckYAt(x);
if (dy - y < 2.5) continue;
ink(x + g(0, 0.3, -0.8, 0.8), y, x + g(0, 0.3, -0.8, 0.8), dy, 0.7, "pen", PALETTE[4]);
}
for (let i = 0; i < 13; i++) {
const t = (i + 0.5) / 13;
const x = lerp(146, NEAR.cx, t);
const y = cableY(t, 540, NS, 20);
const dy = deckYAt(x);
if (dy - y < 2.5) continue;
ink(x + g(0, 0.3, -0.8, 0.8), y, x, dy, 0.7, "pen", PALETTE[4]);
}
for (let i = 0; i < 9; i++) {
const t = (i + 0.5) / 9;
const x = lerp(FAR.cx, 658, t);
const y = cableY(t, FS, 468, 12);
const dy = deckYAt(x);
if (dy - y < 2.5) continue;
ink(x, y, x, dy, 0.6, "pen", PALETTE[4]);
}
// 6 — the deck: a thin plate, the stiffening truss under it as pen ruling
plate([[122, deckYAt(122) - 3.4], [668, deckYAt(668) - 1.4], [668, deckYAt(668) + 0.6], [122, deckYAt(122) + 1.2]],
PALETTE[2], 1.2, 1.6, 0, 0.25);
plate([[122, deckYAt(122) + 1.2], [668, deckYAt(668) + 0.6], [668, deckYAt(668) + 3.4], [122, deckYAt(122) + 8.6]],
PALETTE[4], 1.3, 1.8, 0, 0.28);
ink(122, deckYAt(122) - 3.4, 668, deckYAt(668) - 1.4, 1.2);
ink(122, deckYAt(122) + 8.6, 668, deckYAt(668) + 3.4, 0.8, "pen", PALETTE[2]);
for (let i = 0; i < 44; i++) {
const xa = 122 + i * 12.41, xb = xa + 12.41;
const ta = deckYAt(xa) + 1.2 - (xa - 122) / 546 * 0.6;
const ba = deckYAt(xa) + lerp(8.6, 3.4, (xa - 122) / 546);
const bb = deckYAt(xb) + lerp(8.6, 3.4, (xb - 122) / 546);
const w = lerp(0.8, 0.45, (xa - 122) / 546);
if (i % 2 === 0) ink(xa, ta, xb, bb, w, "pen", PALETTE[4]);
else ink(xa, ba, xb, deckYAt(xb) + 1.2, w, "pen", PALETTE[4]);
}
// 7 — the far tower: the same architecture at a little over half the height
const towerAt = (T, litCol, shadeCol, lineW, name) => {
const litLeg = legPts(T, -1, T.top, T.foot);
const shadeLeg = legPts(T, 1, T.top, T.foot);
halfPlate(litLeg, litCol, 1.35, 2.2, 74);
plate(litLeg, litCol, 1.2, 3.6, 90, 0.3);
plate(shadeLeg, shadeCol, 1.5, 2.2, 90, 0.3);
plate(shift(shadeLeg, 1.5, 1), PALETTE[2], 1.15, 3.0, 92, 0.3);
// four horizontal struts above the deck, X-bracing in the three bays between
const span = T.deck - T.top;
const SY = [T.top + span * 0.14, T.top + span * 0.38, T.top + span * 0.62, T.top + span * 0.86];
const th = T === NEAR ? 7 : 4;
for (let i = 0; i < 4; i++) {
const y = SY[i];
const a = legEdge(T, y, -1), b = legEdge(T, y, 1);
plate([[a.inner - 1, y - th / 2], [b.inner + 1, y - th / 2], [b.inner + 1, y + th / 2], [a.inner - 1, y + th / 2]],
litCol, 1.4, 1.9, 4, 0.28);
ink(a.inner - 1, y + th / 2, b.inner + 1, y + th / 2, T === NEAR ? 1.0 : 0.6, "pen", PALETTE[2]);
}
for (let i = 0; i < 3; i++) {
const y0 = SY[i] + th / 2, y1 = SY[i + 1] - th / 2;
const a0 = legEdge(T, y0, -1), b0 = legEdge(T, y0, 1);
const a1 = legEdge(T, y1, -1), b1 = legEdge(T, y1, 1);
ink(a0.inner, y0, b1.inner, y1, lineW, name, PALETTE[4]);
ink(b0.inner, y0, a1.inner, y1, lineW * 0.9, name, PALETTE[2]);
}
// one brace below the roadway, where the legs run down to the pier
const p0 = T.deck + (T.foot - T.deck) * 0.24, p1 = T.foot - 4;
const c0 = legEdge(T, p0, -1), d0 = legEdge(T, p0, 1);
const c1 = legEdge(T, p1, -1), d1 = legEdge(T, p1, 1);
plate([[c0.inner, p0 - th / 2], [d0.inner, p0 - th / 2], [d0.inner, p0 + th / 2], [c0.inner, p0 + th / 2]],
shadeCol, 1.4, 1.9, 4, 0.28);
ink(c0.inner, p0 + th / 2, d1.inner, p1, lineW, name, PALETTE[4]);
ink(d0.inner, p0 + th / 2, c1.inner, p1, lineW * 0.9, name, PALETTE[2]);
// the Art Deco crown: a stepped cap over each leg
const kt = legEdge(T, T.top, -1), kr = legEdge(T, T.top, 1);
const cap = T === NEAR ? 3 : 1.8;
plate([[kt.outer - cap, T.top - cap * 1.6], [kt.inner + cap, T.top - cap * 1.6], [kt.inner + cap, T.top], [kt.outer - cap, T.top]],
litCol, 1.3, 1.7, 0, 0.25);
plate([[kr.inner - cap, T.top - cap * 1.6], [kr.outer + cap, T.top - cap * 1.6], [kr.outer + cap, T.top], [kr.inner - cap, T.top]],
shadeCol, 1.3, 1.7, 0, 0.25);
// the pier the legs stand on
plate([[T.cx - T.w * 0.58, T.foot], [T.cx + T.w * 0.58, T.foot], [T.cx + T.w * 0.52, T.foot + (T === NEAR ? 7 : 4)],
[T.cx - T.w * 0.52, T.foot + (T === NEAR ? 7 : 4)]], PALETTE[2], 1.2, 1.7, 0, 0.25);
// crisp ends on the mass
ink(legEdge(T, T.top, -1).outer, T.top, legEdge(T, T.foot, -1).outer, T.foot, T === NEAR ? 1.0 : 0.6, "rotring", PALETTE[2]);
ink(legEdge(T, T.top, 1).outer, T.top, legEdge(T, T.foot, 1).outer, T.foot, T === NEAR ? 1.4 : 0.8, "rotring", PALETTE[2]);
};
towerAt(FAR, PALETTE[1], PALETTE[4], 0.65, "pen");
towerAt(NEAR, PALETTE[1], PALETTE[4], 1.15, "rotring");
// 8 — the near tower's setbacks: five steps per leg, ruled where the section changes
for (let i = 0; i < 5; i++) {
const y = NEAR.top + 18 + i * 42;
const a = legEdge(NEAR, y, -1), b = legEdge(NEAR, y, 1);
ink(a.outer - 1.5, y, a.inner + 1, y, 0.85, "pen", PALETTE[2]);
ink(b.inner - 1, y, b.outer + 1.5, y, 0.85, "pen", PALETTE[2]);
}
// the vertical fluting on the near legs — three ribs, not a grate
for (let i = 0; i < 3; i++) {
const f = 0.28 + i * 0.24;
const a0 = legEdge(NEAR, NEAR.top + 6, -1), a1 = legEdge(NEAR, NEAR.deck, -1);
ink(lerp(a0.outer, a0.inner, f), NEAR.top + 6, lerp(a1.outer, a1.inner, f), NEAR.deck, 0.7, "pen", PALETTE[4]);
const b0 = legEdge(NEAR, NEAR.top + 6, 1), b1 = legEdge(NEAR, NEAR.deck, 1);
ink(lerp(b0.outer, b0.inner, f), NEAR.top + 6, lerp(b1.outer, b1.inner, f), NEAR.deck, 0.7, "pen", PALETTE[2]);
}
// the small slot windows in the near legs, the real openings in the steel
for (let i = 0; i < 6; i++) slot(legEdge(NEAR, NEAR.top + 40 + i * 34, -1).outer + 5, NEAR.top + 40 + i * 34, 3, 8);
// 9 — the portal: the roadway passing between the legs, its shadow inside the tower
const pa = legEdge(NEAR, NEAR.deck - 24, -1), pb = legEdge(NEAR, NEAR.deck - 24, 1);
fadePlate([[pa.inner, NEAR.deck - 26], [pb.inner, NEAR.deck - 26], [pb.inner, NEAR.deck - 3], [pa.inner, NEAR.deck - 3]],
PALETTE[2], 1.0, 3.4, 0, 0.7);
ink(pa.inner, NEAR.deck - 26, pa.inner, NEAR.deck + 6, 1.1, "rotring", PALETTE[2]);
ink(pb.inner, NEAR.deck - 26, pb.inner, NEAR.deck + 6, 1.1, "rotring", PALETTE[2]);
// 10 — scale: three vehicles on the roadway as charcoal ticks, and the deep dark at the near foot
ink(268, deckYAt(268) - 1, 277, deckYAt(277) - 1, 2.2, "charcoal");
ink(352, deckYAt(352) - 1, 359, deckYAt(359) - 1, 1.8, "charcoal");
ink(452, deckYAt(452) - 1, 458, deckYAt(458) - 1, 1.5, "charcoal");
fadePlate(box(NEAR.cx - 30, NEAR.foot - 34, 60, 34), PALETTE[2], 1.0, 3.4, 90, 0.7);
// 11 — the accent: the aviation beacon on the near tower, one gilt mark
ink(NEAR.cx - 7, NEAR.top - 6, NEAR.cx - 7, NEAR.top - 11, 2.4, "charcoal", PALETTE[7]);
ink(FAR.cx + 4, FAR.top - 4, FAR.cx + 4, FAR.top - 7, 1.4, "charcoal", PALETTE[7]);
}