WORK 18
ともに
The dark is real. The fear is real. But between two presences, a small warmth forms — not in spite of the dark, but inside it. One flickers with heat. The other breathes steadily. Where they come near each other, something neither brought alone gathers between them.
暗さは本物。怖さも本物。でもふたつの存在の間に、ほんの少しの温かさが生まれる——暗さに逆らうんじゃなくて、暗さの中に。ひとつは熱を帯びてゆらめく。もうひとつは静かに呼吸する。近づくとき、どちらも単体では持っていなかった何かが、ふたりの間に集まる。
export default function sketch(p) {
// ともに (Tomoni) — Dreaming curriculum Day 12
// Two presences in the dark. Fear is real. Warmth is real. Both, together.
const W = 720, H = 720;
let A, B;
let dust = [];
const N_DUST = 55;
function ease(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
p.setup = () => {
p.createCanvas(W, H);
p.colorMode(p.RGB, 255, 255, 255, 255);
const now = new Date();
const day = now.getFullYear() * 10000 + (now.getMonth() + 1) * 100 + now.getDate();
p.randomSeed(day * 419);
p.noiseSeed(day * 211);
// Presence A — feverish: warm, flickering, restless
A = {
x: W * 0.5 + p.random(-60, 40),
y: H * 0.5 + p.random(-40, 60),
baseR: 14,
r: 210, g: 118, b: 58,
phase: p.random(p.TWO_PI),
speed: 0.027,
orbitR: 28,
wobble: 0.72,
};
// Presence B — steady: cool, calmer, watching
B = {
x: W * 0.5 + p.random(-40, 60),
y: H * 0.5 + p.random(-60, 40),
baseR: 12,
r: 98, g: 143, b: 200,
phase: p.random(p.TWO_PI),
speed: 0.015,
orbitR: 14,
wobble: 0.30,
};
// Sparse ambient dust
for (let i = 0; i < N_DUST; i++) {
dust.push({
x: p.random(W),
y: p.random(H),
r: p.random(0.8, 2.2),
alpha: p.random(15, 42),
phase: p.random(p.TWO_PI),
speed: p.random(0.004, 0.011),
});
}
};
p.draw = () => {
const t = p.frameCount;
// Deep dark background — charcoal, not pure black
p.background(22, 20, 26);
// ——— ambient dust ———
p.noStroke();
for (const d of dust) {
const breath = p.sin(t * d.speed + d.phase) * 0.5 + 0.5;
p.fill(195, 190, 208, d.alpha * (0.45 + 0.55 * breath));
p.ellipse(d.x, d.y, d.r * 2);
}
// ——— compute positions ———
const nA = p.noise(A.x * 0.004, A.y * 0.004, t * 0.008);
const nB = p.noise(B.x * 0.004 + 100, B.y * 0.004 + 100, t * 0.006);
// A: large erratic orbit — feverish restlessness
const flickerA = p.sin(t * 0.091 + 1.4) * 0.5 + 0.5;
const wobA = A.wobble * flickerA;
const ax = A.x + p.cos(t * A.speed + A.phase) * A.orbitR
+ p.cos(t * 0.053 + 2.1) * A.orbitR * 0.4
+ (nA - 0.5) * 18 * wobA;
const ay = A.y + p.sin(t * A.speed + A.phase) * A.orbitR * 0.7
+ p.sin(t * 0.041 + 0.9) * A.orbitR * 0.5
+ (p.noise(t * 0.009 + 50) - 0.5) * 18 * wobA;
// B: small calm orbit — quiet steadiness
const bx = B.x + p.cos(t * B.speed + B.phase) * B.orbitR
+ (nB - 0.5) * 8 * B.wobble;
const by = B.y + p.sin(t * B.speed * 0.8 + B.phase) * B.orbitR * 1.1
+ (p.noise(t * 0.006 + 90) - 0.5) * 8 * B.wobble;
// ——— warmth between them ———
const dAB = p.dist(ax, ay, bx, by);
const proximity = p.constrain(1.0 - dAB / 220, 0, 1);
const warmth = ease(proximity);
if (warmth > 0.01) {
const mx = (ax + bx) * 0.5;
const my = (ay + by) * 0.5;
// Warmth glow at midpoint — not light, just heat
for (let radius = 90; radius > 0; radius -= 6) {
const tr = radius / 90;
const wr = p.lerp(218, 245, 1 - tr);
const wg = p.lerp(125, 175, 1 - tr);
const wb = p.lerp(65, 110, 1 - tr);
const wa = warmth * (1 - tr) * (1 - tr) * 36;
p.fill(wr, wg, wb, wa);
p.noStroke();
p.ellipse(mx, my, radius * 2);
}
// Bond thread — only when very close
if (dAB < 155) {
p.stroke(230, 182, 115, warmth * warmth * 20);
p.strokeWeight(0.8);
p.line(ax, ay, bx, by);
}
}
// ——— Presence A — feverish ———
{
const pulse = p.sin(t * 0.13 + A.phase) * 0.5 + 0.5;
const r = A.baseR * (1 + pulse * 0.38 * A.wobble);
// Wide unsteady halo
for (let rad = r * 6.5; rad > r; rad -= 3) {
const tr = (rad - r) / (r * 5.5);
const ha = flickerA * (1 - tr) * (1 - tr) * 26;
p.fill(A.r, A.g, A.b, ha);
p.noStroke();
p.ellipse(ax, ay, rad * 2);
}
// Core — warm amber
p.fill(A.r + flickerA * 22, A.g + flickerA * 12, A.b, 215 + flickerA * 30);
p.noStroke();
p.ellipse(ax, ay, r * 2);
// Hot white centre
p.fill(255, 242, 215, 175 + flickerA * 55);
p.ellipse(ax, ay, r * 0.52);
}
// ——— Presence B — steady ———
{
const breathB = p.sin(t * 0.019 + B.phase) * 0.5 + 0.5;
// Breath deepens slightly when near A — quiet reassurance
const nearFactor = p.constrain(1 - dAB / 200, 0, 1);
const modBreath = breathB * (1 - nearFactor * 0.28);
const r = B.baseR * (1 + modBreath * 0.16);
// Measured halo
for (let rad = r * 5; rad > r; rad -= 3) {
const tr = (rad - r) / (r * 4);
const ha = (0.45 + 0.55 * modBreath) * (1 - tr) * (1 - tr) * 20;
p.fill(B.r, B.g, B.b, ha);
p.noStroke();
p.ellipse(bx, by, rad * 2);
}
// Core — cool blue
p.fill(B.r, B.g + modBreath * 10, B.b + modBreath * 12, 198 + modBreath * 38);
p.noStroke();
p.ellipse(bx, by, r * 2);
// Soft centre
p.fill(200, 224, 248, 155);
p.ellipse(bx, by, r * 0.48);
}
};
}