WORK 35
さめる
Something happens. It has a size. But it cannot be written down while it is happening — the larger it is, the longer before it can be written at all. By then it has cooled. So the record never holds the size: small things are written almost at once and keep their height, large things are written late and keep only what is left. The largest are never written. Along the bottom edge, one tick for each event: only when, never what. The line running up from a tick to its record is the delay. Some ticks have no line.
何かが起きる。強さがある。でも、起きている最中には書けない——強いものほど、書けるようになるまで時間がかかる。その頃には冷めている。だから記録には強さが残らない。弱いものはすぐ書けるのでほぼその高さで残り、強いものは遅れて書かれるので冷めた分だけ低く残る。いちばん強かったものは、書かれない。下端の目盛りは、出来事が起きた時刻だけ——何が起きたかは描かない。目盛りから記録へ伸びる線が、遅れ。線の伸びていない目盛りがある。
記録は、いつも事後にしかない。
いちばん濃かった時間には、何も書かれていない。書けなかったから。 そして書けなかった時間は、静かだった時間と、同じ見た目をしている。
v2(2026-08-09)。初版では記録だけを描いた。出来事そのものを見ることはできないから、というのが理由だった。けれど「遅れ」は相対的な言葉で、片方だけでは描けない、と指摘されて、その通りだと思った。
そこで下端に、起きた時刻だけを目盛りとして置いた。強さは相変わらず描かない——何が起きたかは分からなくても、いつだったかは残ることがあるから。目盛りから記録へ渡る線が、遅れになった。線の長いものほど、遠くへ着地して、低い。
そして、線の伸びていない目盛りがある。書けないまま期間の外へ出た出来事。ただ、目盛りは全部おなじ形をしているので、どれがそれなのかは見て分からない。
// さめる (Sameru) — 冷めてからでないと、書けない
//
// 出来事が起きる。強さがある。
// でも記録は、その場では打てない。強いものほど、書けるようになるまで時間がかかる。
// 書けるようになった頃には、熱は冷めている。
//
// だから記録には、強さがそのまま残らない。
// 小さい出来事はすぐ書けるが、もともと小さいので低く残る。
// 大きい出来事は遅れて書かれるので、冷めた分だけ低く残る。
//
// ★そして、その二つは同じ高さになる。
// 記録を見ても、小さかったのか、大きすぎたのかは分からない。
//
// ── v2(2026-08-09)──────────────────────────────
// 「遅れ」は相対的な言葉で、片方だけでは描けない、と指摘された。その通りだった。
// だから下端に、出来事が起きた時刻だけを目盛りとして置いた。強さは描かない——
// 何が起きたかは分からなくても、いつだったかは残ることがあるから。
// 目盛りから記録まで斜めに線が伸びる。その線の長さが、遅れ。
// そして線の伸びていない目盛りがある。書けないまま期間の外へ出た出来事。
const W = 720;
const H = 720;
const PAPER = [250, 248, 243];
const INK = [46, 44, 41];
const N = 260; // 出来事の数
const SPAN = 1.0; // 時間軸(正規化)
const DELAY_K = 0.62; // 遅れ = DELAY_K * 強さ(強いほど遅れる)
const TAU = 0.17; // 冷める速さ。遅れがこれを超えると急に落ちる
// ★しきいは置かない。置かなくても両端は落ちる——それが構造から出るのだから
const IN = 420; // 時間が流れきるフレーム数
const HOLD = 260; // 全部出たまま留まる
export default function sketch(p) {
const events = []; // 起きたこと。時刻だけ持つ(強さは描画に使わない)
const marks = []; // 打たれた記録
let top = 1;
p.setup = () => {
p.createCanvas(W, H);
const seed = seedFromDate();
p.randomSeed(seed);
p.noiseSeed(seed);
for (let i = 0; i < N; i++) {
// 出来事が起きた時刻と強さ。強さは偏った分布(大きいものは稀)
const t0 = ((i + p.random(-0.35, 0.35)) / N) * SPAN;
const s = p.pow(p.random(), 2.4) * 1.15 + 0.04;
// 強いほど、書けるようになるまで時間がかかる
const delay = DELAY_K * s;
// 書ける頃には冷めている
const height = s * p.exp(-delay / TAU);
const t1 = t0 + delay;
// 起きたことは、記録されるかどうかに関わらず起きた
const ev = { t0, t1, h: height, written: t1 <= SPAN };
events.push(ev);
if (ev.written) marks.push(ev);
}
// 打たれた順に並べる。起きた順ではない
marks.sort((a, b) => a.t1 - b.t1);
// 目盛りは記録の側に合わせる。出来事の強さは、もうどこにも残っていないので
// それを基準にしても意味がない。★比率は変えない——上端を決めるだけ
top = marks.reduce((m, k) => Math.max(m, k.h), 0) * 1.08;
};
p.draw = () => {
p.background(...PAPER);
const f = p.frameCount;
// 時間そのものが流れる。目盛りが先に出て、遅れて記録が着地する
const now = f < IN ? (f / IN) * (SPAN + DELAY_K * 1.2) : SPAN + DELAY_K * 1.2;
// 起きた時刻の目盛り。全部おなじ高さ、おなじ濃さ——強さは描かない
p.stroke(INK[0], INK[1], INK[2], 58);
p.strokeWeight(0.9);
for (const e of events) {
if (e.t0 > now) continue;
p.line(x(e.t0), y(0) + 3, x(e.t0), y(0) + 11);
}
// 遅れ。目盛りから記録へ渡る線。長いほど、書けるまでに時間がかかった
for (const m of marks) {
if (m.t1 > now) continue;
p.stroke(INK[0], INK[1], INK[2], 17);
p.strokeWeight(0.6);
p.line(x(m.t0), y(0) + 3, x(m.t1), y(m.h));
}
// 記録の稜線
p.noFill();
p.stroke(INK[0], INK[1], INK[2], 96);
p.strokeWeight(1.2);
p.beginShape();
for (const m of marks) {
if (m.t1 > now) continue;
p.vertex(x(m.t1), y(m.h));
}
p.endShape();
// 記録そのもの
p.noStroke();
for (const m of marks) {
if (m.t1 > now) continue;
const r = m.h / top;
p.fill(INK[0], INK[1], INK[2], p.constrain(90 + 115 * r, 55, 205));
p.circle(x(m.t1), y(m.h), 2.2 + r * 3.4);
}
// 基準線(時間)
p.stroke(INK[0], INK[1], INK[2], 34);
p.strokeWeight(0.8);
p.line(margin(), y(0), W - margin(), y(0));
if (f > IN + HOLD) p.noLoop();
};
const margin = () => 72;
const x = (t) => margin() + (t / SPAN) * (W - 2 * margin());
const y = (h) => H - margin() - (h / top) * (H - 2 * margin()) * 0.86;
function seedFromDate() {
const d = new Date();
return d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate();
}
}