ブログ |

PROMPT → WEB
#005
LIVING PATTERNS
CLAUDE CODE × CANVAS 2D / REACTION-DIFFUSION

そのWeb演出、Claude Codeで作れます。クリエイティブWeb表現のプロンプト集 #05 Living Patterns

株式会社グルーヴサインの米沢です。

AIコーディングでクリエイティブなWeb表現を実際に作り、そのプロンプトごと公開していく「PROMPT → WEB」の第5回です。

PROMPT → WEB #005

Living Patterns

INTRODUCTION

ここまでの4回には、共通していたことがあります。描く対象があった、ということです。粒子(#01)、文字(#02)、線(#03)、物体(#04)。形はこちらが用意し、それを動かしていました。

今回はその前提を外します。描く対象がありません。あるのは、画面を細かい格子に区切った2種類の物質の濃度だけです。それぞれのマスで「にじむ」「反応する」という単純なルールを毎フレーム計算します。すると、誰も設計していないのに模様が勝手に生まれ、育ち、増える。

これは反応拡散系(Reaction–Diffusion)と呼ばれるもので、提唱したのはアラン・チューリングです。計算機の父として知られる人が晩年に取り組んだのは、「なぜ生き物にはあの模様があるのか」という問いでした。貝殻の縞、熱帯魚の斑点、豹の模様。設計図に描かれているのではなく、単純な化学反応から自然に生じているというのが彼の答えでした。

今回の仕掛けは、その計算をブラウザで毎秒何百万回と回すことです。題材は理科室の標本プレートにしました。なぞると養分が落ちて模様が育ち、クリックすると同じ数式のまま別の生き物に変わります。

RESULT

まず現物です。下のボタンを押してみてください。

クリックすると、この画面の上で Living Patterns が再生されます(何度でも再生可能)。ESC または CLOSE で終了します。

温かいボーン色のプレートに、赤錆色の珊瑚のような模様が広がっていきます。操作は2つです。

左下の標本ラベルに、いま何を見ているのか(feed / kill / 拡散比・世代数)が出ています。ESC または CLOSE で終了します。

動画でもGIFでもありません。あなたの画面の中で、いま計算されている化学反応です。同じ模様は二度と出ません。

WHAT WE MADE

技術的に分解すると、次のようになります。

HOW IT WORKS

1. 式は、たった2行

物質AとBがあり、次の反応が起きるとします。

A + 2B → 3B      Bは、Aを食べて自分を増やす(自己触媒)
B → 除去          Bは一定の割合で取り除かれる
A → 供給          Aは外から一定の割合で補充される

これを各マスの濃度 a, b について式にすると、コードはこれだけです。

const abb = a * b * b;                          // A + 2B → 3B
na = a + (DA * lapA - abb + feed * (1 - a)) * dt;   // Aは減り、外から補充される
nb = b + (DB * lapB + abb - (feed + kill) * b) * dt; // Bは増え、一定割合で消える

feed(供給率)と kill(除去率)が、この世界の法則です。この2つの数字だけで、生まれる模様が決まります。

2. 「にじみ」を計算する

上の lapA / lapB はラプラシアンといって、要はにじみです。周囲のマスと比べて自分が濃ければ薄くなり、薄ければ濃くなる。そのにじみ具合を、周囲8マスの重み付き和で求めます。

const lapB =
  b[u-1]*0.05 + b[u]*0.2 + b[u+1]*0.05 +
  b[i-1]*0.2  - b[i]     + b[i+1]*0.2  +
  b[d-1]*0.05 + b[d]*0.2 + b[d+1]*0.05;

上下左右だけ(4マス)でも動きますが、その場合は模様が縦横の方向に偏ります。斜めを 0.05 で加えると、格子の存在を感じさせない自然な模様になります。画面が四角いことを模様に悟らせないための一手間です。

3. 同じ式のまま、別の生き物になる

面白いのはここからです。AとBのにじむ速さの比(拡散比)も効きます。Bのにじみが遅いほど、模様は細かくなります。

デモの「珊瑚」と「繊維」は、feed も kill もまったく同じ数字です。違うのは拡散比(0.50 と 0.30)だけ。それだけで、枝分かれする珊瑚は、目の細かいレース状の組織に変わります。生き物の模様の多様さが、設計図の量ではなくわずかな条件の差から来ていることが、画面の上でそのまま見えます。

切り替えのとき、パラメータは一瞬で飛ばさず、40フレームほどかけて目標値へ寄せています。こうすると画面がリセットされず、いまいる生き物がゆっくり別の生き物に変態していくように見えます。

4. 計算解像度を、画面から切り離す

素直に実装すると「1ピクセル=1マス」にしたくなりますが、これをやると4Kディスプレイで200万マスを毎フレーム6回計算することになり、即座に破綻します。

そこで計算用のCanvasは横420マス前後に固定し、それを表示用Canvasへ拡大して描いています。

sctx.putImageData(img, 0, 0);                     // 低解像度で計算結果を書く
vctx.imageSmoothingEnabled = true;
vctx.drawImage(sim, 1, 1, w-2, h-2, 0, 0, W, H);  // 画面いっぱいに拡大

拡大時のなめらかな補間が、そのままインクのにじみのような質感になります。粗さが、味方になっている部分です。ついでに外周1マス(計算していない縁)も、この時に切り落としています。

5. 開幕で固まらせない「助走」

模様が見応えのある状態になるには1000回以上の反応が要りますが、それを起動時に一括で回すと1秒以上画面が固まります。かといって最初から毎フレーム6回だと、豊かになるまで待たされます。

そこで最初の42フレームだけ18回、その後は6回に落としています。起動直後は少し重くなりますが、模様が一気に育つ様子そのものが見どころになるので、演出としても都合が良い。性能上の妥協が、そのまま演出になった例です。

THE PROMPT

ここが本題です。上のデモを作るためにClaude Codeへ渡せる、汎用プロンプト全文を掲載します。右上の COPY ボタンでそのままコピーできます。色やラベルを置き換えれば、自分用になります。

あなたはCreative Codingと数値シミュレーションに精通したフロントエンドエンジニアです。
「Living Patterns」= 図形も文字も描かず、画面そのものを反応拡散系(Gray-Scott)として
1ピクセルずつ計算し、模様が自分から育っていくWeb表現を、Canvas 2D で実装してください。
(シミュレーションのライブラリは使わない。数式は自前で書く)

# 置き換える変数
- BG_COLOR   = "#e9e3d5"   // 温かいボーン色。理科室の標本プレートの地
- PRESETS    = 4種類。それぞれ feed / kill / 拡散比 と配色を持つ:
    珊瑚  CORAL f=0.0545 k=0.0620 拡散比0.50 … 赤錆 "#c2543a" / 芯 "#2a1a12"
    繊維  LACE  f=0.0545 k=0.0620 拡散比0.30 … 緑青 "#2f8073" / 芯 "#10201d"
    迷路  MAZE  f=0.0290 k=0.0570 拡散比0.50 … 藍   "#4d4a86" / 芯 "#16142a"
    細胞  CELLS f=0.0390 k=0.0580 拡散比0.50 … 黄土 "#b8842a" / 芯 "#24190e"
- LABEL      = "LIVING PATTERNS"

# シミュレーション(Gray-Scott 反応拡散)
- 物質A,Bの濃度を Float32Array で2枚ずつ持ち、毎ステップ入れ替える(ピンポン)
- 各マスの更新式:
    abb = a*b*b
    a' = a + (DA*lapA - abb + feed*(1-a)) * dt
    b' = b + (DB*lapB + abb - (feed+kill)*b) * dt
    DA=1.0 / DB=拡散比 / dt=1.0
- lap は9点ラプラシアン(上下左右0.2・斜め0.05・中心-1)。斜めを入れて方向の偏りを消す
- 外周1マスは計算せず、描画時に切り落とす(境界条件の分岐をループから追い出す)
- 初期状態: a=1, b=0 に、小さな円形の種を全体に散らす
- 1フレームに複数回ステップを進める(既定6回)。1回では変化が遅すぎる

# 描画
- 計算用の低解像度Canvasと、表示用の全画面Canvasを分ける
- 計算グリッドは横 clamp(innerWidth/3.2, 280, 440) マスに固定する。
  【重要】画面のピクセル数に比例させない。4Kでも計算量を一定に保つため
- 濃度b → 色は、256段階の参照表(Uint32Array)を先に作り、毎ピクセルは表を引くだけにする
  ランプは [地の色 → 淡い色 → 発色(縁)→ 芯の濃色] の4点を補間して作る
- ImageData の buffer を Uint32Array として直接書き、putImageData → drawImage で拡大する
- 拡大は imageSmoothingEnabled = true(補間のぼけを、インクのにじみとして使う)
- 隅に標本ラベル(明朝系)で「標本 NN / 名称 / f・k・拡散比・世代数」を小さく置く
- ネオン・rainbow・紫グラデ・glassmorphism・黒背景は使わない(生物図鑑の落ち着いた質感にする)

# インタラクション
- ポインタを動かすと、その軌跡に沿って円形にBを注入する(なぞると模様が育つ)
  前フレームとの間を補間して打つ。速く動かしても線が途切れないように
- クリック/タップで次のプリセットへ。【重要】画面をリセットしないこと。
  feed/kill/拡散比を40フレームほどかけて目標値へ補間し、配色も同時に送る。
  いまの模様が別の模様へ「変態」して見えるようにする
- Bが尽きて模様が死滅した場合のみ、静かに種を蒔き直す(常時監視はしない。数十フレームに1回で十分)

# パラメータ選定の注意
- feed/kill には「模様が画面を埋める領域」と「まばらな点で終わる領域」がある。
  有名な値でも画面が寂しくなるものは採用しない。候補を複数試し、
  一定濃度を超えたマスの割合(被覆率)が40%以上あるものだけを選ぶこと
- 4つのプリセットは、色ではなく「構造」で違いが出るように選ぶ
  (枝分かれ/細かい網目/迷路/細胞の粒 のように)

# 品質要件(production)
- 全画面オーバーレイ。CLOSE ボタンと ESC で終了。多重起動を防ぐ
- レスポンシブ: リサイズでグリッドを組み直す。devicePixelRatio は最大2でクランプ
- モバイル: 計算グリッドを狭く(200〜300)、1フレームのステップ数も減らす。タッチ操作に対応
- prefers-reduced-motion: アニメーションせず、十分に育てた状態を1枚だけ描いて静止させる
- 起動時に一括で大量ステップを回して固まらせない。最初の数十フレームだけステップ数を増やす
- 終了時に requestAnimationFrame を停止し、全リスナを解除し、CanvasをDOMから除去する
  (何度開閉してもリークしないこと)
- Canvas 2D が使えない環境では静かに何もしない。本文の閲覧を妨げない

CUSTOMIZE

PERFORMANCE

全ピクセルを毎フレーム何度も計算するので、今回は素直に重い題材です。実測しながら決めました。

WHAT AI DID / WHAT HUMAN DIRECTION MATTERS

Gray-Scottの式そのものは、教科書にもAIの頭の中にも正確に入っています。「Canvasで反応拡散を実装して」と言えば、動くものはすぐ出てきます。 ラプラシアンの重みも、ピンポンバッファも、参照表による高速化も、AIは淀みなく書きます。

問題はその後でした。最初に出てきた実装は、正しく動いているのに美しくありませんでした。

原因はパラメータです。反応拡散でよく紹介される有名な値(例えば「細胞分裂」と呼ばれる f=0.0367 / k=0.0649)を入れると、確かに教科書どおりの挙動になります。しかし画面いっぱいで見ると、まばらな点が寂しく散らばっているだけでした。理科的な正しさと、作品としての強さは別物だったわけです。

そこで、9種類の候補を同じ条件で3000ステップ回し、一定濃度を超えたマスの割合(被覆率)を数値で比べました。 結果、有名な値ほど疎(8〜15%)で、採用したのは40%を超えたものだけです。「なんとなく綺麗そう」ではなく計測で決めた、という点では#003・#004と同じやり方をしています。

人間の仕事として残ったのは、次のような判断でした。

最後の1つは、技術的にはパラメータを1つ変えただけです。しかし「同じ法則から、こんなに違う生き物が生まれる」というこの表現の主題は、この並びがないと伝わりません。何を作るかはAIが実装できても、何を並べれば伝わるかは、まだ人が決めることです。

5回続けて、結論は同じところに落ち着いています。実装のコストは劇的に下がりました。下がった分だけ、選ぶことと捨てることに時間を使えるようになった、というのが実感です。

NEXT

粒子(WebGL)、文字(Canvas 2D)、線(SVG)、物理(自作エンジン)、そして今回の計算そのもの。「描く」から「育てる」へ、一歩進んだ回でした。

次回はまた別の仕掛け ── 画像処理(ハーフトーン)、音(Web Audio)、スクロール連動あたりを考えています。

今回のプロンプトも丸ごとコピーして使えます。色を自社のブランドカラーに置き換えて、Claude Codeに渡してみてください。しばらく画面を眺めてしまうはずです。誰も描いていない模様が育っていくのを見るのは、思いのほか飽きません。

レガシーPHPシステムの保守・引き継ぎのご相談を受け付けています。
初回のご相談・ヒアリングは無料です。

無料相談・お問い合わせはこちら

株式会社グルーヴサイン 代表取締役 米沢忠司。PHP開発歴約20年。他社開発システムの保守・引き継ぎを主力に、2007年の設立から19期目。