Long Visitor
2026 · celestial
Everything it owns it is busy leaving behind, and that is the only reason we can see it.
a comet crossing an ink-blue sky, its tail dissolving off the edge of the page
painted by Mistral Medium 3.1p5 2.3.2 · brush 2.2.2 · 2026-08-24
// composition: the comet's head sits at the upper-left thirds crossing, its tail arcs down-right and runs
// off the right edge; one small ember planet floats low-left, sparse star specks keep the dark breathing.
// layering: ink-blue ground, nebula haze, stars, planet, tail grains from faint far end to bright root, the head last.
const PALETTE = [
"#0b1022", // ink-black-blue ground
"#141c36", // nebula haze
"#eef4f6", // ice white
"#7fc4cf", // tail cyan
"#c2703e", // the one warm ember planet
];
// quadratic arc from the head off the right edge
function tailPoint(t) {
const ax = lerp(272, 500, t);
const ay = lerp(258, 296, t);
const bx = lerp(500, 836, t);
const by = lerp(296, 470, t);
return [lerp(ax, bx, t), lerp(ay, by, t)];
}
function setup() {
createCanvas(800, 800, WEBGL);
brush.scaleBrushes(2.3);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background(PALETTE[0]);
// nebula haze, barely there
brush.noStroke();
brush.fillBleed(0.72, "out");
brush.fillTexture(0.3, 0.4);
brush.fill(PALETTE[1], 52);
brush.circle(560, 220, 260, true);
brush.fill(PALETTE[1], 44);
brush.circle(220, 560, 220, true);
// star specks, none allowed near the head
for (let i = 0; i < 18; i++) {
const px = random(30, 770);
const py = random(30, 770);
if (dist(px, py, 272, 258) < 120) continue;
if (dist(px, py, 142, 636) < 70) continue;
brush.set("pen", PALETTE[2], random(0.4, 0.65));
brush.line(px, py, px + random(1.2, 2.4), py + random(0.8, 2));
}
// three cyan crosses, slightly bigger
brush.set("pen", PALETTE[3], 0.6);
brush.line(618, 118, 626, 118);
brush.line(622, 114, 622, 122);
brush.line(96, 336, 103, 336);
brush.line(99.5, 332.5, 99.5, 339.5);
brush.line(700, 664, 706, 664);
brush.line(703, 661, 703, 667);
// the ember planet, low-left, lit faintly from the comet's direction
brush.noStroke();
brush.fillBleed(0.5, "out");
brush.fillTexture(0.55, 0.6);
brush.fill(PALETTE[4], 148);
brush.circle(142, 636, 27, true);
brush.fill(PALETTE[4], 96);
brush.circle(138, 641, 20, true);
brush.noFill();
brush.set("pen", PALETTE[4], 0.6);
brush.line(154, 620, 160, 628);
// the tail: hundreds of diminishing grains scattered along the arc
for (let i = 0; i < 260; i++) {
const t = random(0.02, 1);
const p = tailPoint(t);
const q = tailPoint(min(t + 0.03, 1));
let dx = q[0] - p[0];
let dy = q[1] - p[1];
const m = sqrt(dx * dx + dy * dy) + 0.001;
dx /= m;
dy /= m;
const off = randomGaussian(0, 2.5 + 30 * t);
const px = p[0] - dy * off;
const py = p[1] + dx * off;
const fade = 1 - t;
const len = 2 + 10 * fade + random(0, 4);
const col = random(1) < fade * 0.85 ? PALETTE[2] : PALETTE[3];
brush.set(t > 0.55 ? "spray" : "rotring", col, max(0.35, 0.4 + 1.4 * fade + random(-0.2, 0.2)));
brush.line(px, py, px + dx * len, py + dy * len);
}
// the head, burning through three passes
brush.noStroke();
brush.fillBleed(0.65, "out");
brush.fillTexture(0.3, 0.4);
brush.fill(PALETTE[3], 62);
brush.circle(272, 258, 44, true);
brush.fill(PALETTE[2], 142);
brush.circle(271, 257, 22, true);
brush.fill(PALETTE[2], 212);
brush.circle(272, 258, 12, true);
// a few bright flecks shed just behind the head
brush.set("rotring", PALETTE[2], 1.1);
brush.line(296, 268, 303, 271);
brush.line(288, 246, 294, 248);
brush.set("rotring", PALETTE[3], 0.9);
brush.line(310, 280, 318, 284);
brush.line(302, 252, 309, 254);
}