رفتن به محتواCREATIVE·CODE·ART
1
24
1
1
1
p5.js

Silk Iris

A living iris woven from thousands of threads of light. Move the sliders and watch it change.

مجوز: CC BY-NC-SA 4.0: ریمیکس با ذکر نام، غیرتجاری، با همین مجوز

دیدن کد
// Silk Iris
// Thousands of threads follow a slowly turning noise field. They live between a
// dark pupil and the rim, so the flow gathers into an iris.
// Threads are drawn in colour batches, so it stays smooth on phones.

// Slider values. Missing sliders use the default, and every value is kept inside
// a range where the iris still looks right, whatever a slider is set to.
const SLIDERS = {
  density: { def: 3000, min: 800, max: 6000 },
  flow: { def: 2.4, min: 0.5, max: 6 },
  speed: { def: 1.5, min: 0.2, max: 4 },
  swirl: { def: 0.55, min: 0, max: 1.5 },
};
function value(name) {
  const s = SLIDERS[name];
  const v = typeof params[name] === "number" && isFinite(params[name]) ? params[name] : s.def;
  return Math.min(s.max, Math.max(s.min, v));
}

const BANDS = 32;
const PUPIL = 74;
const RIM = 350;
let threads = [];
let bands = [];
let t = 0;

function setup() {
  createCanvas(800, 800);
  pixelDensity(min(2, displayDensity()));
  background(6, 7, 9);
  // Ember, gold, teal, violet, rose: inner rings to outer rings.
  const stops = [
    color(255, 96, 40),
    color(255, 186, 72),
    color(48, 214, 196),
    color(122, 104, 255),
    color(255, 84, 150),
  ];
  for (let i = 0; i < BANDS; i++) {
    const k = (i / BANDS) * (stops.length - 1);
    const c = lerpColor(stops[floor(k)], stops[min(floor(k) + 1, stops.length - 1)], k % 1);
    // Inner rings are a little brighter, so the eye has a warm core.
    const alpha = (0.42 - (i / BANDS) * 0.18).toFixed(3);
    bands.push({ css: `rgba(${red(c) | 0}, ${green(c) | 0}, ${blue(c) | 0}, ${alpha})`, path: [] });
  }
  spawnAll();
}

function spawnAll() {
  threads = [];
  const count = Math.round(value("density"));
  for (let i = 0; i < count; i++) threads.push(spawn());
}

function spawn() {
  const a = random(TWO_PI);
  // More threads near the pupil, where the warm colours live.
  const r = PUPIL + pow(random(), 1.35) * (RIM - PUPIL);
  return { x: width / 2 + r * cos(a), y: height / 2 + r * sin(a), life: random(80, 260) };
}

function paramsChanged() {
  if (threads.length !== Math.round(value("density"))) spawnAll();
}

function draw() {
  const g = drawingContext;
  // Fade towards black. Every few frames a stronger pass clears the faint residue
  // that a slow fade would otherwise leave behind.
  g.globalCompositeOperation = "source-over";
  g.fillStyle = frameCount % 8 === 0 ? "rgba(6, 7, 9, 0.16)" : "rgba(6, 7, 9, 0.05)";
  g.fillRect(0, 0, width, height);

  const cx = width / 2, cy = height / 2;
  const scale = value("flow") * 0.001;
  const speed = value("speed");
  const swirl = value("swirl");
  for (const b of bands) b.path.length = 0;

  for (const p of threads) {
    const dx = p.x - cx, dy = p.y - cy;
    const d = Math.sqrt(dx * dx + dy * dy);
    const around = Math.atan2(dy, dx);
    // Each thread heads outwards from the centre; the swirl turns that into a spiral
    // and the noise bends it into fibres, like the muscle of an iris.
    const n = noise(p.x * scale, p.y * scale, t);
    const angle = around + HALF_PI * swirl + (n - 0.5) * TWO_PI * 1.6;
    const step = speed * (0.7 + n);
    const nx = p.x + Math.cos(angle) * step, ny = p.y + Math.sin(angle) * step;

    // Colour follows the distance from the pupil, nudged by the noise.
    const ring = (d - PUPIL) / (RIM - PUPIL) + (n - 0.5) * 0.35;
    const k = Math.max(0, Math.min(BANDS - 1, Math.floor(ring * BANDS)));
    bands[k].path.push(p.x, p.y, nx, ny);

    p.x = nx;
    p.y = ny;
    p.life--;
    if (p.life < 0 || d > RIM || d < PUPIL) Object.assign(p, spawn());
  }

  // Additive light: where threads crowd together, they glow.
  g.globalCompositeOperation = "lighter";
  g.lineWidth = 1;
  for (const b of bands) {
    if (b.path.length === 0) continue;
    g.strokeStyle = b.css;
    g.beginPath();
    const s = b.path;
    for (let i = 0; i < s.length; i += 4) {
      g.moveTo(s[i], s[i + 1]);
      g.lineTo(s[i + 2], s[i + 3]);
    }
    g.stroke();
  }
  // A faint golden collarette around the pupil. It builds up over the fading
  // trails, so it's drawn very lightly.
  g.strokeStyle = "rgba(255, 186, 72, 0.009)";
  g.lineWidth = 10;
  g.beginPath();
  g.arc(cx, cy, PUPIL + 4 + 2 * Math.sin(t * 40), 0, TWO_PI);
  g.stroke();

  t += 0.0014 * speed;
}