そのWeb演出、Claude Codeで作れます。クリエイティブWeb表現のプロンプト集 #04 Physics Playground
株式会社グルーヴサインの米沢です。
AIコーディングでクリエイティブなWeb表現を実際に作り、そのプロンプトごと公開していく「PROMPT → WEB」の第4回です。
PROMPT → WEB #004
Physics Playground
INTRODUCTION
ここまでの3回は「どう描くか」の技術で差をつけてきました。WebGLで粒子(#01)、Canvas 2Dで文字(#02)、SVGで線(#03)。ブラウザの主要な描画技術はひと巡りしました。
そこで今回からは、描画技術の目新しさではなく、**「仕掛け」**で驚きを作ります。第4回の仕掛けは リアルタイム物理です。
これまでのアニメーションは、いわば「あらかじめ決めた動き」を再生するものでした。粒子が球になり波になる軌道も、等高線が描き出される順序も、こちらが指定しています。物理シミュレーションはそこが決定的に違います。重力・衝突・摩擦というルールだけを与えて、動きそのものは計算に委ねる。 結果は毎回変わり、誰も正確には予測できません。触ると本当に反応が返ってくる、あの手応えの正体です。
題材は、社名やキーワードを刻んだ文字コインにしました。上からバラバラと落ちてきて、床に積もり、崩れる。カーソルで押しのけられ、クリックで増える。見た目は、ビビッドな色の光沢のある金属球。ガチャガチャのカプセルやおはじきのように、思わず触りたくなる質感を狙いました。理屈より先に、指が動いてしまう「おもちゃ」です。
RESULT
まず現物です。下のボタンを押してみてください。
クリックすると、この画面の上で Physics Playground が再生されます(何度でも再生可能)。ESC または CLOSE で終了します。
記事の上に明るい画面が広がり、PROMPT・WEB・GROOVESIGN の文字コインが上から降ってきて、床に積もっていきます。操作は2つです。
- カーソルを動かす(スマホは画面をなぞる) ── カーソルが見えない反発体になり、コインを押しのけます。積もった山をかき混ぜたり、崩したりできます。
- クリックする(スマホはタップ) ── その場所からコインが数枚、勢いよく湧き出します。押し続ければどんどん積み上がっていきます。
同じ積まれ方は二度と起きません。どこに何色が来るか、どう崩れるかは、その場の計算次第です。ESC または CLOSE で終了します。
これは動画でも物理エンジンライブラリでもなく、300行ほどの自作2D物理をその場で回しています。この演出をClaude Codeに作らせるためのプロンプトは、記事の後半でそのまま公開しています。
WHAT WE MADE
技術的に分解すると、次のようになります。
- 描画はCanvas 2D ── 毎フレーム画面を塗り直し、球と文字を描いています。技術としては#02と同じCanvasですが、主役は描画ではなく物理です。
- 自作の2D物理 ── 外部ライブラリを使わず、重力・壁・床の摩擦・コイン同士の衝突を、200行ほどのコードで実装しています(後述)。
- 重力による落下と堆積 ── 各コインに重力加速度を与えて積分。床と壁で跳ね返り、床では摩擦で横滑りが止まり、やがて静止して積もります。
- コイン同士の衝突 ── 総当たりで重なりを検出し、めり込みを押し戻し、法線方向の速度を反発させます。これがあるから「積もる」「崩れる」が成立します。
- ポインタの反発 ── カーソルを中心に、近いコインほど強く外へ押す力を加えます。触れる楽しさの核です。
- クリックで生成 ── クリック位置から上向きの初速でコインを数枚spawn。上限を超えたら古いものから消し、数を一定に保ちます。
- 光沢のある金属球 ── 各コインを平らな円で終わらせず、球に見せています。左上からの光で明るい面、右下に落ちる陰、下端に環境光の照り返し(リムライト)、そして上に小さな鏡面ハイライト。この4枚を重ねるだけで、Canvasの円が立体的な玉になります(後述)。
HOW IT WORKS
物理エンジンと聞くと身構えますが、この規模なら中身は素朴です。毎フレーム、次の3つを順番にやっているだけです。
1. 動かす(重力と積分)
各コインは位置 (x, y) と速度 (vx, vy) を持ちます。毎フレーム、速度に重力を足し、位置に速度を足す。これだけで落下します。
c.vy += GRAV * dt; // 速度に重力を加える
c.x += c.vx * dt; // 位置を速度分だけ進める
c.y += c.vy * dt;
dt は前フレームからの経過秒。これを掛けることで、端末のフレームレートが変わっても落下速度が一定になります。地味ですが、これを忘れると速い端末だけコインが吹っ飛びます。
2. 壁と床で跳ね返す
画面の外に出ようとしたら、位置を内側へ戻し、その軸の速度を反転させます。反転する際に反発係数(0〜1)を掛けると、跳ね返りの強さを調整できます。床では横方向の速度に摩擦を掛け、やがて滑りが止まって静止します。
if (c.y > H - c.r) { // 床にめり込んだ
c.y = H - c.r; // 床の上に戻す
c.vy = -c.vy * REST; // 上向きに、弱めて跳ね返す
c.vx *= FRICT; // 横滑りを摩擦で減衰
}
3. コイン同士をぶつける
ここが「積もる」の心臓です。2つのコインの中心距離が半径の和より近ければ、衝突しています。まず重なった分を押し戻して離し、次に近づく向きの速度だけを反発させます。
const d = Math.hypot(dx, dy); // 中心間の距離
if (d < a.r + b.r) { // 重なっている
const overlap = (a.r + b.r - d) * 0.5;
// 互いに半分ずつ、法線方向へ押し戻す(めり込み解消)
a.x -= nx * overlap; b.x += nx * overlap;
a.y -= ny * overlap; b.y += ny * overlap;
// 近づく速度成分だけを反発させる(離れつつあるなら触らない)
if (vn < 0) { /* 法線方向の運動量を交換 */ }
}
コインは数十枚なので、総当たり(O(n²))で十分間に合います。数千個を超えるなら空間分割(グリッドや四分木)が必要ですが、この規模で持ち込むのは早すぎる最適化です。まず素朴に書いて、実測してから考える。順序を守るのが結局いちばん速く仕上がります。
なめらかさの鍵は「サブステップ」
物理で最も厄介なのが、コインが速すぎて1フレームで相手をすり抜ける「トンネリング」と、積み重なりが震える「ジッター」です。対策はシンプルで、1フレームの dt を数分割し、物理計算を複数回まわすこと。
const sub = 3;
for (let s = 0; s < sub; s++) step(dt / sub); // 3回に分けて解く
計算量は増えますが、積み重なりが安定し、貫通も減ります。数十枚なら60fpsに余裕で収まります。「1回で大きく動かす」より「小さく何回も動かす」ほうが、物理は安定する。 これは覚えておくと応用が利きます。
平らな円を、金属球に見せる
物理とは別に、見た目でも一つだけ手をかけています。Canvasの arc() はどこまでいっても平らな円です。これを立体的な玉に見せるのに、3Dも画像も要りません。光のふるまいを4枚重ねるだけです。
- 本体 ── 塗りを単色ではなく、光源(画面の左上)を中心にした放射グラデーションにする。明るい面 → 固有色 → 暗い縁、と段階をつけると、それだけで球の丸みが出ます。
- リムライト ── 球の下端に、環境光の照り返しを少しだけ足す。金属やプラスチックの「照り」はここで決まります。
- 鏡面ハイライト ── 左上に小さく鋭い白い光を乗せる。これが「濡れたような艶」の正体です。
- 接地影 ── 真下に楕円の影を1枚。床に接している感じが出て、浮きが消えます。
ポイントは、光は画面に対して固定することです。コインは回転していますが、光源まで一緒に回すと、玉がグルグル光って安っぽくなる。回すのは表面に刻まれた文字だけ。ハイライトは常に左上 ── そう分けると、回っているのに艶の位置は動かない、本物の球の見え方になります。
// 光源を画面左上に置いた放射グラデーション(本体の塗り)
const g = ctx.createRadialGradient(-r*0.4, -r*0.44, r*0.05, 0, 0, r*1.15);
g.addColorStop(0, lighten(color, 0.6)); // 光の当たる面
g.addColorStop(0.55, color); // 固有色
g.addColorStop(1, darken(color, 0.5)); // 陰・縁
全コインは同じ半径なので、このグラデーションはリサイズ時に1度だけ作って使い回します。毎フレーム120個ぶんを作り直す必要はありません(後述)。
THE PROMPT
ここが本題です。上のデモを作るためにClaude Codeへ渡せる、汎用プロンプト全文を掲載します。右上の COPY ボタンでそのままコピーできます。語・色・ラベルを置き換えれば、自分用になります。
あなたはCreative Codingと物理シミュレーションに精通したフロントエンドエンジニアです。
「Physics Playground」=文字コインが重力で落ちて床に積もり、ぶつかって崩れ、
カーソルで押しのけられ、クリックで増える、リアルタイム物理のWeb表現を、
Canvas 2D と自作の軽量2D物理で実装してください。
(物理エンジンライブラリは使わない。200行程度の自作で足りる規模にする)
# 置き換える変数
- WORDS = ["PROMPT", "WEB", "GROOVESIGN"] // 各文字が1枚のコインになる
- BG_COLOR = "#efece4" // 明るい生成り。宇宙や黒背景にしない
- PALETTE = ビビッドな6色(各色を金属球の固有色として使う):
朱 "#ff4d2e" / 青緑 "#1fae9c" / 藍 "#3b5bdb" /
琥珀 "#f6a723" / 墨 "#17130e" / 生成り "#e9e4d8"
※語ごとに1色を割り当てる(PROMPT=朱, WEB=青緑, GROOVESIGN=藍 …)
- LABEL = "PHYSICS PLAYGROUND"
# ビジュアル / クリエイティブディレクション
- コインは平らな円ではなく「光沢のある3Dの金属球」に見せる(ガチャのカプセルのような艶)
- 各球は次の4枚を重ねて立体に見せる:
①本体 = 光源(画面左上)中心の放射グラデ(明→固有色→暗い縁)
②リムライト = 球の下端に環境光の照り返しを薄く
③鏡面ハイライト = 左上に小さく鋭い白(濡れたような艶)
④接地影 = 真下に楕円の影を1枚
- 【重要】光源は画面に固定する。球が回転しても光は左上のまま。回すのは表面の文字だけ
(光源ごと回すと安っぽくなる)
- 球の中央に極太サンセリフの1文字(Helvetica/Arial系, 800)を白抜き or 墨で刻む
- 隅に等幅フォントで小さく "PROMPT → WEB" / "#004" / LABEL を置く
- ネオン・rainbow・紫グラデ・glassmorphism・過剰なブルームは使わない(前作と差別化。艶は上品に)
# 物理(自作・ライブラリ禁止)
- 各コイン: 位置(x,y) 速度(vx,vy) 半径r 回転a 角速度av 文字 色index
- 重力: vy += GRAV * dt (GRAV ≈ 2200 px/s^2)。dtを必ず掛ける(フレームレート非依存)
- 壁: 画面端で位置を内側へ戻し、その軸の速度を反発係数REST(≈0.28)で反転
- 床: 位置を戻して跳ね返し、横速度に摩擦FRICT(≈0.86)を掛けて静止させる
- コイン同士: 総当たり(O(n^2))で
①中心距離 < 半径和 なら重なり分を互いに半分ずつ法線方向へ押し戻す
②近づく速度成分(vn<0)のときだけ法線方向の運動量を反発係数で交換する
(数十枚なので総当たりで十分。空間分割は入れない=早すぎる最適化)
- 【安定化】1フレームのdtを3分割し、物理stepを3回まわす(サブステップ)。
積み重なりのジッターと高速時のすり抜け(トンネリング)を抑える
- dtは上限でクランプ(タブ復帰などの暴れ防止, 例: 0.05秒)
# アニメーション / インタラクション
- 起動時: WORDSの各文字を上から時間差(≈130ms間隔)で1枚ずつ落とす。
x位置はランダム、初速に軽い横ブレを与える
- ポインタ(マウス/タッチ)を「見えない反発体」にする:
半径 R*2.6 以内のコインを、近いほど強く外向きに押す力を毎フレーム加える
- クリック/タップ: その座標からコインを3枚、上向きの初速でspawnして増やす
- コイン数の上限を決め(デスクトップ120/モバイル60)、超えたら古いものから消す
# 描画(Canvas 2D)
- devicePixelRatio対応(setTransform, Math.min(dpr,2))。毎フレーム背景で塗り潰す
- 各コイン: 接地影 → 本体(放射グラデ) → リムライト → 文字(回転aを反映) → 鏡面ハイライト
- 【性能】全球は同じ半径なので、放射グラデーションはリサイズ時に1度だけ生成して使い回す
(毎フレーム全コインぶん作り直さない)
- 隅のラベルは等幅フォントで薄く
# 実装要件
- position: fixed の全画面オーバーレイとして被せ、通常ページに影響させない
- リサイズ・画面回転で幅高さと半径を再計算する
- マウスとタッチの両方に対応。モバイルは半径を大きめ・コイン数を少なめに
# 堅牢性 / 品質
- CLOSE ボタンと ESC で必ず終了できる
- prefers-reduced-motion では、落下アニメを行わず、文字コインを下部に整列して静止表示する
- Canvasが取得できない等の失敗時はエラーを出さず自動でスキップ
- JavaScriptが失敗しても本体ページは必ず表示される Progressive Enhancement
- 終了時に requestAnimationFrame を停止し、イベントリスナ(mouse/touch/resize/key)と
落下用インターバルをすべて解除、Canvasを DOM から除去(何度実行してもリークしないこと)
- 多重起動を running フラグで防ぐ
- production 品質。共通のオーバーレイ/CLOSE/後始末は再利用できる形に分離する
# アクセシビリティ
- CLOSE は button 要素。ESC で閉じられる。オーバーレイに aria-label
- 装飾なので Canvas には aria-hidden を付ける
- 動きが苦手な人向けに prefers-reduced-motion を尊重(静止表示)
まず重力と壁・床の反発で1枚を落として静止させ、次にコイン同士の衝突、
サブステップ、時間差の落下、ポインタ反発、クリック生成、の順に積み上げ、
モバイル・reduced motion・後始末・本番ビルド確認まで進めてください。
弊社がこのデモを作った際も、この粒度から始めました。落下と衝突の土台は、ほぼ一発で動きます。時間をかけたのは、触ったときの手応えの調整でした。反発が強すぎるとコインが跳ね回って落ち着かず、弱すぎると濡れた雑巾のように積もる。摩擦とサブステップの数を行き来しながら、「積もるけれど、押すと気持ちよく崩れる」一点を探しました。
CUSTOMIZE
- 落とす言葉 ──
WORDSを差し替えるだけで、自社名やキャッチコピーが降ってきます。文字数がそのままコインの枚数になります。 - 重力の強さ ──
GRAVを上げると鋭く速く、下げると月面のようにふわりと落ちます。 - 跳ね方 ── 反発係数
REST。上げるとよく弾むゴムボールに、下げると粘土のように「べちゃっ」と積もります。おもちゃっぽさは高め、落ち着きは低めが目安です。 - 積もりやすさ ── 床摩擦
FRICT。1に近いほど横滑りが止まらず散らばり、小さいほどその場にきれいに積もります。 - 押しの強さ ── ポインタ反発の半径と力。強くすると触れただけで山が吹き飛び、弱くすると指でそっと寄せる感触になります。
- 色と艶 ──
PALETTEを差し替えれば全体の気分が変わります。鏡面ハイライトを強く小さくすると硬い金属球に、弱く大きくするとマットなグミのような質感に。ハイライトを消せば、フラットなおはじきに戻ります。 - 静かにする ── 落下をやめて最初から整列させれば、動かない一枚のタイポグラフィになります(reduced-motion時と同じ絵です)。
PERFORMANCE
物理は「触った感触」が命なので、コマ落ちは致命傷です。実運用では次の点に気をつけました。
- 枚数の上限を決める ── デスクトップ120枚・モバイル60枚。上限を超えたら古いコインから消し、総当たり衝突の計算量(O(n²))が青天井にならないようにしています。120枚なら1フレームの衝突判定は約7,000ペアで、実測0.3ms未満。余裕です。
- フレームレート非依存 ── 全ての運動に
dtを掛け、dtは0.05秒でクランプ。タブを離れて戻ってもコインが吹っ飛びません。 - サブステップは3回まで ── 増やすほど安定しますが計算量も比例します。この枚数なら3回で十分な安定と60fpsを両立できました。多ければ良いわけではなく、必要十分を実測で決める部分です。
- 空間分割は入れない ── 数千個を超えるなら必須ですが、数十枚に持ち込むと、むしろ管理コストで遅くなります。規模に合わない最適化はしない、という判断も設計のうちです。
- グラデーションは作り置き ── 球の陰影は放射グラデーションで作りますが、全球が同じ半径なので、色ごとに1本だけ生成してリサイズ時にキャッシュ。毎フレーム120個ぶんを作り直すのと比べ、生成コストがほぼゼロになります。Canvasの重いシャドウブラーは使わず、艶は半透明の重ね塗りで表現しています。
- 後始末 ── 終了時にrequestAnimationFrameと落下インターバルを停止し、全リスナを解除してCanvasをDOMから除去。何度開閉してもリークしません。
WHAT AI DID / WHAT HUMAN DIRECTION MATTERS
重力・衝突・反発・摩擦。物理の基本式そのものは、教科書にもAIの頭の中にも入っています。「Canvasで文字コインが落ちて積もる自作2D物理を」と伝えれば、動く土台はすぐ立ち上がります。 めり込みの押し戻しも、法線方向の運動量交換も、AIは淀みなく書きます。ここは完全にAIの領分です。
一方で、任せきりにできない部分もはっきりしています。
- そもそも「物理を触れるおもちゃにする」という題材を選ぶこと
- 反発と摩擦の一点を決めること ── ここが「気持ちよさ」の全て
- サブステップを入れるという安定化の判断(見た目ではなく、震えるか否かを決める)
- 枚数の上限を決め、空間分割をあえて入れないという規模の見極め
- 平らな円を金属球に見せる「光の4枚」の設計と、光源を画面に固定するという判断
最初に出てきた実装は、反発係数が高すぎてコインがトランポリンのように跳ね回り、いつまでも落ち着きませんでした。数値を一つずつ下げ、床の摩擦を効かせ、サブステップを足す。派手にするのではなく、落ち着かせる方向の調整でした。物理は放っておくと暴れます。それを「気持ちよく暴れて、気持ちよく静まる」範囲に収めるのが、人間の仕事でした。
物理シミュレーションは、AIにとって書きやすい題材です。だからこそ差が出るのは、どの数値で手が止まるかという感覚のほうです。手が速くなった分、この「触って決める」時間にこそ、人が向き合う価値が残っています。4回続けて、同じ結論に辿り着いています。
NEXT
第1回は粒子(WebGL)、第2回は文字(Canvas 2D)、第3回は線(SVG)、そして第4回は物理(自作エンジン)。描画技術で一巡したあと、「仕掛け」で驚きを作る新しい章に入りました。
次回はまた別の仕掛け ── 画像処理(ハーフトーン)、音(Web Audio)、スクロール連動といった方向を考えています。一覧で見たときに「毎回違う驚きがある」状態を、引き続き目指します。
今回のプロンプトも丸ごとコピーして使えます。自社の言葉と色に置き換えて、ぜひ一度Claude Codeに渡してみてください。降ってくる自社名は、想像より少しだけ楽しいはずです。
レガシーPHPシステムの保守・引き継ぎのご相談を受け付けています。
初回のご相談・ヒアリングは無料です。