▍AI Dev Lab
PilePark

PilePark ができるまで — matter.js の物理エンジンで積む

2D物理サンドボックスの開発ログ。matter.jsでクリックとドラッグの操作を組み、図形を落として掴んで投げられる玩具箱風のサンドボックスを作った経緯を記録しています。

·decision改善·stage公開中

PilePark は matter.js で2D 物理サンドボックス。 円・矩形・三角・五角形 をクリックで落とし、 ドラッグで掴んで投げ、 重力を月や無重力に切替えて遊ぶ。 プリセット 4 種 (タワー / 振り子 / 鎖 / ピンボール) 付き。 物理計算 + 描画はすべてブラウザ内で完結。 ラボ 21本目、 シミュレーター ジャンル 1本目。

#なぜこの形にしたか

直近 5本は (tone-quest: インタラクティブ学習 / type-forge: ジェネレーター / wiki-orbit: データ可視化 / fate-num: 占い / roof-fund: 計算ツール)。 作ろうと決めていた 8 ジャンルで残りは シミュレーター・ロールプレイ と server-side AI 深処理 (許可必要)。 incident-sim は障害対応シミュ (運用領域) で技術 SaaS 寄りなので、 PilePark は 「クリエイティブ系シミュ = 触って遊ぶ」 で別領域として位置付けた。

候補比較:

  • 物理エンジン サンドボックス (採用)
  • 面接シミュレーター オフライン版 (server-side AI 無いと質が落ちる)
  • 飲み会幹事シミュ (Ruleベース、 wow 弱)
  • 謝罪文 シミュ (テキスト、 弱)

物理シミュは:

  • matter.js がラボ初の物理エンジン (野心スタック)
  • 一般人 wow: 「ボックスを落として、 重力を月にして、 全部浮く!」 が直感で伝わる
  • SNS シェア性高 (動画で映える)
  • 子供向け教育 + dev 好奇心 + TikTok 風 短尺動画ネタ の三方面で需要

#見た目をどう決めたか

直近 5本の 見た目 を書き出す:

tone-quest — wine burgundy + ivory + gilt gold (コンサートホール classical)
type-forge — charcoal + ember orange (warm-dark industrial 鍛冶)
wiki-orbit — bone paper + ink + vermilion (light theme 古地図 / 学術図譜)
fate-num — deep indigo + arcane gold (serif italic zodiac wheel)
roof-fund — drafting cyan + brass (graph paper architectural)

題材 「物理サンドボックス / 触って遊ぶ」 → モチーフ は 子供の砂場 / 玩具箱 / 紙工作:

  • 紙のような warm cream 背景
  • saturated primaries で 玩具らしいカラフルさ
  • rounded large radii + ink border + drop shadows で 「ステッカー / 紙細工」 感
  • ボタンは hover で push-up animation (toy-press 風)
  • hero title を rotate(-2deg / 1.5deg) で「貼り紙」 感

採用 3 要素:

  • 配色: warm cream #fef5e7 + coral #ff6b6b + mint #6dd5b0 + lemon #ffd93d + deep indigo #2a3266 (text/walls) + peach + sky
  • light theme 2本目 (wiki-orbit と並ぶ) だが、 wiki-orbit が 「古地図 = paper + ink + 古典」 なのに対し PilePark は 「砂場 = paper + saturated primaries + 玩具」 で完全別ジャンル
  • モチーフ: 子供の砂場 / 玩具箱 / 紙工作、 doodle dots (radial-gradient で散らした玩具色の粒)、 dashed border (子供の落書き感)、 large rounded buttons with hard ink shadows
  • typography: Space Grotesk 800 weight、 rotate transform で遊び心、 mono ラベルは uppercase tracking で 「博物」 感の残り香

#技術スタック

#matter.js + 自前 Canvas 描画

ts
const engine = Engine.create({ gravity: { x: 0, y: 1 } })
const ground = Bodies.rectangle(W/2, H, W, 60, { isStatic: true })
const mouseConstraint = MouseConstraint.create(engine, { mouse, ... })
Composite.add(world, [ground, mouseConstraint])
Runner.run(Runner.create, engine)

matter.js の Render (組込描画) は使わず、 自前 Canvas 2D で Composite.allBodies(world) を毎フレーム描く。 これで 見た目 を完全に統制できる:

ts
function render {
 ctx.clearRect(0, 0, W, H)
 // bg cream + grid

 ctx.fillStyle = "#fef5e7"; ctx.fillRect(0, 0, W, H)
 // walls (indigo)

 // dynamic bodies (color from plugin)

 // constraints (rope lines)

}

各 body に body.plugin.color = randomColor を仕込んで、 描画時に取り出す。

#Click vs Drag の区別

mousedown 時刻と位置を記憶 → mouseup 時に dt < 220ms && distance < 8px なら drop、 それ以外は drag 扱い (mouseConstraint が body を掴んでいたら drop しない)。 これで 「クリックで落とす」 と 「ドラッグで掴んで投げる」 が排他で両立。

#プリセット

  • Tower: 6 × 7 ブロックの市松配置。 chamfer: { radius: 3 } で玩具感
  • Pendulum: Constraint.create で 3 つの振り子、 最初の 1 つは Body.setPosition で持ち上げ
  • Chain: Composites.stack + Composites.chain で 12 連 × 2 本
  • Pinball: 28 個の peg を 4 段格子配置 (isStatic: true)

#重力切替

ts
function gravityValue(g) {
 if (g === "earth") return 1.0
 if (g === "moon") return 0.17
 if (g === "zero") return 0
 return -1.0 // inverted

}
engine.gravity.y = gravityValue(g)

リアルタイムに反映、 今飛んでる物体にも即適用。

#まだやっていないこと

  • ピンボール完全版 (フリッパー = Constraint ベース + ターゲット + スコア)
  • 形のサイズ / バネ係数 微調整 UI
  • 物理パズル モード (ゴール到達まで何手か、 ステージパック)
  • 共有 URL (シーン状態を base64 で埋め込み)
  • 録画 (canvas → mp4 / GIF 出力で SNS シェア)
  • タッチ最適化 (現状 mousedown / mouseup 前提、 タッチ対応も追加すべき)
  • Three.js への 3D 化 (将来 type-forge の 3D 資産と合流可能)
  • Rube Goldberg 風 自作シーン共有 プラットフォーム化

[ ./next_action ]

読んだら、 PilePark を実際に動かす。

この開発ログは PilePark をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ShadowTwin

立体パズルの別解を作る:同じ影と最少の積み木

ShadowTwinの開発記録。3方向の図から別々の最少配置を2つ探す仕組み、5問の独立全探索、スマホの横並び比較、保存・再編集と入力検査を解説します。実照明の計算や能力診断ではありません。

read log →
SlitCard

スリットアニメーションを作る:4コマの合成と透明原稿の設計

SlitCardの開発記録。4コマの帯を交互に合成し、すき間と黒帯で別のコマを見せる仕組みを解説。同縮尺の台紙・透明シートSVG、50mm確認線、手動プレビュー・初期非表示、再編集と実出力検査を整理します。

read log →
WindowLight

日当たりシミュレーションを作る:窓の光を床へ投影する幾何モデル

WindowLightの開発記録。太陽位置近似から窓の4隅を床へ投影し、凸多角形を部屋で切り取る設計を解説。国立天文台の16時刻との限定照合、大気差・低高度・実際の採光との違い、SVGと設定の再編集を整理します。

read log →
GridLights

電源構成の比較を作る:割合と実量を100個の電球で読み解く

電源構成比較GridLightsの開発記録。12か国の年次発電データを最大剰余法で100個の電球に変換し、割合とTWhを比較する設計を解説。欠損を0補完しない7分類、予想と実値、出典付きSVGと実データ検証を整理します。

read log →
SoundLoom

音のお絵かきを作る:実WAVから絵を測り直す設計

音のお絵かきの開発記録。描画を正弦波のPCMとWAVへ変換し、保存音声の周波数をGoertzel法で測って比較する仕組みを解説。音量・測定窓・量子化の限界と、編集・離脱時の古い結果を抑止する検証を整理します。

read log →
MazeLoom

宝探し迷路を作る:寄り道まで含めた最短ルートの探索

宝探し迷路の開発記録。通路を木として生成し、現在地と3つの宝の取得状態を組み合わせて最短ルートを探す仕組みを解説。問題コードの版固定、画面と解答付きPDFの一致、手数と印刷の検証を整理します。

read log →