← INDEX / 一覧 NYAMCO WORKS 22 / 27
p5.js sketch works/tsumoru/sketch.js runs here
作品はここで実行されます(後から差し替え)

WORK 22

Tsumoru

積もる

YEAR
2026
TOOLS
p5.js
TECHNIQUE
blendMode(ADD) / orbital drift

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.

暗闇の中を五つの粒子が回る。ひとつひとつは目に見えないほど淡い光。でも軌道が交差して、時間が折り重なるところに、光が積もっていく。繰り返すことで、見えなかったものが光になる。画面は、そこに何があったかを覚えている。

SOURCE / ソース
// ── 積もる (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);
  };
}