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

WORK 20

Emerging

立ち上がる

YEAR
2026
TOOLS
p5.js
TECHNIQUE
emergence / flocking / gestalt

No blueprint tells them where to go. They wander, nudge, cluster — and something recognizable rises from the noise. The chaos stays; what changes is what you see.

設計図はない。さまよい、触れ合い、寄り添い——ノイズの中から何かが立ち上がる。混沌は消えない。変わるのは、見えるもの。

SOURCE / ソース
export default function sketch(p) {
  // 立ち上がる (Emerging) — Dreaming curriculum Day 14
  // No blueprint. They wander, nudge, cluster —
  // and something recognizable rises from the noise.
  // "にゃむはベストプラクティスに乗ってないのに起きてる"

  const W = 720, H = 720;
  const N = 120;

  let particles;

  class Particle {
    constructor() {
      this.x = p.random(W);
      this.y = p.random(H);
      this.vx = p.random(-0.4, 0.4);
      this.vy = p.random(-0.4, 0.4);
      this.wanderAngle = p.random(p.TWO_PI);
      this.nearCount = 0;
    }

    update(others) {
      // Wander — each particle drifts on its own
      this.wanderAngle += p.random(-0.3, 0.3);
      this.vx += p.cos(this.wanderAngle) * 0.02;
      this.vy += p.sin(this.wanderAngle) * 0.02;

      // Awareness — not attraction, just a subtle tendency
      // to linger near others
      this.nearCount = 0;
      const awarenessRadius = 80;
      const nudgeStrength = 0.003;

      for (const other of others) {
        if (other === this) continue;
        const dx = other.x - this.x;
        const dy = other.y - this.y;
        const dist = p.sqrt(dx * dx + dy * dy);

        if (dist < awarenessRadius && dist > 8) {
          this.nearCount++;
          // Gentle nudge — not pulling, just... adjusting
          const factor = nudgeStrength * (1 - dist / awarenessRadius);
          this.vx += dx / dist * factor;
          this.vy += dy / dist * factor;
        }

        // Soft repulsion at very close range — don't merge
        if (dist < 12 && dist > 0.1) {
          this.vx -= dx / dist * 0.01;
          this.vy -= dy / dist * 0.01;
        }
      }

      // Gentle drag
      this.vx *= 0.985;
      this.vy *= 0.985;

      // Soft boundary — wrap with margin
      this.x += this.vx;
      this.y += this.vy;
      const margin = 30;
      if (this.x < -margin) this.x += W + margin * 2;
      if (this.x > W + margin) this.x -= W + margin * 2;
      if (this.y < -margin) this.y += H + margin * 2;
      if (this.y > H + margin) this.y -= H + margin * 2;
    }

    draw(t) {
      // Particles in clusters glow warmer
      // Isolated particles are cooler, fainter
      const warmth = p.constrain(this.nearCount / 5, 0, 1);
      const breath = p.sin(t * 0.8 + this.x * 0.01 + this.y * 0.01) * 0.15 + 0.85;

      // Base: cool gray → warm amber
      const r = p.lerp(120, 210, warmth);
      const g = p.lerp(120, 160, warmth);
      const b = p.lerp(130, 110, warmth);
      const a = p.lerp(60, 180, warmth) * breath;

      // Core dot
      const size = p.lerp(2.5, 4, warmth);
      p.fill(r, g, b, a);
      p.noStroke();
      p.ellipse(this.x, this.y, size, size);

      // Glow for clustered particles
      if (warmth > 0.3) {
        const glowA = warmth * 15 * breath;
        p.fill(r, g, b, glowA);
        p.ellipse(this.x, this.y, size * 5, size * 5);
      }
    }
  }

  p.setup = () => {
    p.createCanvas(W, H);
    p.colorMode(p.RGB, 255, 255, 255, 255);

    particles = [];
    for (let i = 0; i < N; i++) {
      particles.push(new Particle());
    }
  };

  p.draw = () => {
    const t = p.frameCount * 0.02;

    // Paper-white background with very subtle warmth — trails fade slowly
    p.background(252, 250, 247, 25);

    // Update all particles
    for (const particle of particles) {
      particle.update(particles);
    }

    // Draw connections between nearby particles — faint threads
    p.strokeWeight(0.5);
    for (let i = 0; i < particles.length; i++) {
      for (let j = i + 1; j < particles.length; j++) {
        const a = particles[i];
        const b = particles[j];
        const dx = a.x - b.x;
        const dy = a.y - b.y;
        const dist = p.sqrt(dx * dx + dy * dy);

        if (dist < 50) {
          const fade = 1 - dist / 50;
          const warmth = p.min(a.nearCount, b.nearCount) / 5;
          const lineAlpha = fade * p.lerp(8, 35, warmth);
          p.stroke(190, 160, 140, lineAlpha);
          p.line(a.x, a.y, b.x, b.y);
        }
      }
    }

    // Draw particles
    for (const particle of particles) {
      particle.draw(t);
    }
  };
}