そのWeb演出、Claude Codeで作れます。クリエイティブWeb表現のプロンプト集 #08 Kintsugi
株式会社グルーヴサインの米沢です。
AIコーディングでクリエイティブなWeb表現を実際に作り、そのプロンプトごと公開していく「PROMPT → WEB」の第8回です。
PROMPT → WEB #008
Kintsugi
INTRODUCTION
「ボロノイ分割で何か作って」と言うと、AIはたいてい割れた破片を吹き飛ばします。クリックすると画面がガラスのように砕けて、破片が回転しながら散っていく。よく見る演出です。
今回はその逆をやります。破片を飛ばしません。割れ目だけを見せます。
金継ぎ(きんつぎ)という修復技法があります。割れた器を漆で接ぎ、継ぎ目に金を蒔く。壊れた跡を消さずに、むしろ金の線として見せる。割れる前より価値が上がることさえあります。
私の本業は他社が作ったシステムの保守・引き継ぎで、20年ずっとそれをやっています。動いているものを作り直さず、傷んだところに手を入れて使い続ける仕事です。金継ぎの話を最初に聞いたとき、これは自分の仕事の絵だと思いました。割れ目を隠さないというのが、特に。
なので今回の作品は、割れて終わりにしません。
貫入(もともと入っている細かい罅)→ 割れ(一点から走る) → 継ぎ(金を流す)
この3つの段が順に進みます。技術としてはボロノイ分割ですが、主題は分割された結果の境界線のほうです。
RESULT
まず現物です。下のボタンを押してみてください。
クリックすると、この画面の上で Kintsugi が再生されます(何度でも再生可能)。ESC または CLOSE で終了します。
青磁の面に、すでに細かな貫入が入っています。そこへ一点から罅(ひび)が走り、しばらくして金が流し込まれます。 操作はひとつです。
- クリックする(スマホはタップ) ── 押したところから割れます。 同時に釉薬も変わります。青磁 → 白磁 → 鉄釉 → 粉引。鉄釉のときは金ではなく銀、粉引のときは漆で継ぎます。
放っておくと、13秒ごとに別の器が別の場所から割れます。左下の箱書きに、いまの釉薬・破片の数・罅の本数・どの段にいるか(罅/割/継/了)が出ています。ESC または CLOSE で終了します。
近づいて見ると、罅の線は直線ではありません。そして金の両側には暗い縁が残っています。金は面の上に描かれているのではなく、窪みに流し込まれているように見えるはずです。
WHAT WE MADE
技術的に分解すると、次のようになります。
- ボロノイ分割を自前で書く ── ライブラリなし。ドロネー三角形分割も経由しません。
- 半平面クリッピング ── 画面の四角形を、隣り合う母点との垂直二等分線で切り落としていくだけ。定義がそのまま実装になります。
- ロイド緩和 ── 母点を自分の領域の重心へ寄せる操作を3回。これをやらないと細長い破片ができます。
- 2つの尺度 ── 細かい貫入(2300片)と、粗い割れ(96片)。同じコードを密度違いで2回使っています。
- 辺の重複を落とす ── 隣り合う2領域は同じ辺を共有しているので、そのままだと全部2度描きになります。
- 一点から走る罅 ── 辺ごとに衝撃点からの距離を測り、その順に現れます。先端だけは途中まで描きます。
- 継ぎは罅より細く ── 暗い縁を残すと、金が窪みに収まって見えます。
- 太さでまとめて描く ── 罅を1本ずつ描くと毎フレーム748回の描画呼び出し。太さを丸めてまとめると20回です。
HOW IT WORKS
1. ボロノイ分割は、定義をそのまま書けばいい
ボロノイ図を作ると聞くと、ドロネー三角形分割を計算してその双対を取る、という手順を思い浮かべます。正攻法ですが、実装は重いです。
定義に戻ります。母点 i の領域とは「他のどの母点よりも i に近い点の集まり」です。ということは、画面全体の四角形から出発して、他の母点 j との垂直二等分線で「j 側」を切り落としていけば、残ったものがそのまま領域になります。
// 母点 i と j の垂直二等分線で、j 側を捨てる
const dx = sx[j] - px, dy = sy[j] - py; // i から j への向き
const mx = (sx[j] + px) * 0.5, my = (sy[j] + py) * 0.5; // 2点の中点
// 各頂点について (頂点 - 中点)・(向き) の符号を見る。正なら j 側なので捨てる
const d = (ax - mx) * dx + (ay - my) * dy;
あとは多角形を線で切る処理(Sutherland–Hodgman)を繰り返すだけです。定義を読めば書けるという手触りが、この作品を作っていていちばん気持ちよかったところでした。
もちろん全母点と比べる必要はありません。2300個の母点で総当たりすると530万回の比較になります。粗い格子に母点を配っておき、5×5マスの近傍だけを見ます。実測すると1母点あたり平均19〜25個で足りていました。
2. ロイド緩和という一手間
母点を完全な乱数で置くと、偏って細長い破片ができます。陶器の割れ方には見えません。
対策は2段階です。まず粗い格子の中で位置を揺らして置く。そのうえでロイド緩和をかけます。各母点を、自分の領域の重心へ移動させる。それだけを数回繰り返すと、粒が驚くほど揃います。
const [cx, cy, area] = centroid(poly); // 自分の領域の重心へ
if (area > 0) { sx[i] = cx; sy[i] = cy; }
今回は割れに3回、貫入に1回かけています。回数が多いほど整然として、少ないほど自然に散らばります。 貫入をあまり整えないのは、器の罅が規則正しく入っていたら気味が悪いからです。
3. 同じコードを、2つの尺度で使う
この作品の見た目を支えているのは、密度の違う分割を2枚重ねていることです。
- 貫入: 面積900px²に1片。細く、薄く、面に焼き込む(起動時に1回だけ)
- 割れ: 面積14,000px²に1片。太く、濃く、時間をかけて走る
どちらもまったく同じ Tessellation クラスで作っています。違うのは母点の数と緩和の回数だけ。尺度を2つ持つと、一気に「物」に見えます。 片方だけだと、ただの図形の網です。
4. 辺は、必ず2回出てくる
領域ごとに輪郭を描くと、隣り合う領域が共有している辺を2度描くことになります。線が太くなり、半透明にすると重なった部分だけ濃くなります。
辺の中点を鍵にして重複を落とします。
const key = (Math.round(mx * 2) << 16) ^ Math.round(my * 2);
if (seen.has(key)) continue;
seen.add(key);
同じ辺は同じ中点を持つので、これで一意になります。96片の割れから、重複を落として249本の罅が残りました。
5. 罅は直線では走らない
取り出した辺をそのまま線で結ぶと、いかにも計算された多角形になります。罅に見せるには折らないといけません。
辺を3等分し、中間の点だけを法線方向へ少しずらします。ずらす量は辺の長さに応じて頭打ちにしてあります(長い辺が大きく波打つと、罅ではなく蛇行した川に見えます)。
走る順番は、衝撃点から辺の中点までの距離で決めています。ただしその距離に少し揺らぎを掛けています。素直に距離だけで並べると、罅がきれいな真円に広がって嘘っぽくなるためです。
6. 金は、罅より細く置く
継ぎの段で、最初に書いた実装は「罅の上に、罅より太い金の線を引く」でした。結果はオレンジ色の輪郭線で、器には見えませんでした。
直したのは順番と太さです。
- まず暗い罅を太めに引く
- その上に金の下地を、罅より細く引く
- さらに明るい金を、もっと細く引く
こうすると両側に暗い縁が残り、金が窪みに収まっているように見えます。**金を「描く」のではなく、罅を「埋める」**という順番でした。
7. 太さでまとめて描く
罅を1本ずつ beginPath → stroke すると、継ぎの段では249本 × 3パス + 先端で毎フレーム748回の描画呼び出しになります。
太さを0.35px刻みに丸めて、同じ太さの罅を1本のパスにまとめました。
// 太さの近い罅を同じ入れ物へ。塗りは入れ物ごとに1回だけ
buckets[bucketOf((0.9 + 2.4 * age) * e.weight)].push(i);
実測すると1フレームあたり20.3回。見た目の差は、太さが0.35px単位に丸まることだけです。
THE PROMPT
ここが本題です。上のデモを作るためにClaude Codeへ渡せる、汎用プロンプト全文を掲載します。右上の COPY ボタンでそのままコピーできます。釉薬の色や破片の数を置き換えれば、自分用になります。
あなたはCreative Codingと計算幾何に精通したフロントエンドエンジニアです。
「Kintsugi」= 釉薬の器が一点から割れ、その割れ目を金で継ぐWeb表現を、
Canvas 2D で実装してください。ボロノイ分割のライブラリは使わず、自前で求めること。
# 最重要の方針
- 【破片は飛ばさない】。割れた欠片を回転させて散らす演出にはしない。
主役は破片ではなく「割れ目そのもの」。
- 段は3つ。貫入(もともと入っている細かい罅)→ 割れ(一点から走る)→ 継ぎ(金を流す)。
- 分割の計算は起動時に1回だけ。毎フレームやるのは「どこまで割れたか」の線引きだけ。
# 置き換える変数
- GLAZES = 4種類。釉薬の色と、継ぎに使う金属を組にする:
青磁 SEIJI 面 "#9fc4b8"→"#6d9a8e" 継ぎ 金 "#8a6526"/"#f2dda2"
白磁 HAKUJI 面 "#eee8dc"→"#c9c0af" 継ぎ 金 "#96702a"/"#f7e9b8"
鉄釉 TETSU 面 "#3b332c"→"#191512" 継ぎ 銀 "#6f787d"/"#e9f0f4"
粉引 KOHIKI 面 "#ded5c6"→"#b0a492" 継ぎ 漆 "#5e2a20"/"#b96b52"
- LABEL = "KINTSUGI"
# ボロノイ分割(自前で書く)
- 【重要】ドロネー三角形分割を経由しない。定義どおり半平面クリッピングで求める:
画面より少し大きい四角形から始め、近傍の母点との垂直二等分線で
「相手側」を切り落としていく(Sutherland–Hodgman)。残った多角形が領域。
- 全母点と総当たりしない。粗い格子に母点を配り、5x5マスの近傍だけを見る
- 母点は完全な乱数で置かない。粗い格子の中で揺らして置き、そのうえで
【ロイド緩和】(母点を自分の領域の重心へ寄せる)を数回かける。
これをやらないと細長い破片ができて、陶器の割れ方に見えない
- 【重要】密度の違う分割を2枚重ねる:
貫入 … 面積900px²に1片、緩和1回。細く薄く、起動時に面へ焼き込む
割れ … 面積14,000px²に1片、緩和3回。太く濃く、時間をかけて走らせる
尺度が2つあると、図形の網ではなく「物」に見える
- 領域の輪郭をそのまま描くと、隣り合う領域が共有する辺を2度描いてしまう。
辺の中点を鍵にして重複を落とすこと
# 割れの走り方
- 辺ごとに「衝撃点から辺の中点までの距離」を持たせ、その順に現れさせる
- 【重要】距離に少し揺らぎ(±15%程度)を掛ける。素直に距離順だと
罅がきれいな真円に広がって嘘っぽくなる
- 辺は直線で結ばない。3等分して中間の点を法線方向へずらす(折れ線にする)。
ずらす量は辺の長さに応じて頭打ちにする(長い辺が波打つと川に見える)
- 先端(いま走っている罅)だけは途中まで描く。到達済みの罅は太く落ち着かせる
# 継ぎ(ここを間違えると輪郭線にしか見えない)
- 罅が走りきってから少し置いて、衝撃点の側から順に金が流れる
- 【重要】金は罅より【細く】置く。順番は
① 暗い罅を太めに ② 金の下地を細く ③ 明るい金をもっと細く
こうすると両側に暗い縁が残り、金が窪みに収まって見える。
金を「描く」のではなく、罅を「埋める」
# 性能
- 【重要】罅を1本ずつ beginPath/stroke しない。太さを0.35px刻みに丸めて
同じ太さをまとめ、まとまりごとに1回だけ stroke する
(そのままだと継ぎの段で毎フレーム700回以上の描画呼び出しになる)
- 面(釉薬+貫入)は1枚のオフスクリーンに焼いておき、毎フレームは drawImage 1回で敷く
# インタラクション
- クリック/タップした場所から割れる。同時に次の釉薬へ切り替える
- 放っておいたら13秒ほどで、別の器が別の場所から割れる
- 隅に箱書き(釉薬名・破片の数・罅の本数・いまどの段か)を明朝系で小さく置く
- ネオン・rainbow・glassmorphism は使わない。陶器の質感にする
# 品質要件(production)
- 全画面オーバーレイ。CLOSE ボタンと ESC で終了。多重起動を防ぐ
- レスポンシブ: リサイズで面を焼き直し、分割も作り直す
- devicePixelRatio は最大2でクランプし、以降の描画は CSS px で行う
- 狭い画面では、箱書きと操作ヒントが重ならないよう配置を組み替える
- prefers-reduced-motion: アニメーションせず、継ぎ終わった姿を1枚だけ描いて静止させる
- 終了時に requestAnimationFrame を停止し、全リスナを解除し、CanvasをDOMから除去する
- 乱数は種から作る(同じ器を再現できるように)
CUSTOMIZE
- 破片の大きさ ── 面積あたりの母点の数。大きくすると大胆な割れ、小さくすると粉々になります。粉々にすると継ぎが主役になり、別の作品になります。
- 緩和の回数 ── 多いほど整然、少ないほど自然。0回にすると細長い破片が混じって、それはそれで「落として割った」感じが出ます。
- 貫入の密度 ── 器の格を決めているのは、実はここです。細かいほど古く見えます。
- 継ぎの金属 ── 金・銀・漆。線を細くすると繊細に、太くすると大胆な景色になります。
- 揺らぎの量 ── 割れる順番に掛ける揺らぎ。大きくすると、あちこちが同時に割れて事故らしくなります。
- 静かにする ── 継ぎ終わった1枚を止めれば、そのまま壁紙になります(この記事のOGP画像が実際それです)。
PERFORMANCE
分割は起動時と釉薬の切り替え時だけ。毎フレームやっているのは線を引くことだけです(1920×1080・Nodeで計算部分を実測)。
- 割れの分割 ── 母点120個・近傍平均19.3個・ロイド緩和3回で 1.1ms。
- 貫入の分割 ── 母点2304個・近傍平均25.1個・緩和1回で 8.4ms。
- 近傍だけを見る効果 ── 2304個で総当たりすると530万回の比較になります。5×5マスに限ると1母点あたり25個です。
- 1フレームの描画呼び出し ── 20.3回(実測)。太さでまとめる前は 249本 × 3パス + 先端 = 748回。
- 面は焼いておく ── 釉薬と貫入は1枚のオフスクリーンに描いておき、毎フレームは
drawImage1回で敷くだけです。 - 後始末 ── 終了時にRAFを停止し、全リスナを解除してCanvasを除去。何度開閉してもリークしません。
WHAT AI DID / WHAT HUMAN DIRECTION MATTERS
ボロノイ分割そのものは、AIはよく知っています。むしろ知りすぎていて、頼んでいないものまで出してきました。
最初に返ってきたのは、ライブラリを使ってドロネー三角形分割を計算し、その双対からボロノイ図を得て、クリックで破片を飛ばす実装でした。正しいし、速いし、よくある。しかしライブラリを外すと何も残りません。
半平面クリッピングで書くよう指定したのは人間側です。実装量は増えますが、コードが定義そのものになり、読めば分かるものになりました。 連載の目的が「渡せるプロンプトを公開すること」である以上、ここは譲れないところでした。
もうひとつ、AIが自分からは提案しなかったのがロイド緩和です。最初の版は母点を一様乱数で置いていて、細長い破片が混じった、いかにも計算機的な分割でした。「陶器の割れ方に見えない」という判断は、絵を見ないと下せません。
金の置き方も同じです。最初は罅の上に太い金の線を引いていて、オレンジ色の輪郭線にしか見えませんでした。罅より細く置いて暗い縁を残すという1行の変更で、ようやく金が窪みに収まりました。これは技術の話ではなく、実物の金継ぎを見たことがあるかどうかの話です。
人間の仕事として残ったのは、次のような判断でした。
- 破片を飛ばさないと決めること(AIが最初に出す形を、あえて捨てる)
- ライブラリを外し、定義をそのまま書くと決めること
- ロイド緩和を入れること(絵を見て決める)
- 密度の違う分割を2枚重ねること
- 金を「描く」のではなく罅を「埋める」順番にすること
- 割れる順番に揺らぎを混ぜること(正しい計算を、あえて濁す)
最後の2つは、どちらも「正しく作ると嘘っぽくなる」類の調整です。距離順に正確に割れると真円になり、金を上から描くと輪郭線になる。正しさと、それらしさは違う。 その差を埋めるのは、いまのところ人の目です。
8回続けて、実装は一貫して速いままです。そして毎回、捨てる判断と、濁す判断に時間を使っています。
NEXT
粒子、文字、線、物理、計算、製版、音、そして今回の分割。
次回はまた別の仕掛け ── 影だけで形を見せる表現、幾何の証明が組み上がる線、スクロール連動あたりを考えています。
今回のプロンプトも丸ごとコピーして使えます。釉薬の色を自社のブランドカラーに置き換えれば、まったく別の器が割れます。割れ目を消さずに残すという考え方は、画面の中だけの話でもありません。私はそれを20年、他人の書いたコードでやっています。
レガシーPHPシステムの保守・引き継ぎのご相談を受け付けています。
初回のご相談・ヒアリングは無料です。