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;
}