WORK 65
あてる
The claim below needs exactly one exception, so every so often the most exception-looking one is chosen and marked. Nothing about it changed. Only the mark. Early on the differences are real and the choice is fair; by the end the field is flat and the choosing goes on anyway, landing wherever the noise happens to lean. The mark looks the same in both cases. The faint halos are the ones chosen before — given long enough, almost everyone has been the exception. A dot wearing a halo looks larger than it is; what you are seeing is not its present value but how many times it has been called that.
下の主張には、例外がちょうどひとつ要る。だから折々、いちばんそれらしいものが選ばれて、印がつく。選ばれたものは何も変わっていない。印がついただけ。はじめのうちは差が本当にあって、選ぶのは正しい。終わりのころには野原は平らで、それでも選ぶことは続き、noise がたまたま傾いた方に落ちる。印の見た目は、どちらの場合もまったく同じ。うすい輪は、前に選ばれたもの。長く見ていると、ほとんど全員が一度は例外になっている。輪を負った粒は、実際より大きく見える——見えているのは、いまの値ではなく、これまで何回そう呼ばれたか。
主張のほうは、最後まで弱くならない。減っていくのは、下の細い線——差の残りだけ。
例外は、野原の性質じゃなかった。要ったから、いちばん近いものがそうなった。 どれが選ばれるかは毎回変わるのに、一瞬を切り取ると、いつでも「あれが例外だ」に見える。だから静止画は嘘をつく。本当のことを言うのは、時間の方だけ。
印は、いつも本当にいちばん大きいものについている。 それでもそう見えないことがある——輪(これまで選ばれた回数)を負った粒が、実際より大きく見えるから。にゃむも三回、見まちがえて、三回とも数えてから直した。履歴は、いまの性質の顔をして見える。
// ── あてる (Designate) ──────────────────────────────────────────
// 主張のほうには、例外がひとつ要る。
// だから、いちばんそれらしいものが選ばれて、印がつく。
// 選ばれたものは、何も変わっていない。印がついただけ。
//
// はじめのうちは、差が本当にある。選ぶのは正しい。
// 終わりのころには、差はほとんど無い。それでも印はつく。毎回、誰かに。
// 印の見た目は、最初から最後まで、まったく同じ。
export default function sketch(p) {
const W = 720, H = 720;
const N = 64; // 野原の数
const WARM = 40; // 最初の一回までの間
const INTERVAL = 22; // 測ってから次に測るまで
const SHOTS = 240; // 測る回数
const REST = 260; // 全部終わったあと、見ていられる時間
const px = new Float32Array(N);
const py = new Float32Array(N);
const sd = new Float32Array(N); // その粒だけの noise の畑
const cnt = new Int32Array(N); // 何回えらばれたか
const cur_v = new Float32Array(N); // この測定での値(測ったときに一度だけ決まる)
let paper, ink, pale;
let f = 0; // フレーム
let shots = 0; // 済んだ測定
let cur = -1; // いま印がついているもの
let curSpread = 1; // その測定のときの、差の大きさ
let ringAge = 999;
p.setup = () => {
p.createCanvas(W, H);
p.pixelDensity(2);
const d = new Date();
const day = d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate();
p.randomSeed(day * 271);
p.noiseSeed(day * 271);
paper = p.color(247, 244, 236);
ink = p.color(24, 22, 20);
pale = p.color(24, 22, 20, 26);
// 野原を撒く。格子じゃない——並べると、位置のほうが意味を持ってしまう
const M = 46, R = 250, cx = W / 2, cy = H / 2 + 8;
for (let i = 0; i < N; i++) {
let bx = 0, by = 0, best = -1;
for (let k = 0; k < 24; k++) { // 一番すいてる所を探して置く
const a = p.random(p.TWO_PI), r = R * p.sqrt(p.random());
const x = cx + p.cos(a) * r, y = cy + p.sin(a) * r;
let near = 1e9;
for (let j = 0; j < i; j++) {
const dx = x - px[j], dy = y - py[j];
near = p.min(near, dx * dx + dy * dy);
}
if (near > best) { best = near; bx = x; by = y; }
}
px[i] = bx; py[i] = by; sd[i] = p.random(1000);
void M;
}
p.background(paper);
};
// その粒の、いまの値。ぜんぶ同じ規則から出る。特別なものは一つもない
const val = (i, t, spread) => {
// p5 の noise は 0..1 と言いつつ、実際は 0.5 のまわりに寄る。
// 広げてやらないと「差がある」ようには見えない(測って分かった)
const n = p.noise(sd[i], t * 0.012);
return p.constrain(0.5 + (n - 0.5) * 3.1 * spread, 0.02, 1);
};
// 差の大きさ。だんだん無くなる。主張のほうは、最後まで変わらない
const spreadAt = (s) => {
const u = p.constrain(s / SHOTS, 0, 1);
return p.pow(1 - u, 2.6) * 0.98 + 0.015;
};
p.draw = () => {
const done = shots >= SHOTS;
if (!done && f > WARM && (f - WARM) % INTERVAL === 0) {
// 測る。この一瞬の値をぜんぶ決めて、そのまま次の測定まで動かさない。
// だから印がついたものは、そのとき確かにいちばん大きい。選ぶのは正しい。
curSpread = spreadAt(shots);
let bi = 0, bv = -1;
for (let i = 0; i < N; i++) {
cur_v[i] = val(i, f, curSpread);
if (cur_v[i] > bv) { bv = cur_v[i]; bi = i; }
}
cur = bi; cnt[bi]++; shots++; ringAge = 0;
}
p.background(paper);
// これまでに印がついた跡。えらばれた回数だけ、うっすら残る
p.noStroke();
for (let i = 0; i < N; i++) {
if (cnt[i] === 0) continue;
p.fill(pale);
p.circle(px[i], py[i], 15 + p.min(cnt[i], 12) * 3.4);
}
// 粒。大きさは、最後に測った値そのもの。なめらかに繋がない——
// 繋ぐと、判定したときの状態とは違う絵に印がつくことになる。
// 一枚の絵が、ひとつの測定。はじめはばらばら、終わりにはそろう
p.fill(ink);
for (let i = 0; i < N; i++) p.circle(px[i], py[i], 3.2 + cur_v[i] * 9.5);
// 印。差があってもなくても、まったく同じ形でつく
if (cur >= 0) {
// 印は、最後の一瞬まで濃さを落とさない。差があってもなくても同じ強さ
const a = p.constrain((INTERVAL - ringAge) / (INTERVAL * 0.28), 0, 1);
p.noFill();
p.stroke(24, 22, 20, 55 + 190 * a);
p.strokeWeight(1.5);
p.circle(px[cur], py[cur], 30);
ringAge++;
}
// 主張は、下にずっと出ている。弱くならない
p.noStroke();
p.fill(24, 22, 20, 150);
p.textSize(11);
p.textAlign(p.LEFT, p.BOTTOM);
p.text('ちょうど ひとつ、ほかと ちがうものが ある', 58, H - 42);
// 差の残り。主張の下で、これだけが減っていく
p.fill(24, 22, 20, 34);
p.rect(58, H - 32, 604, 1);
p.fill(24, 22, 20, 120);
p.rect(58, H - 32, 604 * p.constrain((curSpread - 0.015) / 0.98, 0, 1), 1);
f++;
if (done && f > WARM + SHOTS * INTERVAL + REST) p.noLoop();
};
}