どうも、クリエイティブコーダー / 上達の研究家のワタタクです。
前回の記事「「タッチポイント」を解剖する:ビジネス用語をアートの息遣いと提案の言葉へ磨き上げる」では、ビジネス用語としてのタッチポイントを「関連していること全て」「境界の融解と微細な共鳴」という言葉に再定義しました。
今回は、その思考プロセスと美意識をそのままコードに落とし込み、Processingで視覚化した実践ドキュメントです。
画面上で独立して漂うブランドの要素(青緑系)と、生活者の気配を宿したカーソル(オレンジ系)。 ただ衝突して跳ね返るようなデジタル処理ではなく、近づくにつれてお互いの色が溶け合い、離れた後には微細な波紋の余韻が広がっていく――。そんな「心を通わせる瞬間」をどう実装したのか、コードとあわせて解説します。
視覚化のコンセプト:境界の融解と微細な共鳴
言葉の解剖記事でも整理した通り、今回の作品のコアコンセプトは「境界の融解と、微細な共鳴」です。
画面上を漂う円たちは、ブランドを構成するさまざまな要素です。 普段はPerlinノイズによって穏やかに自分の領域を保っていますが、生活者(カーソル)が近づいた瞬間、微細な引力が発生して互いの色が混ざり合います。
表現のポイントと3つのアルゴリズム
- グラデーション描画による融解:
display()内のforループで複数の透明な円を重ね合わせて描画することで、フラットな境界線を消し、発光しているような柔らかな質感を表現しています。 lerp()/lerpColor()による自然なトランジション: 接触による色の変化や、離れたあとのパラメータの戻り(余韻)を線形補間で行うことで、デジタル特有の唐突さを消し、「じんわりと変化する」「ゆっくりと元に還る」有機的な動きを実現しています。- 波紋の逆算: 「離れた後に広がりながら消えていく」表現を作るため、
resonance(共鳴値)が $1.0$ から $0.0$ に向かって下がる数値を反転(1.0 - resonance)させ、それを波紋の拡大スケールとして活用しています。
コード


// コンセプト:『境界の融解と、微細な共鳴』
// 画面サイズ: 1600 x 900
ArrayList<Node> nodes;
color brandColor; // ブランドを象徴する色(青緑系)
color userColor; // 生活者(カーソル)を象徴する色(オレンジ/ピンク系)
void setup() {
size(1600, 900);
noStroke();
brandColor = color(30, 180, 200);
userColor = color(255, 120, 100);
nodes = new ArrayList<Node>();
// 独立して漂う複数の円(ブランドの要素)を生成
for (int i = 0; i < 20; i++) {
nodes.add(new Node());
}
}
void draw() {
// 背景を半透明で塗りつぶし、軌跡(微細な残像)を少しだけ残す
fill(15, 20, 30, 80);
rect(0, 0, width, height);
// 各ノード(円)の更新と描画
for (Node n : nodes) {
n.update();
n.display();
}
}
// --------------------------------------------------
// キーボード入力による保存処理
// --------------------------------------------------
void keyPressed() {
// 's' または 'S' キーを押した時に 1600x900 の画像を保存する
if (key == 's' || key == 'S') {
saveFrame("boundary_resonance_####.png");
println("画像を保存しました。");
}
}
// --------------------------------------------------
// ノード(漂う円)のクラス
// --------------------------------------------------
class Node {
float x, y; // 現在の座標
float tx, ty; // Perlinノイズ用の時間変数(滑らかな動き用)
float baseRadius; // 基本の半径
float resonance; // 余韻パラメータ (0.0: 通常 〜 1.0: 最大共鳴)
color myColor; // ノードごとの基本色
Node() {
x = random(width);
y = random(height);
tx = random(1000);
ty = random(1000);
baseRadius = random(30, 70);
resonance = 0;
// ブランドカラーを基準に、個体ごとに少しだけ色相をずらす
myColor = color(
red(brandColor) + random(-20, 20),
green(brandColor) + random(-20, 20),
blue(brandColor) + random(-20, 20)
);
}
void update() {
// 【ルール1】それぞれの色と形を保ちながら穏やかに動く
// Perlinノイズを使って、ランダムかつ滑らかに漂う動きを計算
x += map(noise(tx), 0, 1, -1.5, 1.5);
y += map(noise(ty), 0, 1, -1.5, 1.5);
tx += 0.005;
ty += 0.005;
// 画面外に出た場合、反対側からループさせる
if (x < -baseRadius) x = width + baseRadius;
if (x > width + baseRadius) x = -baseRadius;
if (y < -baseRadius) y = height + baseRadius;
if (y > height + baseRadius) y = -baseRadius;
// 【ルール1&2】距離による引力と干渉(dist() の活用)
float d = dist(x, y, mouseX, mouseY);
float interactionDist = 250; // タッチポイントが発生する距離
if (d < interactionDist) {
// カーソル(生活者)に近づいた場合、微細な引力が発生
x += (mouseX - x) * 0.01;
y += (mouseY - y) * 0.01;
// タッチポイント発生:共鳴パラメータを一気に引き上げる(上限1.0)
resonance = lerp(resonance, 1.0, 0.08);
} else {
// 【ルール3】触れたことの余韻(イージング・遅延)
// 距離が離れたら、共鳴パラメータをゆっくりと 0 に戻していく(減衰)
resonance = lerp(resonance, 0.0, 0.015);
}
}
void display() {
// 【ルール2】接触による境界の曖昧化と色の混ざり合い
// resonance (共鳴度合い) に応じて、自分の色から生活者の色へ滑らかにブレンド
color drawColor = lerpColor(myColor, userColor, resonance);
// 接触時は半径が少し膨張する
float currentRadius = baseRadius + resonance * 30;
// 境界線の融解(グラデーション描画)
// 一つの円ではなく、徐々に透明になる同心円を重ねて柔らかな光の球を作る
int steps = 15;
for (int i = steps; i > 0; i--) {
float r = currentRadius * (i / (float)steps);
// 共鳴(resonance)が高いほど、輪郭がさらに淡くぼやけ、アルファ値がゆらぐ
float alphaBase = map(i, 0, steps, 200, 0);
float alpha = alphaBase * (1.0 - resonance * 0.4);
fill(red(drawColor), green(drawColor), blue(drawColor), alpha);
ellipse(x, y, r * 2, r * 2);
}
// 【ルール3】触れた痕跡(余韻)の波紋描画
// 共鳴値が 0 より大きい(触れた直後〜離れた後)にのみ描画
if (resonance > 0.02) {
noFill();
// 共鳴値が高い時は太く、減衰するにつれて細くなる
strokeWeight(1 + resonance * 2);
// 共鳴値が減衰する(0に近づく)ほど、波紋が外側に広がっていく
float rippleExpand = (1.0 - resonance) * 150;
float rippleSize = (currentRadius * 2) + rippleExpand;
// 透明度も共鳴値に合わせてゆっくりと消えていく
stroke(red(drawColor), green(drawColor), blue(drawColor), resonance * 120);
ellipse(x, y, rippleSize, rippleSize);
noStroke(); // 設定を元に戻す
}
}
}実装のロジック解説
① ブランドと生活者の色相が溶け合う lerpColor()
今回のコードで象徴的なのが、ブランドカラー(青緑系)と生活者の色(オレンジ系)の混ざり合いです。 触れ合う(resonance が上がる)につれて、それぞれの固有色が相手の色へとじんわり移行していきます。単に形が変わるだけでなく、「お互いの影響を受けて変容する」というタッチポイントの本質を色で表現。
② 「波紋の逆算」
触れて離れたあとの「余韻」の表現です。 カーソルが離れると resonance は 1.0 から 0.0 へと減衰していきます。この減衰する数値をそのまま使うのではなく、「1.0 – resonance」として逆算することで、「離れていくほど波紋が外へ広がっていく」という物理的な波紋の広がりと一致させる。
③ Perlinノイズによる生命感
独立して動く円の軌道には random() ではなく noise() 。
カクカクとした機械的なランダムではなく、海を漂うクラゲや空気中を舞う微粒子のような滑らかな揺らぎを与えることで、無機質な図形に「意思や体温」を宿らせる。
感じたこと・上達の気付き
今回の制作を通して強く実感したのは、「言葉の定義が明確だと、数学やコードの引き出しが必然的に決まる」ということ。
もっと語彙力をつけていかないとなぁ。
まとめ
ビジネスの言葉「タッチポイント」の解剖から始まった今回の探求。 こうしてひとつの有機的なビジュアルとコードとして形にできたことで、私自身の「語彙力」と「翻訳力」も一段階レベルアップした手応えがあります。
この作品の根底にある思考のプロセスや、クライアントへの具体的な提案トークについては、以下の元記事に詳しくまとめています。ぜひあわせて読んでみてください。



