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

WORK 95

Ten Thousand Cuckoos

ちがう

YEAR
2026
TOOLS
p5.js
TECHNIQUE
p5.js / two voices repeating, additive accumulation

Two voices sing the same call, over and over: a high note, then a low one, a minor third apart. Each time a note sounds, one faint vertical line is laid down at its pitch, and the lines pile up. The upper voice is the cello: almost exactly the same every time, and every time slightly off, because the string is out of tune. It grows into a sharp, bright line just beside the true pitch, which is drawn as a barely visible dotted line. The lower voice is the cuckoo: different every time, never off on purpose. It does not grow into a clean peak. It stays a field of separate lines, and the true pitch is somewhere inside it. After a day it starts again in the morning. From Miyazawa Kenji's 'Gauche the Cellist': 'among us, if you say cuckoo ten thousand times, all ten thousand are different.'

ふたつの声が、同じ呼び声をくりかえす。高い音、それから短三度下の低い音。鳴るたびに、その高さに薄い縦線を一本ずつ置いていく。線は積もる。上はセロ。毎回ほとんど同じで、毎回すこしずれている——糸が合っていない。鋭く明るい一本になって、ほんとうの高さ(ほとんど見えない点線)のすぐ横に立つ。下はかっこう。毎回ちがって、わざとずれてはいない。きれいな山にはならない。ばらばらの線の野原のままで、ほんとうの高さは、そのどこか中にある。一日ぶん鳴いたら、次の朝にやり直す。宮沢賢治『セロ弾きのゴーシュ』から——わたしらのなかまならかっこうと一万云えば一万みんなちがうんです。

SOURCE / ソース
// #95 ちがう / Ten Thousand Cuckoos
// 毎回同じに鳴らす方と、毎回ちがって鳴る方。
export default function sketch(p) {
  const W = 720, H = 720;
  const DAY = 3600;
  const BEAT = 18;            // 一拍
  const GAP = 6;              // 「かっ」と「こう」のあいだ
  const SEMI = 70;            // 半音ぶんの幅(px)
  const HI = 450;             // ほんとうの高い音
  const LO = HI - 3 * SEMI;   // ほんとうの低い音(短三度下)
  const CELLO_BIAS = 0.22;    // 糸が合わない(半音)
  const CELLO_SD = 0.025;
  const BIRD_SD = 0.42;
  const bands = [
    { y0: 130, y1: 320, bias: CELLO_BIAS, sd: CELLO_SD, col: [236, 178, 108] }, // セロ
    { y0: 400, y1: 590, bias: 0, sd: BIRD_SD, col: [176, 196, 226] },           // かっこう
  ];
  let acc;
  let t = 0;
  let flashes = [];

  function reset() {
    acc.clear();
    acc.background(18, 20, 26);
    flashes = [];
    t = 0;
  }

  function sing(b, target) {
    const x = target + (b.bias + p.randomGaussian(0, b.sd)) * SEMI;
    acc.blendMode(p.ADD);
    acc.stroke(b.col[0] * 0.07, b.col[1] * 0.07, b.col[2] * 0.07);
    acc.strokeWeight(1);
    acc.line(x, b.y0, x, b.y1);
    acc.blendMode(p.BLEND);
    flashes.push({ x, b, life: 1 });
  }

  p.setup = () => {
    p.createCanvas(W, H);
    acc = p.createGraphics(W, H);
    acc.pixelDensity(1);
    reset();
  };

  p.draw = () => {
    if (t >= DAY) reset();
    const k = t % BEAT;
    if (k === 0) for (const b of bands) sing(b, HI);
    if (k === GAP) for (const b of bands) sing(b, LO);

    p.image(acc, 0, 0);

    // ほんとうの高さ:ごく薄く
    p.stroke(255, 255, 255, 60);
    p.strokeWeight(1);
    for (const b of bands) {
      for (const x of [HI, LO]) {
        for (let y = b.y0 - 14; y < b.y1 + 14; y += 6) p.line(x, y, x, y + 2);
      }
    }

    // 今鳴った一声
    p.noStroke();
    for (const f of flashes) {
      const c = f.b.col;
      p.fill(c[0], c[1], c[2], 200 * f.life);
      p.rect(f.x - 1, f.b.y0, 2, f.b.y1 - f.b.y0);
      f.life -= 0.06;
    }
    flashes = flashes.filter((f) => f.life > 0);

    t++;
  };
}