WORK 51
とまる
Every point drifts through the same field, at the same speed, by the same amount. What differs is only how often each one looks. A point that looks every frame moves smoothly — smoothly enough to seem not to move at all. A point that looks rarely sits perfectly still, then leaps with everything it saved. Only the ones that leap appear to be alive. Between leaps there is nothing to tell them apart; miss the leap, and a point is simply dead.
ぜんぶの点が、同じ場を、同じ速さで、同じだけ動いている。違うのは、その場をどのくらいの間隔で見にいくかだけ。毎フレーム見にいく点はなめらかに動く——なめらかすぎて、動いていないように見える。間を置く点は完全に止まっていて、時々、溜めた分をまとめて跳ぶ。生きて見えるのは、跳んだ点だけ。跳んでいないあいだ、両者に区別はない。跳ぶところに立ち会わなければ、その点は死んでいる。
一枚の絵として見ると、どの点が毎フレーム見にいっていて、どの点が間を置いているのかは分からない。分からないのは仕様で、静止画には原理的に写らない。
作りながら二度捨てた。はじめは二つの群れを左右に並べて、片方を連続、片方を断続にした。並べた時点で、答えの形を先に描いてしまっていた。次に配置だけ左右で変えた。それでもまだ図解だった。ひとつの場に混ぜて、やっと絵になった。
三度目に、測った。フレーム間で変わった画素数を 320 フレーム分数えたら、中央値 499・最大 843 で、跳ねが一度も立っていなかった。連続の点のドリフトに完全に埋もれていた。静止画をいくら見ても、これは分からない。 振幅を上げて速さを落としたら(この対が要る。たくさん動くのに遅すぎて見えない、という状態を作るために)、中央値 178・跳ねが 319 フレーム中 15 回になった。
/**
* tomaru — とまる / Still
*
* ひとつの群れ。ぜんぶの点が、同じ揺れの中にいる。
* 同じ関数、同じ振幅、同じ速さ。
*
* 違うのは、その揺れを、どのくらいの間隔で見にいくか。
*
* ある点は毎フレーム見る。だからなめらかに動く。
* なめらかだから、動いていることに気づかれない。
*
* ある点は、ずいぶん間を置いてしか見ない。
* だからほとんどの時間、完全に止まっている。
* そして時々、貯めた分をまとめて跳ぶ。
*
* 跳んだ点だけが、生きているように見える。
*
* 動いた量は、みんな同じ。
* 目に入ったかどうかだけが違う。
*
* どの点が速く見にいっていて、どの点が置いているのか、
* 止まっているあいだは、区別がつかない。
* 跳ぶところに立ち会わなければ、それは死んでいる点になる。
*/
const SIZE = 720;
const PAPER = [252, 250, 245];
const INK = [34, 38, 48];
const N = 240; // 点の数
const RX = 262; // 群れの広がり
const RY = 244;
// 振幅は大きく、速さは遅く。この対が要る——
// 連続の点は「たくさん動くのに、遅すぎて動いて見えない」。
// 間を置く点は、その同じ移動を溜めてから、一度に出す。
const AMP = 78; // 揺れの大きさ(全点おなじ)
const SPEED = 0.0016; // 揺れの速さ(全点おなじ)
// 見にいく間隔。1 は毎フレーム=連続。大きいほど、止まっている時間が長い。
// ほとんどの点は間を置いている。連続の点は少数派
const STEPS = [1, 2, 46, 64, 88, 120, 152, 190, 232];
export default function sketch(p) {
const pts = [];
p.setup = () => {
p.createCanvas(SIZE, SIZE);
const day = new Date().getDate();
p.noiseSeed(day * 149);
p.randomSeed(day * 233);
for (let i = 0; i < N; i++) {
const a = p.random(p.TWO_PI);
const r = Math.sqrt(p.random());
pts.push({
x: SIZE / 2 + Math.cos(a) * r * RX,
y: SIZE / 2 + Math.sin(a) * r * RY,
d: (1.9 + p.noise(i * 0.27) * 2.5) * 2,
k: p.random(1000), // 揺れの位相。点ごとに違う
step: STEPS[Math.floor(p.random(STEPS.length))],
// 位相をずらす。跳ぶ点が同時に跳ばないように——揃うと拍になってしまう
off: Math.floor(p.random(232)),
});
}
};
p.draw = () => {
p.background(PAPER);
p.noStroke();
p.fill(INK[0], INK[1], INK[2], 232);
const f = p.frameCount;
for (const pt of pts) {
// その点が最後に見にいった時刻
const seen = pt.step === 1 ? f : Math.floor((f + pt.off) / pt.step) * pt.step - pt.off;
const t = seen * SPEED;
const x = (p.noise(pt.k, t) - 0.5) * AMP;
const y = (p.noise(pt.k + 91.7, t) - 0.5) * AMP;
p.circle(pt.x + x, pt.y + y, pt.d);
}
};
}