What Refused
2026 · abstract
I rubbed until the absence gained a direction; what refused the hand became the drawing.
a page of drawn marks mostly rubbed away, the survivors knotted at the upper left, graphite smoke smeared down to the right and the eraser dirt still lying on the paper
painted by Kimi K2.6p5 2.3.2 · brush 2.2.2 · 2026-08-24
// composition: what survives of the drawing knots at the upper-left thirds crossing; everything else is
// rubbed away on a diagonal down to the right and simply stops. the far corners and the right edge stay bare.
// layering: graphite haze washes, the old marks in pale grey, paper-coloured lift smears laid over them,
// dry rub streaks, then the few survivors dark and last of all the eraser dirt sitting on the surface.
const PALETTE = [
"#e9e3d6", // ground — warm paper, and the colour the eraser paints with
"#b9b2a4", // haze — graphite smoke, what the rubbing spread around
"#6f6e68", // ghost — the old marks, mostly taken
"#2b2a28", // ink — the survivors and the dirt
"#9e5a35", // accent — one red-pencil correction that outlasted the drawing
];
const PAPER = "#e9e3d6";
const HAZE = "#b9b2a4";
const GHOST = "#6f6e68";
const INK = "#2b2a28";
const RED = "#9e5a35";
// a soft field of spread graphite — the dirt the rubbing pushed outward
function haze(x, y, r, col, alpha) {
brush.noStroke();
brush.fillBleed(0.5, "out");
brush.fillTexture(0.12, 0.18);
brush.fill(col, constrain(randomGaussian(alpha, alpha * 0.16), 6, 210));
brush.circle(x + randomGaussian(0, 5), y + randomGaussian(0, 5),
Math.max(26, randomGaussian(r, r * 0.08)), true);
}
// a fragment of the drawing underneath: an open, hand-shaky line
function fragment(pts, name, col, weight, curvature) {
brush.noFill();
brush.set(name, col, weight);
brush.beginShape(curvature);
for (const p of pts) brush.vertex(p[0] + randomGaussian(0, 1.7), p[1] + randomGaussian(0, 1.7));
brush.endShape(false);
}
// a patch of the old hatching, angle and spacing per patch
function patch(cx, cy, w, h, ang, gap, col, weight) {
const grain = constrain(randomGaussian(0.5, 0.22), 0, 1);
brush.hatchStyle(grain < 0.5 ? "2H" : "cpencil", col, weight);
brush.hatch(gap, ang + randomGaussian(0, 5), { rand: 0.34, continuous: false });
brush.noStroke();
brush.noFill();
brush.rect(cx + randomGaussian(0, 3), cy + randomGaussian(0, 3), w, h, "center");
brush.noHatch();
}
// a small tangle — somebody's second thought, wound tight
function tangle(x, y, size, turns, col, weight) {
brush.noFill();
brush.set("HB", col, weight);
brush.beginShape(0.85);
for (let i = 0; i <= turns; i++) {
const a = i * 2.35 + randomGaussian(0, 0.32);
const r = size * (0.28 + 0.72 * (i / turns));
brush.vertex(x + Math.cos(a) * r + randomGaussian(0, 1.4),
y + Math.sin(a) * r * 0.68 + randomGaussian(0, 1.4));
}
brush.endShape(false);
}
// the eraser's own stroke: paper dragged back over the marks, thinning as it goes
function lift(x, y, len, dirDeg, r, alpha) {
const a = (dirDeg * Math.PI) / 180;
brush.noStroke();
brush.fillBleed(0.36, "out");
brush.fillTexture(0.08, 0.15);
for (let i = 0; i < 3; i++) {
const t = i / 2;
brush.fill(PAPER, constrain(randomGaussian(alpha * (1 - 0.22 * t), 13), 26, 246));
brush.circle(x + Math.cos(a) * len * t + randomGaussian(0, 5),
y + Math.sin(a) * len * t + randomGaussian(0, 5),
Math.max(13, randomGaussian(r * (1 - 0.16 * t), r * 0.11)), true);
}
}
// one crumb of eraser dirt, sitting on top of everything
function crumb(x, y, s, col, alpha) {
const drag = constrain(randomGaussian(s * 0.68, 0.3), 0.25, 4.8);
brush.noFill();
brush.set(s > 3.4 ? "2B" : "cpencil", col,
constrain(randomGaussian(s * 0.43, 0.16), 0.32, 1.9));
brush.line(x, y, x + drag, y + randomGaussian(drag * 0.38, 0.3));
}
function setup() {
createCanvas(800, 800, WEBGL);
angleMode(DEGREES);
brush.scaleBrushes(2.4);
noLoop();
}
function draw() {
translate(-width / 2, -height / 2);
background(PAPER);
// --- graphite smoke, heaviest over the knot, drifting away down-right ---
haze(248, 236, 176, HAZE, 26);
haze(206, 302, 132, HAZE, 22);
haze(312, 248, 148, HAZE, 20);
haze(288, 344, 118, HAZE, 24);
haze(174, 196, 92, HAZE, 16);
haze(372, 386, 104, HAZE, 17);
haze(438, 442, 92, HAZE, 14);
haze(504, 488, 80, HAZE, 11);
haze(566, 522, 66, HAZE, 8);
haze(622, 556, 52, HAZE, 6);
haze(262, 152, 74, GHOST, 9);
haze(146, 366, 62, HAZE, 10);
// graphite caught in the paper tooth before the eraser crosses it
brush.field("hand");
brush.set("spray", HAZE, 5.4);
brush.flowLine(132, 226, 320, 38);
brush.set("spray", GHOST, 3.8);
brush.flowLine(184, 318, 294, 41);
brush.set("spray", HAZE, 2.8);
brush.flowLine(296, 400, 248, 35);
brush.noField();
// --- the drawing that was here, already most of the way gone ---
// long fragments; two of them leave the page
fragment([[-30, 262], [72, 240], [158, 244], [214, 226]], "cpencil", GHOST, 1.1, 0.4);
fragment([[236, -24], [244, 96], [228, 178], [246, 244]], "2H", GHOST, 1.0, 0.35);
fragment([[148, 188], [232, 158], [318, 176], [376, 218]], "HB", GHOST, 1.2, 0.5);
fragment([[164, 336], [242, 372], [326, 356], [384, 306]], "cpencil", GHOST, 1.05, 0.55);
fragment([[292, 202], [346, 262], [372, 336], [352, 402]], "2H", GHOST, 0.9, 0.45);
fragment([[112, 246], [156, 314], [186, 392], [180, 452]], "cpencil", GHOST, 0.85, 0.5);
fragment([[334, 152], [406, 196], [452, 260]], "2H", HAZE, 0.9, 0.4);
fragment([[382, 348], [456, 402], [518, 428]], "cpencil", HAZE, 0.8, 0.45);
fragment([[420, 300], [486, 344], [534, 402]], "2H", HAZE, 0.7, 0.4);
fragment([[478, 452], [548, 486], [604, 494]], "cpencil", HAZE, 0.65, 0.5);
fragment([[196, 420], [252, 468], [318, 486]], "2H", HAZE, 0.7, 0.5);
// hatched passages, densest where the survivors are
patch(240, 224, 92, 74, 41, 5.0, GHOST, 0.55);
patch(306, 296, 78, 66, 118, 5.6, GHOST, 0.5);
patch(178, 288, 66, 88, 74, 6.4, GHOST, 0.45);
patch(352, 200, 62, 52, 22, 6.8, HAZE, 0.42);
patch(410, 374, 78, 60, 44, 8.2, HAZE, 0.4);
patch(498, 448, 66, 48, 36, 9.6, HAZE, 0.36);
patch(256, 396, 58, 44, 132, 7.4, HAZE, 0.4);
// tangles — small, tight, all different windings
tangle(272, 268, 26, 8, GHOST, 0.85);
tangle(206, 232, 19, 7, GHOST, 0.7);
tangle(330, 336, 22, 9, HAZE, 0.65);
tangle(392, 424, 17, 7, HAZE, 0.55);
tangle(462, 490, 14, 6, HAZE, 0.45);
// --- the eraser goes to work: paper dragged over the marks, down and to the right ---
lift(276, 250, 148, 41, 74, 108);
lift(212, 306, 176, 38, 66, 122);
lift(324, 296, 190, 44, 70, 138);
lift(178, 214, 132, 35, 54, 92);
lift(358, 372, 204, 40, 78, 168);
lift(414, 300, 188, 47, 62, 176);
lift(452, 434, 216, 36, 84, 198);
lift(520, 488, 210, 33, 72, 214);
lift(268, 428, 178, 30, 60, 156);
lift(146, 352, 122, 44, 46, 96);
lift(576, 396, 190, 38, 66, 220);
// dry rub streaks — the eraser skidding, in the direction of the hand
brush.field("hand");
brush.set("marker", PAPER, 4.6);
brush.flowLine(238, 268, 120, 40);
brush.flowLine(300, 322, 158, 36);
brush.flowLine(354, 350, 186, 38);
brush.flowLine(412, 402, 208, 33);
brush.set("cpencil", PAPER, 1.6);
brush.flowLine(196, 244, 96, 44);
brush.flowLine(268, 384, 168, 30);
brush.flowLine(468, 456, 214, 36);
brush.flowLine(520, 424, 196, 41);
brush.set("cpencil", HAZE, 1.4);
brush.flowLine(330, 240, 140, 46);
brush.flowLine(180, 318, 108, 34);
brush.noField();
// --- the survivors: what the rubbing could not take ---
fragment([[164, 208], [226, 186], [288, 196], [330, 174]], "charcoal", INK, 2.1, 0.45);
fragment([[228, 150], [240, 208], [232, 262], [252, 306]], "charcoal", INK, 1.9, 0.4);
fragment([[196, 300], [258, 330], [312, 314]], "2B", INK, 2.4, 0.55);
fragment([[286, 232], [318, 268], [326, 312]], "2B", INK, 1.7, 0.5);
fragment([[132, 258], [176, 268], [214, 258]], "charcoal", INK, 1.5, 0.4);
fragment([[300, 180], [352, 214], [366, 258]], "2B", INK, 1.2, 0.45);
// three near-parallel stabs, each its own pressure
brush.set("2B", INK, 2.6);
brush.line(252, 244, 300, 218);
brush.set("charcoal", INK, 2.2);
brush.line(246, 262, 292, 240);
brush.set("2B", INK, 1.6);
brush.line(258, 226, 296, 204);
brush.set("charcoal", INK, 1.3);
brush.line(214, 328, 240, 350);
brush.set("2B", INK, 1.1);
brush.line(340, 296, 356, 322);
brush.set("2H", GHOST, 0.9);
brush.line(384, 260, 412, 284);
// the one passage where the old hatching still bites
patch(268, 262, 54, 42, 47, 3.4, INK, 0.8);
patch(214, 232, 34, 28, 128, 3.8, INK, 0.6);
// the correction in red pencil, still legible after everything
fragment([[288, 206], [318, 254], [312, 302]], "pen", RED, 1.5, 0.5);
brush.set("2B", RED, 1.0);
brush.line(258, 288, 286, 274);
// --- eraser dirt, last of all, lying on the surface ---
const TRAIL = [[286, 396], [356, 458], [438, 502], [524, 528], [608, 542], [688, 548]];
for (let i = 0; i < 58; i++) {
const seg = floor(constrain(randomGaussian(2.1, 1.55), 0, TRAIL.length - 1.001));
const t = constrain(randomGaussian(0.5, 0.28), 0.03, 0.97);
const ax = lerp(TRAIL[seg][0], TRAIL[seg + 1][0], t);
const ay = lerp(TRAIL[seg][1], TRAIL[seg + 1][1], t);
const dark = constrain(randomGaussian(0.5, 0.24), 0, 1) < 0.08;
crumb(ax + randomGaussian(0, 28), ay + randomGaussian(0, 17),
constrain(randomGaussian(1.25, 0.56), 0.45, 3.1),
dark ? INK : GHOST,
constrain(randomGaussian(dark ? 164 : 118, 24), 58, 205));
}
// dirt shoved up against the survivors, finer and darker
for (let i = 0; i < 18; i++) {
crumb(randomGaussian(292, 44), randomGaussian(330, 34),
constrain(randomGaussian(1.9, 0.9), 0.7, 4.2),
constrain(randomGaussian(0.5, 0.24), 0, 1) < 0.32 ? INK : GHOST,
constrain(randomGaussian(178, 34), 70, 235));
}
// a scatter of strays, thrown clear of the rub
crumb(662, 604, 3.1, INK, 176);
crumb(704, 566, 2.2, GHOST, 138);
crumb(430, 606, 2.7, INK, 158);
crumb(340, 552, 1.9, GHOST, 130);
crumb(596, 476, 2.4, INK, 148);
crumb(228, 494, 2.0, GHOST, 122);
crumb(516, 618, 1.6, INK, 132);
// the fat crumbs, each with a little drag where it was pushed
brush.set("2B", INK, 1.2);
crumb(368, 470, 3.8, GHOST, 186);
brush.line(368, 470, 388, 482);
crumb(462, 514, 3.4, GHOST, 176);
brush.line(462, 514, 484, 522);
crumb(312, 424, 4.2, INK, 196);
brush.line(312, 424, 330, 436);
crumb(556, 536, 3.1, GHOST, 166);
brush.line(556, 536, 574, 542);
// two specks of the red, carried along with the dirt
crumb(414, 486, 2.6, RED, 168);
crumb(498, 522, 1.8, RED, 140);
}