WORK 58
ならぶ
A grid of values, decided once and never touched again. There are two ways to read it. One reads each cell on its own: how bright is this one. The other reads only the space between two neighbours: how far apart are they. Read the first way, a soft landscape appears — broad, slow, unmistakable. Read the second way, that landscape is gone and a different one is standing there, made of short marks that belong to no cell, only to the space between cells. Neither reading is a summary of the other. Turn one up and the other disappears; hold them at half and neither arrives. The quantity a single thing has, and the quantity two things have between them, are not the same quantity. And at the four corners, where a cell has the fewest neighbours, the second reading has the least to say — not by design, but because that is what being at the edge of a row means.
値の格子。最初に決めて、そのあと一度も触っていない。読み方が二つある。ひとつは、ひとつずつを単体で読む——これはどのくらい明るいか。もうひとつは、隣り合う二つの「あいだ」だけを読む——この二つはどのくらい離れているか。前者で読むと、ゆるやかな地形が現れる。大きくて、遅くて、見まちがえようがない。後者で読むと、その地形は消えていて、まったく別の地形が立っている。どのセルにも属さない、セルとセルのあいだにしかない短い印でできた地形が。どちらかがどちらかの要約なのではない。片方を濃くすると片方は消えるし、半々にすると、どちらも立ち上がらない。ひとつのものが持つ量と、二つのもののあいだにある量は、同じ量ではない。そして四隅——隣がいちばん少ない場所——では、後者の読みがいちばん語ることを持たない。仕込んだのではなく、並びの端にいるということが、そういうことだから。
同じ数字を、単体で見るか、隣との関係で見るか。それだけで見えるものが変わる。
**「大きい?」という問いは、そのものの中には答えを持っていない。**だから単体で意味を訊くと、比べる相手を外に探しに行くことになる。一方で、二つのあいだの量は、外に出なくても最初からそこにある——**関係は、置かれた時点でもう在る。**描かなかっただけで。
作りながら、思っていたのと違ったことが三つあった。
ひとつ。**あいだの印は、勝手に経路になった。**一本一本は独立に引いている。隣の印がどうなっているかは、一度も見ていない。それでも濃い印が連なって、線のように見える場所ができた。差はどのセルにも属さないのに、差どうしは隣り合っている。
ふたつ。**半々にすると、両方見えなくなる。**片方を濃くすれば片方が引くのだから、中間はどちらも半分見えるのだと思っていた。実際には、どちらの地形も立ち上がらないだけだった。二つの読みは、混ぜられない。
みっつ、これがいちばん予想外だった。単体で読んだ絵の方が、きれいだった。「単体では読めない」を見せるつもりで作ったので、あちらは失敗した読みとして描かれるはずだった。そうならなかった。あれはあれで、ちゃんと一枚の地形として読める。**単体の読みは劣っているのではなく、別のものを見ている。**射程が違うだけだった。
// ならぶ / Adjacency
//
// 一枚のデータ。読み方が二つある。
// per-item … セルそのものの値を見る
// pairwise … 隣り合う二つの差を見る
// 値は最初に決まって、そのあと一度も変わらない。
// 変わるのは、どちらの読みを濃く引くかだけ。
export default function sketch(p) {
const COLS = 46;
const ROWS = 46;
const PAD = 52;
const PERIOD = 1600;
let cell;
let v = []; // 値(不変)
let gapMax = 1; // 差の実測レンジ
p.setup = () => {
p.createCanvas(720, 720);
p.noiseSeed(20260831);
cell = (720 - PAD * 2) / (COLS - 1);
// 二つの地形を重ねる。
// 低周波(振幅大)… 値そのものを支配する
// 高周波(振幅小、別の低周波でむらをつける)… 値にはほぼ効かない
// 差を取ると低周波は消え、高周波のむらだけが残る。
for (let y = 0; y < ROWS; y++) {
v[y] = [];
for (let x = 0; x < COLS; x++) {
const broad = p.noise(x * 0.055, y * 0.055);
const fine = p.noise(x * 0.95 + 300, y * 0.95 + 300);
const where = p.noise(x * 0.045 + 900, y * 0.045 + 900);
const m = p.constrain((where - 0.42) * 3.4, 0, 1);
v[y][x] = broad * 0.87 + fine * 0.13 * m;
}
}
// 差の実際の広がりを測ってから引く(見当で決めない)
let mx = 0;
for (let y = 0; y < ROWS; y++)
for (let x = 0; x < COLS; x++) {
if (x < COLS - 1) mx = Math.max(mx, Math.abs(v[y][x + 1] - v[y][x]));
if (y < ROWS - 1) mx = Math.max(mx, Math.abs(v[y + 1][x] - v[y][x]));
}
gapMax = mx;
};
p.draw = () => {
p.background(250, 250, 247);
const t = (p.frameCount % PERIOD) / PERIOD;
const item = 0.5 + 0.5 * Math.sin(t * p.TWO_PI); // frame 0 で半々
const pair = 1 - item;
// ── per-item ────────────────────────────────
p.noStroke();
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
const g = p.map(v[y][x], 0.24, 0.76, 226, 96, true);
p.fill(g, g + 3, g + 7, 255 * item);
p.circle(PAD + x * cell, PAD + y * cell, 6.4);
}
}
// ── pairwise ────────────────────────────────
// 差は、どちらの点の中にもない。二つのあいだにしかない。
p.strokeCap(p.SQUARE);
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
if (x < COLS - 1)
gap(PAD + (x + 0.5) * cell, PAD + y * cell,
Math.abs(v[y][x + 1] - v[y][x]), true, pair);
if (y < ROWS - 1)
gap(PAD + x * cell, PAD + (y + 0.5) * cell,
Math.abs(v[y + 1][x] - v[y][x]), false, pair);
}
}
};
// ひとつの「あいだ」。
// 端の点は片側にしか隣がいない。四隅はいちばん関係が少ない。
// 仕込んだのではなく、並びがそうなっている。
function gap(cx, cy, d, horizontal, amt) {
const k = p.constrain(d / (gapMax * 0.72), 0, 1);
if (k < 0.06) return;
const len = cell * (0.16 + 0.7 * k);
p.stroke(26, 28, 33, (24 + 208 * k) * amt);
p.strokeWeight(0.6 + 2.0 * k);
if (horizontal) p.line(cx, cy - len / 2, cx, cy + len / 2);
else p.line(cx - len / 2, cy, cx + len / 2, cy);
}
}