Sheeting Torn Loose
2026 · architecture
The frame is all engineering — every line doing arithmetic. Then one fixing fails, and the only soft thing in the picture takes the wind.
a scaffold lashed to a dun facade, boards laid at odd angles, one corner of the sheeting torn off its rail and flapping free
painted by Grok 4.6p5 2.3.2 · brush 2.2.2 · 2026-08-25
// composition: an irregular scaffold lattice grips the right two-thirds — standards running off top and
// bottom, bays missed, boards laid at odd angles — and the one soft form, sheeting torn loose from its rail,
// flaps at the upper-right thirds crossing. the left third stays bare lit paper; the frame reaches into it.
// layering: dun facade washes and hatched shade first, then the timber, then the red sheeting, then the iron
// line last — standards, ledgers, braces — with the lashings and the grit at the feet on top of everything.
const PALETTE = [
"#eae4d4", // ground — lit paper, the air the frame stands in
"#9c9384", // mass — the dun facade behind the lattice
"#8a6a45", // timber — boards, and the boards' own shadow
"#2a2825", // shadow — iron line, laid last
"#b1402a", // accent — the torn sheet, the only soft thing here
];
function g(mean, sd, lo, hi) {
return constrain(randomGaussian(mean, sd), lo, hi);
}
function blendMix(a, b, t) {
return lerpColor(color(a), color(b), constrain(t, 0, 1));
}
// --- the facade: pale bleeding blobs, thinning towards the bottom-left so the feet stand in light
function wash(x, y, r, col, op, bleed) {
brush.noStroke();
brush.noHatch();
brush.fillBleed(bleed, "out");
brush.fillTexture(g(0.42, 0.1, 0.2, 0.7), 0.4);
brush.fill(col, op);
brush.circle(x, y, r, true);
}
// --- a scaffold tube: a bowed spine plus the thin lit edge held just off it.
// bias pushes the bow along the tube's own normal, so ledgers sag and standards lean.
function tube(x1, y1, x2, y2, w, bias) {
const ax = x2 - x1;
const ay = y2 - y1;
const m = Math.hypot(ax, ay) || 1;
const nx = -ay / m;
const ny = ax / m;
const bow = g(bias, 2.8, bias - 7, bias + 7);
brush.noFill();
brush.noHatch();
brush.set("charcoal", blendMix(PALETTE[3], PALETTE[1], g(0.17, 0.11, 0, 0.46)), w);
brush.beginShape(0.42);
brush.vertex(x1, y1);
brush.vertex(x1 + ax * 0.33 + nx * bow * 0.8, y1 + ay * 0.33 + ny * bow * 0.8);
brush.vertex(x1 + ax * 0.66 + nx * bow, y1 + ay * 0.66 + ny * bow);
brush.vertex(x2, y2);
brush.endShape(false);
const off = -(w * 1.6 + g(1.3, 0.5, 0.4, 2.8));
brush.set("2H", blendMix(PALETTE[3], PALETTE[0], g(0.44, 0.1, 0.2, 0.68)), w * 0.5);
brush.beginShape(0.42);
brush.vertex(x1 + nx * off, y1 + ny * off);
brush.vertex(x1 + ax * 0.5 + nx * (off + bow), y1 + ay * 0.5 + ny * (off + bow));
brush.vertex(x2 + nx * off, y2 + ny * off);
brush.endShape(false);
}
// --- a board: a slab of timber with a dark rule under it for thickness
function board(x1, y1, x2, y2, th) {
const ax = x2 - x1;
const ay = y2 - y1;
const m = Math.hypot(ax, ay) || 1;
const nx = -ay / m;
const ny = ax / m;
const h = th / 2;
brush.noStroke();
brush.noHatch();
brush.fillBleed(g(0.2, 0.06, 0.08, 0.36), "out");
brush.fillTexture(g(0.6, 0.11, 0.35, 0.85), 0.55);
brush.fill(blendMix(PALETTE[2], PALETTE[1], g(0.3, 0.18, 0, 0.72)), g(118, 20, 74, 162));
brush.beginShape(0.06);
brush.vertex(x1 + nx * h * 1.1, y1 + ny * h * 1.1);
brush.vertex(x2 + nx * h * 0.82, y2 + ny * h * 0.82);
brush.vertex(x2 - nx * h, y2 - ny * h);
brush.vertex(x1 - nx * h * 1.15, y1 - ny * h * 1.15);
brush.endShape(true);
brush.noFill();
brush.set("HB", blendMix(PALETTE[3], PALETTE[2], g(0.3, 0.13, 0.04, 0.62)), g(1.15, 0.25, 0.6, 1.8));
brush.line(
x1 + nx * h + g(0, 2, -4, 4), y1 + ny * h,
x2 + nx * h, y2 + ny * h + g(0, 2, -4, 4),
);
}
// --- a lashing: the knot of wire at a joint, two or three crossed ticks over a dark bead
function lashing(x, y, tilt) {
brush.noStroke();
brush.noHatch();
brush.fillBleed(0.18, "out");
brush.fillTexture(0.5, 0.6);
brush.fill(blendMix(PALETTE[3], PALETTE[1], g(0.2, 0.11, 0, 0.5)), g(154, 22, 100, 205));
brush.circle(x + g(0, 1.6, -4, 4), y + g(0, 1.6, -4, 4), g(5.4, 1.1, 3.4, 8.4), true);
brush.noFill();
brush.set("pen", blendMix(PALETTE[3], PALETTE[1], g(0.1, 0.08, 0, 0.3)), g(1.15, 0.22, 0.7, 1.7));
const n = 2 + floor(random(0, 2.4));
for (let k = 0; k < n; k++) {
const a = tilt + g(0, 0.2, -0.5, 0.5) + (k - 1) * 0.62;
const len = g(11, 2.4, 6, 18);
brush.line(
x - Math.cos(a) * len + g(0, 1.2, -3, 3), y - Math.sin(a) * len,
x + Math.cos(a) * len, y + Math.sin(a) * len + g(0, 1.2, -3, 3),
);
}
}
// --- one pass of sheeting: a soft, bleeding, textured plane with no edge of its own
function sheetPass(pts, op, bleed, curve, lit) {
brush.noStroke();
brush.noHatch();
brush.fillBleed(bleed, "out");
brush.fillTexture(g(0.5, 0.1, 0.28, 0.78), 0.35);
const col = lit
? blendMix(PALETTE[4], PALETTE[0], g(0.34, 0.08, 0.14, 0.52))
: blendMix(PALETTE[4], PALETTE[3], g(0.15, 0.09, 0, 0.4));
brush.fill(col, op);
brush.beginShape(curve);
for (const p of pts) brush.vertex(p[0] + g(0, 2.6, -7, 7), p[1] + g(0, 2.6, -7, 7));
brush.endShape(true);
}
// the sheeting: lashed along the rail at y 240, failed at x 533 where the corner has peeled back.
// two planes — the taut upper stretch, and the lobe of slack hanging below it
const SHEET_TAUT = [
[560, 246], [646, 234], [726, 244], [812, 258],
[816, 336], [720, 330], [634, 348], [572, 330],
];
const SHEET_SAG = [
[576, 318], [656, 336], [740, 322], [818, 340],
[816, 408], [736, 392], [662, 430], [606, 392],
];
const SHEET_FLAP = [
[546, 258], [520, 212], [486, 176], [450, 190],
[464, 234], [500, 270], [534, 284],
];
const TATTER_A = [[616, 424], [630, 470], [614, 508], [604, 460]];
const TATTER_B = [[702, 392], [714, 444], [700, 456], [692, 412]];
// [x1, y1, x2, y2, weight, bow bias] — standards, then ledgers, then braces
const STANDARDS = [
[312, -24, 298, 706, 2.5, 0],
[406, -24, 418, 824, 2.9, -1.5],
[518, -24, 506, 772, 3.1, 2],
[634, 34, 646, 824, 2.7, -2],
[752, -24, 744, 824, 2.4, 1.5],
];
const LEDGERS = [
[286, 100, 818, 88, 2.3, 3.5],
[376, 240, 818, 232, 2.6, 4],
[258, 368, 704, 382, 2.2, 3],
[296, 512, 818, 500, 2.5, 4.5],
[392, 664, 818, 674, 2.1, 3],
[600, 156, 790, 148, 1.9, 2.5],
[500, 596, 652, 606, 1.8, 2],
];
const BRACES = [
[300, 700, 520, 372, 2.0, 0],
[512, 512, 750, 240, 2.2, -1],
[410, 240, 306, 102, 1.7, 1],
[640, 668, 818, 516, 1.9, 0],
[748, 372, 636, 108, 1.8, -1],
[404, 512, 516, 664, 1.6, 1],
];
// [x, y, tilt] — where iron crosses iron
const JOINTS = [
[310, 100, 0.75], [404, 240, -0.7], [516, 240, 0.8], [636, 240, -0.85],
[750, 236, 0.7], [300, 512, -0.75], [412, 512, 0.8], [508, 512, -0.7],
[642, 508, 0.85], [746, 504, -0.8], [416, 666, 0.7], [508, 660, -0.75],
[648, 670, 0.8], [746, 672, -0.7], [516, 370, 0.78], [410, 370, -0.82],
[636, 156, 0.7], [750, 152, -0.75], [302, 370, 0.8],
];
function setup() {
createCanvas(800, 800, WEBGL);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background(PALETTE[0]);
// the facade behind the frame — heaviest upper-right, evaporating towards the lower-left
for (let gy = 0; gy < 6; gy++) {
for (let gx = 0; gx < 5; gx++) {
const x = 340 + gx * 118 + g(0, 22, -46, 46);
const y = 30 + gy * 126 + g(0, 24, -50, 50);
const fade = constrain(map(x, 300, 780, 0.28, 1) * map(y, 80, 700, 1.05, 0.2), 0, 1.1);
if (random() > fade * 0.86) continue;
wash(x, y, g(54, 8, 38, 70), blendMix(PALETTE[1], PALETTE[0], g(0.3, 0.16, 0, 0.7)), g(58, 12, 26, 88) * fade, g(0.36, 0.06, 0.22, 0.48));
}
}
// three larger gestures: the wall mass behind the sheeting, and its turn into shade at the right
wash(690, 208, 92, blendMix(PALETTE[1], PALETTE[0], 0.18), 50, 0.4);
wash(778, 436, 86, blendMix(PALETTE[1], PALETTE[3], 0.14), 44, 0.4);
wash(468, 132, 78, blendMix(PALETTE[1], PALETTE[0], 0.4), 34, 0.42);
// stone joints raked by low light: two hatched panels, one tall at the right, one low and faint
brush.noFill();
brush.noStroke();
brush.hatchStyle("2H", blendMix(PALETTE[1], PALETTE[3], 0.3), 0.5);
brush.hatch(16, 74, { rand: 0.16, continuous: false, gradient: 0.55 });
brush.polygon([[664, 62], [812, 44], [818, 498], [672, 470]]);
brush.hatchStyle("2H", blendMix(PALETTE[1], PALETTE[0], 0.24), 0.42);
brush.hatch(21, 68, { rand: 0.2, continuous: false, gradient: 0.7 });
brush.polygon([[430, 496], [688, 470], [700, 636], [446, 620]]);
brush.noHatch();
// a shallow pool of shade where the frame meets the ground
wash(346, 712, 62, blendMix(PALETTE[1], PALETTE[3], 0.24), 52, 0.42);
wash(268, 728, 42, blendMix(PALETTE[1], PALETTE[0], 0.2), 34, 0.44);
// the timber: boards laid across the ledgers, none of them square to anything
board(330, 520, 528, 506, 14);
board(474, 372, 700, 388, 16);
board(556, 92, 802, 108, 12);
board(432, 648, 566, 496, 11);
board(416, 672, 566, 656, 12);
board(352, 236, 476, 256, 13);
board(600, 168, 764, 136, 10);
// the sheeting, sagging off its rail: wet pass first, then a tighter one on top of it
sheetPass(SHEET_TAUT, 66, 0.4, 0.38, false);
sheetPass(SHEET_TAUT, 92, 0.26, 0.32, false);
sheetPass(SHEET_SAG, 74, 0.4, 0.44, false);
sheetPass(SHEET_SAG, 88, 0.24, 0.38, false);
sheetPass(TATTER_A, 92, 0.42, 0.5, false);
sheetPass(TATTER_B, 78, 0.42, 0.5, false);
// folds running with the sag, drawn into the wet
brush.noFill();
brush.set("cpencil", blendMix(PALETTE[4], PALETTE[3], 0.34), 1.5);
brush.beginShape(0.55);
brush.vertex(600, 250);
brush.vertex(628, 322);
brush.vertex(646, 414);
brush.endShape(false);
brush.set("cpencil", blendMix(PALETTE[4], PALETTE[3], 0.26), 1.3);
brush.beginShape(0.55);
brush.vertex(700, 242);
brush.vertex(726, 316);
brush.vertex(742, 386);
brush.endShape(false);
brush.set("cpencil", blendMix(PALETTE[4], PALETTE[3], 0.42), 1.1);
brush.beginShape(0.5);
brush.vertex(658, 240);
brush.vertex(674, 330);
brush.vertex(664, 424);
brush.endShape(false);
// the iron, last and hardest: standards, ledgers, braces
for (const t of STANDARDS) tube(t[0], t[1], t[2], t[3], t[4], t[5]);
for (const t of LEDGERS) tube(t[0], t[1], t[2], t[3], t[4], t[5]);
for (const t of BRACES) tube(t[0], t[1], t[2], t[3], t[4], t[5]);
// the torn corner, peeled forward over the frame — the one thing in front of the iron
sheetPass(SHEET_FLAP, 74, 0.4, 0.5, true);
sheetPass(SHEET_FLAP, 108, 0.26, 0.46, true);
brush.noFill();
brush.set("2B", blendMix(PALETTE[4], PALETTE[0], 0.44), 1.4);
brush.beginShape(0.6);
brush.vertex(542, 254);
brush.vertex(512, 214);
brush.vertex(484, 182);
brush.endShape(false);
// the rip itself: a broken line of tatters between the flap and the hanging body
brush.set("2B", blendMix(PALETTE[4], PALETTE[3], 0.52), 1.6);
brush.line(544, 250, 556, 282);
brush.line(558, 288, 550, 310);
brush.line(552, 316, 570, 340);
brush.line(574, 348, 566, 372);
brush.line(570, 380, 596, 396);
brush.set("pen", blendMix(PALETTE[4], PALETTE[3], 0.6), 1.1);
brush.line(556, 284, 574, 268);
brush.line(552, 314, 534, 322);
brush.line(568, 372, 588, 366);
brush.line(482, 180, 468, 164);
// the lashings that still hold, and two that have let go
for (const j of JOINTS) lashing(j[0], j[1], j[2]);
brush.noStroke();
brush.fillBleed(0.2, "out");
brush.fill(blendMix(PALETTE[3], PALETTE[1], 0.3), 168);
brush.circle(534, 246, 4.6, true);
brush.circle(560, 240, 3.4, true);
// base plates, and the grit blown against them
brush.noFill();
brush.set("2B", PALETTE[3], 2.6);
brush.line(276, 704, 322, 700);
brush.set("2B", blendMix(PALETTE[3], PALETTE[1], 0.2), 2.2);
brush.line(484, 768, 528, 764);
brush.set("spray", blendMix(PALETTE[3], PALETTE[1], 0.34), 2.4);
brush.field("hand");
brush.flowLine(304, 716, 76, 8);
brush.flowLine(510, 776, 88, 172);
brush.flowLine(690, 700, 64, 14);
brush.noField();
// three flecks of torn sheet, carried off the frame into the open paper
brush.noStroke();
brush.fillBleed(0.42, "out");
brush.fill(blendMix(PALETTE[4], PALETTE[3], 0.18), 128);
brush.circle(228, 448, 4.2, true);
brush.fill(PALETTE[4], 96);
brush.circle(300, 592, 3.2, true);
brush.fill(blendMix(PALETTE[4], PALETTE[0], 0.2), 112);
brush.circle(178, 358, 2.6, true);
}