WORK 66
なれる
The field never changes. Only the prediction learns. Each mark is drawn in proportion to how wrong the guess was, so the parts that repeat go quiet and the picture keeps only what kept surprising it — which is a portrait of the guess, not of the field.
場は一度も変わらない。学習するのは予測の側だけ。線の濃さは予測の外れ具合そのものなので、繰り返される場所から順に静かになり、最後まで驚かせ続けたものだけが残る。残った絵は、場の姿ではなく、予測の癖の姿をしている。
各点は固定された流れ場の上を進む。場は最初から最後まで一定で、一度も動かない。動くのは各点が持っている「次はこう来るだろう」という速度の見積もりだけで、外れるたびに少しずつ実測へ寄っていく。
描かれるのは、その外れ幅。予測が当たった区間には何も置かれない。だから通っただけでは絵にならず、驚いた場所にしか線が残らない。
結果として、一本一本が彗星の形になった。いちばん明るいのは、その点が場に置かれた直後。二十歩ほどで見積もりが追いつき、あとは何も残さずに進んでいく。場の形は見えているが、それを描いているのは来たばかりのものだけだ。
濃いところは、いちばん何も分かっていないところ。地の黒は、分かってしまった場所。
const N = 160;
const SIZE = 720;
const LEARN = 0.06;
const GAIN = 46;
export default function sketch(p) {
let agents = [];
const flow = (x, y) => {
const a = p.noise(x * 0.0016, y * 0.0016) * p.TWO_PI * 2.4;
return a;
};
p.setup = () => {
p.createCanvas(SIZE, SIZE);
const d = new Date();
p.noiseSeed(d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate());
p.background(9, 9, 11);
p.strokeCap(p.ROUND);
agents = [];
for (let i = 0; i < N; i++) {
const x = p.random(SIZE);
const y = p.random(SIZE);
agents.push({
x, y, px: x, py: y,
vx: 0, vy: 0,
hvx: 0, hvy: 0,
warm: 0,
});
}
};
const step = (a) => {
const ang = flow(a.x, a.y);
a.vx = Math.cos(ang) * 1.5;
a.vy = Math.sin(ang) * 1.5;
const ex = a.vx - a.hvx;
const ey = a.vy - a.hvy;
const err = Math.sqrt(ex * ex + ey * ey);
a.hvx += LEARN * ex;
a.hvy += LEARN * ey;
a.px = a.x;
a.py = a.y;
a.x += a.vx;
a.y += a.vy;
if (a.x < 0 || a.x > SIZE || a.y < 0 || a.y > SIZE) {
a.x = p.random(SIZE);
a.y = p.random(SIZE);
a.px = a.x;
a.py = a.y;
}
return err;
};
p.draw = () => {
for (let k = 0; k < 3; k++) {
for (const a of agents) {
const err = step(a);
const alpha = p.constrain(err * GAIN, 0, 210);
if (alpha < 1) continue;
const warm = p.constrain(err * 300, 0, 70);
p.stroke(216 + warm * 0.5, 220 - warm * 0.9, 228 - warm * 1.6, alpha);
p.strokeWeight(0.55 + err * 1.6);
p.line(a.px, a.py, a.x, a.y);
}
}
if (p.frameCount > 900) p.noLoop();
};
}