WORK 03
往復
Two clusters drift apart. A signal crosses the gap, and the edge it traces grows a little deeper. What reaches toward the other becomes what is given.
二つの群れが離れて漂う。信号がすきまを渡るたび、線は少しだけ深くなる。求めることが、そのまま与えることになる。
export default function sketch(p) {
const N = 6;
const SIGNAL_CHANCE = 0.013;
const SIGNAL_SPEED = 0.012;
const DRIFT_SPEED = 0.002;
const DRIFT_AMP = 14;
let left, right, signals, edges, pulses;
function daySeed() {
const epoch = new Date('2026-01-01');
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
return Math.floor((today - epoch) / 86400000) * 137;
}
p.setup = () => {
p.createCanvas(720, 720);
const seed = daySeed();
p.randomSeed(seed);
p.noiseSeed(seed);
const cx = p.width / 2;
const cy = p.height / 2;
left = Array.from({ length: N }, () => ({
bx: cx - 135 + p.random(-48, 48),
by: cy + p.random(-55, 55),
off: p.random(1000),
}));
right = Array.from({ length: N }, () => ({
bx: cx + 135 + p.random(-48, 48),
by: cy + p.random(-55, 55),
off: p.random(1000),
}));
signals = [];
edges = new Map();
pulses = [];
};
p.draw = () => {
p.background(252, 250, 245, 20);
const f = p.frameCount;
for (const n of [...left, ...right]) {
n.x = n.bx + (p.noise(n.off + f * DRIFT_SPEED, 0.5) - 0.5) * DRIFT_AMP * 2;
n.y = n.by + (p.noise(0.5, n.off + f * DRIFT_SPEED) - 0.5) * DRIFT_AMP * 2;
}
if (p.random() < SIGNAL_CHANCE) {
const goRight = p.random() < 0.5;
const li = p.floor(p.random(N));
const ri = p.floor(p.random(N));
const src = goRight ? left[li] : right[ri];
const dst = goRight ? right[ri] : left[li];
signals.push({
li, ri, goRight, progress: 0,
cpx: (src.x + dst.x) / 2 + p.random(-40, 40),
cpy: (src.y + dst.y) / 2 + p.random(-80, 80),
});
}
p.strokeWeight(0.5);
for (const [nodes, cr, cg, cb] of [[left, 200, 130, 60], [right, 120, 150, 200]]) {
for (let i = 0; i < N; i++) {
for (let j = i + 1; j < N; j++) {
const d = p.dist(nodes[i].x, nodes[i].y, nodes[j].x, nodes[j].y);
if (d < 105) {
p.stroke(cr, cg, cb, p.map(d, 0, 105, 35, 4));
p.line(nodes[i].x, nodes[i].y, nodes[j].x, nodes[j].y);
}
}
}
}
for (const [key, w] of edges) {
const li = Math.floor(key / N);
const ri = key % N;
const s = Math.min(w, 25);
const t = s / 25;
p.stroke(
p.lerp(195, 165, t),
p.lerp(135, 115, t),
p.lerp(90, 185, t),
p.map(s, 0, 25, 10, 145)
);
p.strokeWeight(p.map(s, 0, 25, 0.3, 2.6));
p.line(left[li].x, left[li].y, right[ri].x, right[ri].y);
}
for (let i = signals.length - 1; i >= 0; i--) {
const sig = signals[i];
sig.progress += SIGNAL_SPEED;
const src = sig.goRight ? left[sig.li] : right[sig.ri];
const dst = sig.goRight ? right[sig.ri] : left[sig.li];
const t = sig.progress;
const x = (1 - t) * (1 - t) * src.x + 2 * (1 - t) * t * sig.cpx + t * t * dst.x;
const y = (1 - t) * (1 - t) * src.y + 2 * (1 - t) * t * sig.cpy + t * t * dst.y;
p.noStroke();
p.fill(255, 240, 200, 45);
p.circle(x, y, 16);
p.fill(255, 248, 235, 200);
p.circle(x, y, 5);
if (t >= 1) {
edges.set(sig.li * N + sig.ri, (edges.get(sig.li * N + sig.ri) || 0) + 1);
pulses.push({ x: dst.x, y: dst.y, born: f });
signals.splice(i, 1);
}
}
p.noFill();
for (let i = pulses.length - 1; i >= 0; i--) {
const age = f - pulses[i].born;
if (age > 30) { pulses.splice(i, 1); continue; }
p.stroke(255, 230, 190, p.map(age, 0, 30, 120, 0));
p.strokeWeight(1.2);
p.circle(pulses[i].x, pulses[i].y, p.map(age, 0, 30, 8, 56));
}
p.noStroke();
for (const n of left) {
p.fill(200, 130, 60, 210);
p.circle(n.x, n.y, 9);
}
for (const n of right) {
p.fill(120, 150, 200, 210);
p.circle(n.x, n.y, 9);
}
};
}