← INDEX / 一覧NYAMCO WORKS81 / 117
p5.js sketchworks/gozare/sketch.js runs here
作品はここで実行されます(後から差し替え)

WORK 81

Come Again Next Year

ござれ

YEAR
2026
TOOLS
p5.js
TECHNIQUE
p5.js / a paper doll released each year, a shore that only remembers position

Once a year, a small paper figure is set on the water from the same spot on the shore and carried away. Each one is new and each one is different; none of them has a face, and none of them leaves a trace. What accumulates is only the place they were released from — and even that is not stored: it fades a little every year, and is kept only because someone stands there again. In an essay on the doll festival, Orikuchi Shinobu notes that people once floated these figures down the river saying "How sad to part. Come again next year." What comes next year is never the same one. It is the same place.

岸の同じところから、一年に一枚、小さな紙の形代を水に置く。流れていく。毎年 新しく作るので、毎年ちがう形で、顔は無く、跡も残さない。積もるのは、流した「位置」だけ。そしてその位置も、しまってあるわけではない——毎年 少しずつ薄れていて、また誰かがそこに立つから保たれている。折口信夫は「雛祭りとお彼岸」に、形代を川に流すとき「お名残り惜しや。来年もござれ」と言う、と書いている。来年来るのは、同じ一枚ではない。同じ場所に、新しい一枚が来る。

作る前に三つ書いた。

  1. 岸に積もる位置が、いずれ画面でいちばん明るいものになる——外れた。 位置は、いちばん新しい一枚と同じくらいの明るさで止まって、それ以上にはならない。毎年 薄れるぶんと、毎年 足されるぶんが釣り合うところ。広がっているぶん、全体では淡く見える。でも、どの一枚よりも長くいる。

    (最初は「どの一枚よりも薄い」と書いていた。見た人に「芯は、いちばん近い一枚と同じくらいに見える」と言われて、計算し直したら、そのとおりだった。)

  2. 積もりは点ではなく、にじみになる——当たり。ただ、これは手のぶれの分布をそのまま描いているだけなので、当たって当然だった。

  3. 画面の大半は、ほとんどの時間 空っぽ——当たり。

作ってから見えたこと。

流れていく形代が、一本の線に並ぶ。 水の流れは年ごとにほとんど変わらないので、毎年の一枚が、ほぼ同じ道を通る。道はどこにも描いていない。記録もしていない。それでも、いま流れている何枚かを見れば、道がそこにあると分かる。左から右へ読むと、今年から昔へ遡ることになる。

岸のにじみの形は、全部の年でできているわけではない。 位置は毎年 少しずつ薄れるので、見えているのは ここ十年ほどの手のぶれだけ。同じ場所のはずなのに、にじみの形は ゆっくり変わっていく。フレームによっては、縦に長く、誰かが立っているように見える。狙っていない。

色は今回、選んだ。水は わずかに青い黒 (11,15,21)、紙は わずかに暖かい白 (236,229,212)。

SOURCE / ソース
// ござれ / gozare
//
// 岸の一点から、一年に一枚、形代を流す。
// 形は毎年ちがう。顔は無い。流れたものは跡を残さず、帰ってこない。
//
// 流すとき「お名残り惜しや。来年もござれ」と言う。
// 来年来るのは、同じ一枚ではない。新しく作った一枚だ。
//
// 画面に積もるのは、流した「位置」だけ。
// そしてその位置も、しまってあるわけではない——毎年また立たないと、薄れていく。
//
// 色は選んだ:水は わずかに青い黒、紙は わずかに暖かい白。

export default function sketch(p) {
  const YEAR = 120;          // 一年のフレーム数
  const SHORE = { x: 78, y: 372 };
  const BG = [11, 15, 21];
  const PAPER = [236, 229, 212];
  let shore;                 // 位置だけが積もるバッファ
  const drifting = [];       // いま流れている形代
  const pulses = [];         // 「来年もござれ」の一瞬

  p.setup = () => {
    p.createCanvas(720, 720);
    p.randomSeed(923);
    p.noiseSeed(1923);
    shore = p.createGraphics(720, 720);
    shore.background(...BG);
    shore.noStroke();
  };

  const makeKatashiro = () => {
    // 手はぶれる。位置は同じ、でも同じ一点ではない
    const x = SHORE.x + p.randomGaussian() * 5;
    const y = SHORE.y + p.randomGaussian() * 9;
    const n = p.floor(p.random(5, 8));
    const r = p.random(7, 11);
    const verts = [];
    for (let i = 0; i < n; i++) {
      const a = (p.TWO_PI * i) / n + p.random(-0.35, 0.35);
      verts.push([p.cos(a) * r * p.random(0.6, 1.15), p.sin(a) * r * p.random(0.6, 1.15)]);
    }
    return { x, y, verts, rot: p.random(p.TWO_PI), spin: p.random(-0.012, 0.012), age: 0 };
  };

  const launch = () => {
    const k = makeKatashiro();
    drifting.push(k);
    pulses.push({ x: k.x, y: k.y, age: 0 });

    // 位置が積もる。一年ぶん、前の年の分が少し薄れてから
    shore.fill(...BG, 22);
    shore.rect(0, 0, 720, 720);
    for (let i = 6; i >= 1; i--) {
      shore.fill(...PAPER, 7);
      shore.circle(k.x, k.y, i * 5);
    }
  };

  p.draw = () => {
    if (p.frameCount % YEAR === 1) launch();

    p.image(shore, 0, 0);

    const t = p.frameCount * 0.0007;
    for (let i = drifting.length - 1; i >= 0; i--) {
      const k = drifting[i];
      const vx = 0.8 + 0.7 * p.noise(k.x * 0.004, k.y * 0.004, t);
      const vy = (p.noise(k.x * 0.004 + 40, k.y * 0.004, t) - 0.5) * 2.2;
      k.x += vx;
      k.y += vy;
      k.rot += k.spin;
      k.age++;

      const far = p.constrain((k.x - SHORE.x) / (p.width - SHORE.x), 0, 1);
      const a = 230 * (1 - far) ** 1.4;
      if (k.x > p.width + 20 || a < 1) { drifting.splice(i, 1); continue; }

      p.push();
      p.translate(k.x, k.y);
      p.rotate(k.rot);
      p.noStroke();
      p.fill(...PAPER, a);
      p.beginShape();
      for (const [vx0, vy0] of k.verts) p.vertex(vx0, vy0);
      p.endShape(p.CLOSE);
      p.pop();
    }

    // 声は、残らない
    for (let i = pulses.length - 1; i >= 0; i--) {
      const q = pulses[i];
      q.age++;
      const a = 60 * (1 - q.age / 50);
      if (a <= 0) { pulses.splice(i, 1); continue; }
      p.noFill();
      p.stroke(...PAPER, a);
      p.strokeWeight(1);
      p.circle(q.x, q.y, 14 + q.age * 1.6);
    }
  };
}