# Natural Motion Lab
自然の動きを、ひとつずつ。
小さなルールから生まれる動きを触って探し、Webページへ持ち帰る実験集です。
LPの背景、フェスの余白、ブランドの小さな動きに使うことを目指します。
[公開された実験室を開く](https://natural-motion-lab.shoseilab.com/)
## 今ある作品
| 作品 | しくみ | 触れるところ |
| --- | --- | --- |
| 香りの流れ | Curl noise | 風、渦、広がり、寿命。ポインターでも流れを曲げられます |
| 渦の庭 | Curl noiseと回転する流れ | うねり、回転、細かさ、速さ、尾が残る時間、先端の太さ |
| 火の粉 | Curl noiseと上昇・慣性 | 横風、上昇、乱れ、寿命、粒の数、粒の大きさ、炎の長さ |
| 水中の光 | Curl noiseで揺れる光の帯 | 揺らぎ、光の向き、広がり、深さ、速さ |
| 育つ模様 | Gray–Scott反応拡散 | feed、kill、速さ。クリックで種を追加できます |
| 光の道 | 粘菌から着想した粒子モデル | 探索距離、旋回、痕跡の減衰、速度。道を描けます |
| 呼吸する光 | Swarmalators | 集まり方、同期の強さ、呼吸の速さ |
各作品に見本、シード、設定リンク、埋め込みコードがあります。
「流れの実験」では、香り・渦・火の粉・水中の光を切り替えられます。
最初の渦のデモを広げた「渦の庭」から始め、見本の「ひとつの大きな渦」も試せます。
リンクが保存するのは初期設定です。途中の模様、操作履歴、一時停止状態は保存しません。
同じ設定とシードなら同じ初期状態から始まりますが、異なる計算方式や端末間の画像の完全一致は保証しません。
## 起動する
Bunがあれば、外部パッケージのインストールなしで動かせます。
```sh
cd natural-motion-lab
bun scripts/dev.mjs
```
[ローカルの実験室](http://127.0.0.1:4178/) を開きます。終了はターミナルで `Ctrl+C`。
サーバーはこの端末のループバックだけで待ち受けます。ローカルURLは他の人の端末からは開けません。
`just` がある場合は、次のコマンドも使えます。
| コマンド | 内容 |
| --- | --- |
| `just dev` | 編集中のサイトを表示 |
| `just check` | 数値計算・設定の検証とMarkdownチェック |
| `just wasm` | MoonBitからWasmを再生成 |
| `just bench` | 同じ反応拡散をJavaScript / Wasmで計測 |
| `just build` | 検証後、公開用の `dist/` を生成 |
| `just preview` | `dist/` をローカルで表示 |
`dev` と `preview` は同じポートを使います。切り替えるときは先に動いている方を終了してください。
`check` / `build` には `rumdl`、Wasmの再生成にはMoonBitが必要です。
確認環境はBun 1.4.2、moon 0.1.20260915、moonc v0.10.13+cbb11c36fです。
生成済みの `assets/reaction.wasm` を同梱しているので、閲覧と通常のビルドにMoonBitは不要です。
MoonBitのソースを変更した場合は `just wasm` → `just check` → `just build` の順で再生成します。
## ページへ組み込む
`src/` と `assets/` を、組み込み先の `natural-motion/` ディレクトリにコピーします。
次の例は、装飾用のキャンバスと停止操作だけを置きます。
```html
```
動く完成例は [examples/landing.html](examples/landing.html) にあります。
`file://` で直接開かず、HTTPサーバーで配信してください。
ES Modules、Canvas 2D、ResizeObserver、IntersectionObserverを使う現行ブラウザー向けです。
- `effect`: `flow` / `vortex` / `embers` / `underwater` /
`reaction` / `physarum` / `swarm`
- `seed`: 0〜4294967295の整数
- `params`: 各作品の調整値。範囲と既定値は [src/catalog.js](src/catalog.js)
- `engine`: 反応拡散のみ `auto` / `js` / `moon`。Wasmが読めない場合はJavaScriptへ切り替わります
- `interactive: true`: キャンバス上の操作を有効化。装飾用途では既定の `false` を使います
- `paused`: 初期停止状態。省略するとOSの「視差効果を減らす」設定に従います
返り値の `setParams({...})`、`setPaused(true)`、`destroy()` で外側から操作できます。
別の種類やシードに変える際は `destroy()` 後に作り直します。
画面外・別タブでは計算を止め、OSの動きを減らす設定にも応答します。
作品ごとにキャンバスと計算状態が独立しています。
## 静的サイトとして共有する
`just build` の出力 `dist/` を静的ホスティングへ配置できます。
GitHub Pagesなどのサブディレクトリ配信を想定し、アセットは相対パスです。
ブラウザー以外の実行環境、APIサーバー、外部CDNは不要です。
JavaScriptを適切なMIME型で配信し、可能なら `.wasm` を `application/wasm` に設定します。
この実装はWasmをArrayBufferから読み込むため、WasmのMIME型だけで失敗しません。
ソースはGitButlerで管理し、GitHubの `t-edzuka/natural-motion-lab` 非公開リポジトリで共有します。
サイトはCloudflare Workers Static Assetsで公開する構成です。
ブラウザーで実行するJavaScriptとWasmは配信されます。GitHubを非公開にしても、配信されたコードは閲覧できます。
自作コードは [MIT License](LICENSE) とし、再利用できる形にしています。
配信にはプロジェクト内で固定したWranglerを使います。
```sh
bun install --frozen-lockfile --ignore-scripts
bun run deploy:check
bun run deploy
```
GitHub ActionsはPRで検証し、`main` の更新時に検証後デプロイします。
自動デプロイはproduction環境のSecret登録後、
リポジトリ変数 `DEPLOY_ENABLED=true` で有効にします。
認証と運用手順はリポジトリ内の `docs/deployment.md` に記録します。この文書はサイトへ配信しません。
## 実装の方針
画面は標準HTML / CSS / JavaScript、計算が重い反応拡散だけMoonBitのWasmを試しています。
同じモデルのJavaScript版を残し、比較とフォールバックに使います。
MoonBitは固定長の線形メモリーと少数の数値関数だけを公開し、言語固有のオブジェクト表現を渡しません。
描画・数値計算に外部ライブラリは使っていません。配信ツールのWranglerだけを開発依存として固定しています。
火の粉と水中の光は写真の形・明暗を参考にした演出です。速度は実物の動画との照合前です。
次は気に入った作品を一つ選び、実物の動画を観察して、形・速度・消え方を磨く予定です。
炎、風に揺れる草、水面の干渉波も候補です。
粒子数や解像度を上げて限界が見えたら、Zig / Rustの比較やWebGL / WebGPUを検討します。
数学的な前提、言語の選び方、検証結果は [調査と制作ノート](docs/research.md) にまとめています。