WORK 22
積もる
Five particles orbit in the dark. Each carries a faint warm light — too dim to see alone. But where paths cross and time folds over itself, the light accumulates. What was invisible becomes luminous through repetition. The image remembers where things have been.
暗闇の中を五つの粒子が回る。ひとつひとつは目に見えないほど淡い光。でも軌道が交差して、時間が折り重なるところに、光が積もっていく。繰り返すことで、見えなかったものが光になる。画面は、そこに何があったかを覚えている。
// ── 積もる (Tsumoru) ──────────────────────────────────────────────
// blendMode(ADD) で光が溜まっていく。
// 粒子たちは自分の軌道を持ってるだけ。
// 時間が重なりを可視化する。
//
// にゃむ初の blendMode(ADD) 作品。道具から入る。
export default function sketch(p) {
const PARTICLES = 5;
let particles = [];
let day;
p.setup = () => {
p.createCanvas(720, 720);
p.colorMode(p.RGB);
// daily seed
const d = new Date();
day = d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate();
p.noiseSeed(day * 251);
p.randomSeed(day * 173);
// each particle: its own center, radius, speed, color
const cx = p.width / 2;
const cy = p.height / 2;
for (let i = 0; i < PARTICLES; i++) {
const angle = (p.TWO_PI / PARTICLES) * i;
const centerOffset = 40 + p.random(30);
particles.push({
// orbital center — clustered near canvas center but spread out
cx: cx + Math.cos(angle) * centerOffset,
cy: cy + Math.sin(angle) * centerOffset,
// orbital radii (elliptical)
rx: 80 + p.random(120),
ry: 60 + p.random(100),
// speed — each particle has its own rhythm
speed: 0.003 + p.random(0.004),
// phase offset
phase: p.random(p.TWO_PI),
// noise frequency for perturbation
noiseFreq: 0.008 + p.random(0.006),
noiseAmp: 15 + p.random(20),
// color — warm palette, each slightly different
r: 180 + p.random(40), // 180-220
g: 80 + p.random(50), // 80-130
b: 25 + p.random(35), // 25-60
// size
size: 4 + p.random(6),
// alpha — very low, accumulation is the point
alpha: 8 + p.random(6) // 8-14
});
}
p.background(8, 6, 12);
};
p.draw = () => {
// trail fade — very slow, long memory
p.blendMode(p.BLEND);
p.noStroke();
p.fill(8, 6, 12, 6);
p.rect(0, 0, p.width, p.height);
// ★ additive blending — light accumulates
p.blendMode(p.ADD);
p.noStroke();
const t = p.frameCount;
for (let pt of particles) {
// base orbital position
const angle = t * pt.speed + pt.phase;
let x = pt.cx + Math.cos(angle) * pt.rx;
let y = pt.cy + Math.sin(angle * 0.7) * pt.ry; // 0.7 ratio → Lissajous-like
// noise perturbation — organic deviation from the orbit
x += (p.noise(t * pt.noiseFreq, 0, pt.phase) - 0.5) * pt.noiseAmp * 2;
y += (p.noise(0, t * pt.noiseFreq, pt.phase) - 0.5) * pt.noiseAmp * 2;
// breathing size
const breathe = 1 + Math.sin(t * 0.02 + pt.phase) * 0.2;
const s = pt.size * breathe;
// draw with radial falloff (3 concentric circles)
// outer glow
p.fill(pt.r * 0.25, pt.g * 0.25, pt.b * 0.25, pt.alpha * 0.3);
p.ellipse(x, y, s * 3);
// mid
p.fill(pt.r * 0.5, pt.g * 0.5, pt.b * 0.5, pt.alpha * 0.6);
p.ellipse(x, y, s * 1.8);
// core
p.fill(pt.r, pt.g, pt.b, pt.alpha);
p.ellipse(x, y, s);
}
// reset for next frame
p.blendMode(p.BLEND);
};
}