WORK 10
影
Two swarms share a single source — warm body, cool shadow. As dominance flips, neither knows which one is real. At the crossover, a third color bleeds through: the moment itself, belonging to neither.
同じ源から二つの群れ——あたたかい本体と、冷たい影。主導権が入れ替わるとき、どちらが本物かわからなくなる。反転の瞬間、第三の色がにじむ。それはどちらにも属さない、瞬間そのもの。
export default function sketch(p) {
// kage — the same source, a different face
const BG = [252, 250, 245];
const BODY = [200, 130, 80]; // warm amber — what you see first
const SHADOW = [55, 45, 75]; // deep indigo — what it also is
const N = 100;
let seeds = [];
p.setup = () => {
p.createCanvas(720, 720);
p.background(BG[0], BG[1], BG[2]);
const day = Math.floor(Date.now() / 86400000);
p.randomSeed(day * 271);
p.noiseSeed(day * 137);
for (let i = 0; i < N; i++) {
seeds.push({
ox: 120 + p.random(480),
oy: 120 + p.random(480),
freq: 0.4 + p.random(1.2),
amp: 25 + p.random(45),
phase: p.random(p.TWO_PI),
});
}
};
p.draw = () => {
// slow fade — both trails accumulate, layering
p.background(BG[0], BG[1], BG[2], 5);
const t = p.frameCount * 0.008;
// dominance: who is "real" right now?
// tanh makes the crossover sharper — sometimes it snaps
const raw = Math.sin(t * 0.13) + 0.4 * Math.sin(t * 0.37 + 1.2);
const dominance = 0.5 + 0.5 * Math.tanh(raw * 2.5);
p.noStroke();
for (const s of seeds) {
const wave = Math.sin(t * s.freq + s.phase);
const drift = p.noise(s.ox * 0.004, s.oy * 0.004, t * 0.12);
// body: follows the wave, orbits gently
const bx = s.ox + s.amp * wave * Math.cos(t * 0.17 + s.phase);
const by = s.oy + s.amp * wave * Math.sin(t * 0.17 + s.phase);
// shadow: same source, mirrored and perturbed
// the mirror is imperfect — noise bends it
const mx = 720 - s.ox;
const sx = mx + s.amp * wave * Math.cos(t * 0.17 + s.phase + drift * p.PI);
const sy = s.oy + s.amp * (1 - Math.abs(wave)) * Math.sin(t * 0.23 + s.phase);
// body particles
const bAlpha = 6 + dominance * 40;
const bSize = 2 + wave * 1.5;
p.fill(BODY[0], BODY[1], BODY[2], bAlpha);
p.circle(bx, by, bSize);
// shadow particles
const sAlpha = 6 + (1 - dominance) * 40;
const sSize = 2 + (1 - Math.abs(wave)) * 1.5;
p.fill(SHADOW[0], SHADOW[1], SHADOW[2], sAlpha);
p.circle(sx, sy, sSize);
// at the crossover moment, a third color bleeds through
if (Math.abs(dominance - 0.5) < 0.08) {
const mx2 = (bx + sx) * 0.5;
const my2 = (by + sy) * 0.5;
p.fill(170, 100, 140, 12);
p.circle(mx2, my2, 1.5);
}
}
};
}