salt deciding its own architecture
painted by Mistral Medium 3.1p5 2.3.2 · brush 2.2.2 · 2026-08-24
// composition: crystal shards radiating up-left from a seed at the lower-right, paper beyond the growth front
// layering: pale halo at the seed, large shards, medium, small; hatching on a few; the amber shard late
const PALETTE = [
"#eff1ec", // cold paper
"#aeb9c4", // ice grey
"#7c93a6", // ice blue
"#41505e", // shard shadow
"#cf8a3b", // one amber shard
];
const SEED_PT = { x: 578, y: 612 };
function shard(distance, angle, length, width, col, opacity, hatched) {
const bx = SEED_PT.x + cos(angle) * distance;
const by = SEED_PT.y + sin(angle) * distance;
const tipX = bx + cos(angle + randomGaussian(0, 4)) * length;
const tipY = by + sin(angle + randomGaussian(0, 4)) * length;
const px = cos(angle + 90) * width;
const py = sin(angle + 90) * width;
if (hatched) {
brush.hatchStyle("2H", "#41505e", 0.45);
brush.hatch(3.6, angle + randomGaussian(0, 8), { rand: 0.1 });
brush.noStroke();
brush.noFill();
} else {
brush.noStroke();
brush.fillBleed(0.18, "in");
brush.fillTexture(0.35, 0.45);
brush.fill(col, opacity);
}
brush.beginShape(0.08);
brush.vertex(bx - px, by - py);
brush.vertex(bx + px * randomGaussian(0.9, 0.15), by + py * randomGaussian(0.9, 0.15));
brush.vertex(tipX, tipY);
brush.endShape(true);
if (hatched) brush.noHatch();
// one crisp edge per shard — crystals catch light on a single face
brush.set("pen", "#2d3944", 0.8);
brush.line(bx - px, by - py, tipX, tipY);
}
function setup() {
createCanvas(800, 800, WEBGL);
angleMode(DEGREES);
brush.scaleBrushes(2.3);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background("#eff1ec");
// saline halo where growth began
brush.noStroke();
brush.fillBleed(0.55, "out");
brush.fillTexture(0.6, 0.5);
brush.fill("#aeb9c4", 75);
brush.circle(SEED_PT.x, SEED_PT.y, 96, true);
// three large shards define the reach — long enough to carry up-left
shard(30, -128, 450, 50, "#7c93a6", 150, false);
shard(24, -158, 354, 40, "#aeb9c4", 160, false);
shard(38, -100, 396, 45, "#aeb9c4", 130, true);
// medium growth crowding the seed
shard(20, -142, 150, 18, "#7c93a6", 160, false);
shard(26, -172, 122, 15, "#aeb9c4", 165, false);
shard(30, -86, 138, 16, "#7c93a6", 135, true);
shard(16, -116, 168, 14, "#41505e", 120, false);
shard(34, -62, 96, 12, "#aeb9c4", 150, false);
shard(44, -188, 88, 12, "#aeb9c4", 140, false);
shard(22, -140, 106, 10, "#7c93a6", 170, true);
// the amber shard: mid-size, slightly against the grain — the true accent
shard(40, -118, 142, 13, "#cf8a3b", 200, false);
// small splinters at the frontier, thinning out
for (let i = 0; i < 22; i++) {
const angle = randomGaussian(-125, 26);
const distance = 130 + random(40, 260);
shard(distance, angle, max(14, randomGaussian(34, 12)), max(3, randomGaussian(5, 1.6)),
random() < 0.5 ? "#aeb9c4" : "#7c93a6", 130, false);
}
}