{
  "markdown": "# VJ Overlay Tool\n\nブラウザだけで動く、音に反応するフルスクリーン・ビジュアルツールです。マイク / ライン入力 / ループバック（デスクトップ音声）を解析し、なめらかで音楽に反応するビジュアルを描画します。OBS のブラウザソースや、プロジェクター用のフルスクリーンブラウザに常駐させて使う前提で設計しています。\n\n- **既定シーンは Semantic Synth**。seed から GLSL シェーダそのものを合成する生成シーンです（後述）\n- **Canvas 2D を手書き**で描画（three.js / p5 などの外部描画ライブラリは不使用）\n- さらに **WebGL2 による GPU シーン**（Fluid Ink / Smoke / Lava / Aurora）を追加。リッチで有機的なアンビエント映像（後述）\n- 透過背景に対応。OBS のオーバーレイとしてそのまま重ねられます\n- 11 のシーン（生成: Semantic Synth、2D: Bars / Waveform / Particles / Radial / Rings / Lissajous、GPU: Fluid Ink / Smoke / Lava / Aurora）\n- **シード（look gacha）** によって全シーンの見た目が劇的に変化します（後述）\n- 設定は localStorage に保存され、URL パラメータで上書き可能\n\n## 技術スタック\n\n- Vite + React 18 + TypeScript (strict)\n- 描画・音声解析は素の TypeScript クラス（React は UI / 設定のみ）\n- ランタイム依存は React のみ\n\n## クイックスタート\n\n```bash\npnpm install\npnpm dev\n```\n\nブラウザで表示された URL（通常 `http://localhost:5173`）を開きます。初回はマイク（音声入力）の許可を求められます。**許可するとビジュアルが音に反応し始めます。**\n\n- ブラウザの自動再生制限により、起動直後は音声が止まったままのことがあります。その場合はパネルの **「▶ Click to start audio」** ボタンを一度クリックしてください。\n- 入力デバイスはパネルの **Input device** から選べます（許可後にデバイス名が表示されます）。\n\n## キーボードショートカット\n\n入力欄にフォーカスがあるときは無効になります。\n\n| キー | 動作 |\n| --- | --- |\n| `1` – `9`, `0` | シーンを番号で選択（`1` = Semantic Synth、`0` = 10 番目のシーン） |\n| `←` / `→` | 前 / 次のシーンへ |\n| `H` | コントロールパネルの表示 / 非表示 |\n| `F` | フルスクリーン切り替え |\n| `A` | オートサイクル（自動シーン切替）の ON / OFF |\n| `B` | 背景（黒 / 透過）の切り替え |\n| `R` | シード（look gacha）を引き直す（ガチャ） |\n| `D` | Semantic Synth の Generator 構成はそのまま、細部だけ引き直す（reroll details） |\n| `S` | Scene Deck 窓を開く（`?deck=1`。BroadcastChannel、bridge 不要） |\n| `T` | タップテンポ（手動 BPM 入力） |\n\nパネルはマウスを止めると約 3 秒で自動的にフェードアウトし、カーソルも隠れます。マウスを動かすと再表示されます。\n\n## シード（look gacha）\n\nパネル上部の **Look** にシード文字列を入力すると、その文字列から**決定論的に**ビジュアル全体の「個性」が生成されます。色だけでなく、配色モード・要素数・サイズ・モーション速度・対称数・回転方向・ゆらぎ、そして各シーンの**レイアウト（variant）**まで一括で変化するので、シードを引き直すたびにガチャを引くように見た目が大きく変わります。\n\n- パネルの **🎲 ボタン**（またはキーボードの `R`）を押すと、`neon-tiger-042` のような読みやすいランダムシードが生成され、即座に反映されます。\n- 同じシード文字列からは**必ず同じ見た目**が再現されます（フレームをまたいでも安定）。気に入った見た目はシード文字列を控えておけば再現できます。\n- シード入力欄は **Enter キー** または**フォーカスを外した（blur）**タイミングで適用されます（1 文字ごとには反映されません）。\n- 各シーンは音への反応（bass / level / beat）を維持したまま、シードに応じた `variant`（0〜3 の離散レイアウト）で描画されます。たとえば Bars なら「下から」「中央ミラー」「上下対向」「左右から内側へ」、Particles なら「上昇」「周回」「雨」「スターフィールド」など。\n\n## GPUシーン（WebGL）\n\n2D の手書きシーンに加えて、**WebGL2 のフラグメントシェーダ**で描く 4 つの「リッチ」シーンを搭載しています。ノイズ（FBM）・メタボール・流体シミュレーションを GPU で評価して、有機的で立体感のある映像を生成します。\n\n- **Fluid Ink（水面の墨流し / マーブリング）**: GPU 上で本物の流体シミュレーション（Stable Fluids ＋ 渦度強化）を毎フレーム解き、見えない「スターラー」がゆっくり水をかき混ぜながらインクを注ぎ込みます。低域が渦の力を、音量がインクの量を、中域が色相のドリフトをゆったり押し上げ、墨流しのように色が折り畳まれていきます。`variant` で「マーブリング」「縁からのジェット」「マンダラ（対称マーブル）」「リボン」の 4 レイアウトに変化します。浮動小数点レンダリング非対応の GPU では簡易マーブル表現（FBM ＋ ドメインワープ）に自動フォールバックします。\n- **Smoke（煙 / 霧）**: 5 オクターブの FBM をドメインワープ（別の FBM で参照位置を歪める）してもやもやと立ち上る煙・霧を描きます。`variant` で「立ち上る筋」「横に流れる霧の帯」「中心を巡る渦」「細いお香の煙」の 4 レイアウトに切り替わります。\n- **Lava（ラバランプ）**: CPU 側で 8〜18 個のメタボールを浮力でゆっくり昇降させ（壁でソフトバウンス、横ゆらぎ）、シェーダで smooth-min 場を評価して縁・内部グラデーション・リムライト・外周グローを付けます。`variant` で「クラシックなランプ」「横流れ」「中心周回」「左右ミラー」に変化します。\n- **Aurora（オーロラ / 星雲）**: 縦方向に減衰する正弦波の帯を FBM でワープし、速度・色相の異なる複数レイヤーを視差合成します。`variant` で「上から垂れるカーテン」「地平線グロー」「星雲（雲状 FBM ＋ 星のきらめき）」「太い 1 本のリボン」に変化します。\n\n### 音への反応（アンビエント設計）\n\nビートで爆発させるのではなく、**音楽にゆったり呑まれる**ように、各シーンが内部で秒スケール（時定数 0.5〜2 秒）に平滑化した状態を持ち、音をなめらかに吸収します。\n\n- **低域（bass）**: 大きなうねり・ドリフト速度・渦の強さなど、大局的なモーションを揺らし、強めます。\n- **中域（mid）**: 色温度（パレットのブレンド）をゆっくりシフトします。\n- **高域（treble）**: 細かなシマー（さざ波・きらめき）を加えます。\n- **全体音量（level）**: 密度・明るさを底上げします。\n- ビートパルスは**ごく控えめ**（最大 15% 程度）にしか効きません。\n- 音声が止まっていても、各シーンは時間だけで穏やかに動き続けます（アイドルドリフト）。\n\n### パフォーマンスと対応環境\n\n- **ノートPC の内蔵 GPU（iGPU）での 1080p 常用**を想定し、手描き Canvas シーンとは別レイヤーで描画します。\n- GL キャンバスの **DPR（デバイスピクセル比）は最大 1.5 に制限**しています（高解像度ディスプレイでの過負荷を回避）。\n- **WebGL2 が必須**です。非対応の環境では GPU シーンは**自動的に無効化**され（シーン選択でグレーアウト・`（WebGL2なし）` 表示）、2D シーンのみで動作します。コンテキストロスト時も自動復帰します。\n- 透過は 2D シーンと同様に保たれるので、OBS の透過オーバーレイにそのまま使えます。\n\n> シーンキーは **`1`〜`9`、`0`**（10 番目）です。シーンは 11 個あるので、末尾の 1 つ（Aurora）だけ数字キーの割り当てがありません。`←` / `→`・シーンピッカー・`?scene=aurora` から選べます。\n\n## Semantic Synth（生成シンセシーン・既定）\n\n固定の描画ロジックを持つ 10 シーンとは別に、**seed から GLSL シェーダそのものを合成するシーン**「Semantic Synth」を搭載しています。**これが既定のシーン**で、何も指定しなければ起動時に開きます（キー `1`、または `?scene=semantic-synth`）。\n\n> 既に一度使ったブラウザでは、最後に選んだシーンが localStorage に残っているのでそちらが優先されます。明示的に開くには `?scene=semantic-synth` かキー `1` を使ってください。\n> WebGL2 が使えない環境では自動的に Bars（2D）へフォールバックします。\n\n- Source / Field / Modifier / Material の 4 カテゴリ・計 100 種類の Generator の中から、seed に基づいて決定的に組み合わせを選び、GLSL を組み立てて描画します。あらかじめ用意された固定シーンではなく、**都度合成される「実験的な」シーン**です。\n- 選ばれる組み合わせは look gacha の **シードがそのまま効きます**。同じシードなら同じ Generator 構成・パラメータ・Look が常に再現されます。\n- シードを引き直す（🎲 / `R`）と、他のシーンと同様に**ハード切替ではなくクロスフェード**で新しい Look へ滑らかに遷移します。\n- **Generator の構成（どの operator を使うか）は変えずに、パラメータ・変調・配色・composition だけ変えたい**こともあります。その場合は 🎲 の隣の **🧬 ボタン**（またはキーボードの `D`）を使ってください。同じ Generator 構成のまま細部だけ新しいシードで引き直されます（`vj-tweak.mjs --reroll` でも同じことができます）。構成そのものは変わらないので、後述のオーディオ・リアクション（拍で起きるグリッチの種類）も変わりません。\n- bass / mid / treble / level などの音声解析結果が Patch 内のパラメータを変調し、他の GPU シーンと同様に音楽にゆったり反応します。\n\n#### 拍で起きることは Patch ごとに違います（オーディオ・リアクション）\n\nGenerator は 105 個中 8 個しか音の uniform を読まないので、**画面全体に効く音の反応は Patch 共通の層**が担っています。以前はここが「軽いズームイン＋明るさの持ち上げ」の 1 種類しか無く、どのシードを引いても拍で起きることは同じでした。今はこの層がカタログになっていて、**Patch ごとに座標系の反応 1〜2 種類＋色の反応 1 種類**が決定的に選ばれます。\n\n| 座標に効く | 色に効く |\n| --- | --- |\n| `punchZoom` 拍の頭で画面が寄る | `rgbSplit` 拍で R/B が横にずれる（色収差グリッチ） |\n| `sliceShift` 横帯ごとに水平にずれる（テープが飛ぶようなグリッチ） | `echoGhost` 拍で少し縮んだ残像が背後に重なる |\n| `blockCrush` 拍でマクロブロックに潰れる | `hueSlam` 拍で色相が飛ぶ |\n| `jitter` 拍で画が飛び、高域で細かく震える | `posterCrush` 拍で階調が段々に潰れる（ビットクラッシュ） |\n| `spinKick` 拍で回転が蹴られる | `invertFlash` 強い拍だけ一瞬ネガになる |\n| `waveTear` 走査線が横に裂けて流れる | `scanGlow` 走査バンドが流れ、拍で光る |\n| `mirrorSnap` 拍で鏡像に畳まれる | `channelRoll` 強い拍で RGB が入れ替わる |\n\n- どれが載るかは **Operator の構成（トポロジ）から決まります**。シードを引き直して構成が変われば、拍で起きることも変わります。\n- 乱数のパターン（どの帯がずれるか等）はシードで変わるので、同じリアクションでも Patch ごとに違う崩れ方をします。\n- `rgbSplit` / `echoGhost` は拍の瞬間だけシェーダを追加で評価します。重い Patch（heavy な Generator 入り、または厚いスタック）には載りません。\n- **無音では完全に無効**です。リアクション層は音が止まると恒等変換に落ち、Patch 素の見た目に戻ります。\n- 変調ルート（Patch 内パラメータへの結線）側も、「大きさ」に偏らないよう効き方ごとに重みを付けて選んでいます。歪み・輝き・動きにも同じくらい票が回ります。\n\n#### 動きは音で駆動されます\n\n固定 10 シーンの「アイドルドリフト」（音が無くても時間だけで動き続ける）とは違い、Semantic Synth の時間は**音の大きさで進む速さが変わります**。\n\n- **無音のときはほぼ止まります**（通常速度の 5% 程度）。引きによって「音と関係なくギュインギュイン動く」Patch が出てしまうのを、Generator 側ではなく時間軸の側で止めています。\n- 音が入ると素早く等速まで立ち上がり、止まったあとは 1 秒弱かけてゆっくり減速します（急停止するとカクついて見えるため）。\n- BPM がロックしていれば全体の速さがテンポに追従し（0.7〜1.25 倍）、拍の頭で少しだけ前に押されます。ブレイク中はグリッドが自走していても**無音なら動きは増えません**。\n- Patch ごとの動きの速さ（`composition.speed`）も効きます。上限は 1 倍なので、引きによって従来より速くなる Patch はありません。\n\n#### 音で画が消えることはありません\n\n大音量が続く環境（クラブなど）で画面が真っ暗になり続ける、という事故を構造的に防いでいます。\n\n- 自動生成される変調ルートは**必ず unipolar かつ正の量**なので、音は常に「足す」方向にしか効きません。無音時の見た目が下限で、音が入るほど増えます。\n- 変調先は「増える = 見える / 動く」パラメータの**許可リスト**に限定しています。`threshold` / `gate` / `dropout` のような、上げると絵が消えるパラメータは自動では選ばれません（手で組んだ Patch には制限はかかりません）。\n- 全 Patch 共通で、拍に合わせた軽いズームイン（5%）と明るさの持ち上げ（15%）、音量に応じたズーム（6%）が main() に入ります。Generator 側が音の uniform を読まなくても最低限は音に反応します。\n- その上に載るオーディオ・リアクションも、**暗くする方向の効果は拍のエンベロープにだけ**乗せています（音量では暗くしません）。走査線のような表現も明るくする側だけで作ってあるので、大音量が続いても減光は持続しません。\n- 負荷が高い環境ではフレーム時間を監視し、**内部レンダリング解像度を自動的に段階的に下げて**フレームレートを維持します（余裕が戻れば解像度も自動的に戻ります）。\n\n### Timeline パネル\n\n画面右側の **Timeline パネル**では、数秒〜数小節先の演出イベント（シード変更やトランジションなど）をあらかじめ予約でき、演出の流れを JSON として記録・再生できます。詳細は Issue #3 の RFC を参照してください。\n\n## Scene Deck（別窓ポン出し）\n\nResolume のシーン一覧のように、**別窓で 8 スロットのバリエーションをポン出し**できます。メイン窓（semantic-synth）のいまの Patch をベースに、同じ Generator 構成のままパラメータ違いを 8 面自動生成し、キーボードで即遷移させます。通信は同一オリジンの **BroadcastChannel**（`vj-deck-v1`）だけで、`pnpm bridge` も `?bridge=1` も不要です。\n\nデッキ窓では AudioEngine / Renderer は起動しません（マイク許可プロンプトも、常時の GPU 負荷も無し）。メイン窓をプロジェクター出力、Deck を手元の操作卓にする運用を想定しています。\n\n### 現場セットアップ\n\n1. メイン窓を `?ui=hide` で開く（必要なら `?scene=semantic-synth&seed=…`）\n2. メイン窓で **`F`** を押してフルスクリーンにする。Fullscreen は**メイン窓側のユーザー操作が必要**で、Deck からリモートでは入れない\n3. メイン窓で **`S`** を押して Deck を開く\n4. 以後の操作（seed ガチャ / シーン / hue / 背景 / タップテンポ / lock）は全部 Deck から\n\n### 開き方\n\n- コントロールパネルの **Scene Deck** ボタン\n- メイン窓で **`S`**（`D` は reroll details。Issue #51 では `D` と書いてあったが、PR #49 で `D` が細部ガチャに使われているため `S`）\n- URL に `?deck=1`（または `?deck=true`）。窓名 `vj-scene-deck` なので連打しても窓は増えない\n\n### 使い方\n\n1. メイン窓を semantic-synth にする\n2. デッキ窓を開くと、現行 Patch から 8 面（BASE + V1…V7）が生成される。WebGL2 があればサムネイルが 1 枚ずつ埋まる（無ければ色チップのまま）。画像（サンプラー）を使う Patch はサムネイルを透明なダミーテクスチャで描くため、ほぼ空の絵になる（スロットはラベルと detail で判別できる）\n3. `1`–`8` でポン出し。`Shift+数字` は選択中プリセットを無視して cut。`X` でトランジションを cut → default → slow と巡回（以前は `T`。`T` はタップテンポに変更）\n4. `←` `→` `↑` `↓` で 4×2 のカーソル、`Enter` / `Space` でそのスロットを出す。`Shift+←` / `Shift+→` はメイン窓のシーン送り\n5. `R` でライブ Patch を取り直して再生成、`G` で base を保ったまま bankSeed ガチャ\n6. 自動送り: `A` で ON/OFF、`M` で秒 / 小節、`-` / `=` で間隔。順序はツールバーの seq / rnd（rnd はいまのスロット以外）\n7. 小節モードはメイン窓が tempo LOCK のときだけ進む。外れたら警告して待つ。切断中は自動送りは止まる\n8. 操作卓: `Q` seed ガチャ、`W` 細部 reroll、`B` 背景、`H` hue 固定トグル、`[` / `]` hue ∓15°、`T` タップテンポ、`,` / `.` テンポ ÷2 / ×2、`/` テンポ AUTO、`L` Timeline lock 30s トグル、`Shift+A` メイン窓オートサイクル\n9. 手札は自動保存される。`A`〜`H` はクリックで呼出、`Shift+クリック` で保存。`Shift+S` は次の空きスロットへ。JSON copy / paste で持ち運び（詳細は「手札の保存」）\n\n| キー | 動作 |\n| --- | --- |\n| `1`–`8` | スロットをポン出し |\n| `Shift+数字` | cut でポン出し |\n| `←` `→` `↑` `↓` | 4×2 カーソル |\n| `Shift+←` / `Shift+→` | メイン窓のシーン送り |\n| `Enter` / `Space` | カーソル位置を出す |\n| `T` | タップテンポ（**変更**。以前はトランジション巡回） |\n| `X` | トランジション巡回（`T` から**移動**） |\n| `,` / `.` | テンポ ÷2 / ×2 |\n| `/` | テンポ AUTO |\n| `Q` | seed ガチャ（メイン窓 `R` 相当。Deck の `R` は bank 再生成） |\n| `W` | 細部 reroll（メイン窓 `D` 相当） |\n| `B` | 背景トグル |\n| `H` | hue 固定トグル（fixed に入るとき現在 hue を固定） |\n| `[` / `]` | hue 固定 ∓15° |\n| `L` | Timeline lock 30s ⇄ 解除 |\n| `Shift+A` | メイン窓のオートサイクル ON/OFF |\n| `A` | Deck 側 auto 送り ON/OFF |\n| `M` | auto の秒 / 小節 |\n| `-` / `=` | auto 間隔 |\n| `R` | ライブ Patch から bank 再生成 |\n| `G` | bank ガチャ |\n| `Shift+S` | いまの手札を次の空きスロット（無ければ `A`）へ保存 |\n| `F` | **この窓**のフルスクリーン（メイン窓の出力全画面にはならない） |\n\n手動でポン出した位置から自動送りが続きます（秒タイマーはリセット）。バンクはトリガーのたびに作り直さないので、「さっきの 3 番」が消えません。Claude Code / `vj-tweak` / seed ガチャでメインの画がバンクの外に出たら **BASE CHANGED** が出るので、`R` で取り直してください。\n\n### MIDI\n\nScene Deck 窓だけで Web MIDI を受けます（メイン窓には載せません）。**Deck が背面・非フォーカスでも効きます**（Web MIDI はフォーカス不要）。\n\n- **Chrome / Edge**: 標準対応\n- **Firefox**: Web MIDI 用の site permission add-on が必要（108+）\n- **Safari**: 非対応。Deck は落ちず「MIDI 非対応」と出ます\n\n使い方:\n\n1. Deck の MIDI パネルで **MIDI** を押す。権限は **2 段**（MIDI 本体と SysEx）。両方許可する\n2. nanoPAD2 は SysEx の **Search Device**（Family ID `12 01`）で確定し、**Native KORG Mode** に入る。`native` バッジが出る。入れなければ Current Scene Data Dump でパッドの Note# からマップを自動生成する\n3. **learn** を ON にし、割り当てたい action を選んでパッド / CC を叩く（nanoPAD2 以外、または Native を使わない場合）。同じトリガーが既にあれば上書き（警告）。`Esc` で learn 終了。learn 中もキーボードのパッドは使える\n4. マッピングは `localStorage` キー `vj-deck-midi-v1`（`{ activeMapping, nanopad: { preferNative, swapRows } }`）。JSON 書き出し / 読み込みで別 PC へ持てる\n\n**Native mode の in / out**: 接続時に Native In、Deck 窓を閉じる（またはリロードする）ときに Native Out を送る。送らないと nanoPAD2 が Native のまま残り、他アプリで普段のノートが出なくなる。戻らなければ **USB を抜くか電源再投入**。\n\n**Scene LED**（Native mode のみ）:\n\n| LED | 意味 |\n| --- | --- |\n| Scene 1 | host 接続中（conn live） |\n| Scene 2 | Deck auto ON |\n| Scene 3 | tempo LOCK |\n| Scene 4 | learn 中。エラー時は 500ms 点滅 |\n\n**既定マップ**（Native。上下が逆ならパネルの「上下入替」）: 下段 8 パッド（Note 72–79 / ch 2）で slot 1–8、上段（Note 64–71）で cut、Scene ボタン（CC 57 / ch 16）でタップテンポ、X-Y タッチ（CC 11）でカーソル発火。X/Y 軸は未割り当て（learn で hue / interval などに）。Touch Scale の `92/82/B2` は無視。\n\nベロシティ cut は MIDI パネルの `cut ≥`（1–127）。空欄でオフ。強く叩いたら cut、弱ければ選択中 preset。\n\n### 手札の保存\n\nバンク（BASE + V1…V7）は **base Patch と bankSeed** だけを保存します。8 面そのものは持たず、同じ入力なら `buildSceneBank` が同じ手札を再生成します。\n\n- **自動保存**: バンク / seed / トランジション / auto / カーソルが変わるたびに 500ms debounce で `localStorage` キー `vj-deck-banks-v1` の `current` を更新する（クラッシュ耐性）。Deck 起動時に `current` があれば **メイン窓の currentPatch を待たずに** 復元する。未保存なら従来どおり live から生成する\n- **A〜H**: ツールバーの小ボタン。クリックで呼び出し、`Shift+クリック` で保存（上書き確認なし）。空は薄く表示。右クリック / 長押しで名前を編集。呼び出しはバンクを差し替えるだけで **pad は自動では叩かない**し **`seed:set` も送らない**（live の画は pad 1 などを押すまで変わらない）。menu の **seed を採用** を押すと `seed:set` が送られ、Settings.seed とメインの画の両方が作り直される\n- **JSON**: `JSON copy` でいまのスナップショットをクリップボードへ。`menu` 内のテキストエリアに貼って `JSON paste`。別 PC・別ブラウザへ持てる\n- **別 PC では画像が欠ける**: Patch は画像の hash 参照だけを持ち、ピクセルは同一オリジンの IndexedDB `vj-images` にある。別マシンへ JSON だけ持っていくとサンプラーは透明ダミーになる（スロットはラベルと detail で判別できる）\n- **STALE**: 復元した base が現行 catalog で invalid（generatorVersion の更新など）。バンクはフォールバック（バリエーションが全部 BASE 相当）で出すので落ちない。`R` で live から取り直す\n- **クリア**: menu 内の `clear current` → `confirm clear current`（2 段）。次回起動は live から生成する。A〜H は消さない\n\n## テンポ同期（BPM 検出）\n\n入力音声からリアルタイムに BPM を推定し、ビートグリッドを生成して各シーンのパルスやオートサイクルを音楽の拍に合わせます。パネルの **Tempo** セクションに現在の BPM とステータスチップ、拍位置を示す 4 つのドット（1 拍目＝ダウンビートを強調）が表示されます。\n\n- **自動検出**: 低域（バス）のオンセットから自己相関で BPM を推定します。**四つ打ち（キックが規則的な曲）に強く**、ロックまでは**数秒**かかります。検出が安定すると `LOCK`、探索中は `SEARCH…` と表示されます。\n- **フリーホイール**: 一度ロックすればビートグリッドは自走するので、**ブレイク中（無音区間）でも拍を刻み続けます**。曲が戻ると位相を緩やかに再同期します。\n- **TAP / ×2 / ÷2 / AUTO ボタン**:\n  - **TAP**: ボタン（またはキーボードの `T`）を曲に合わせて 2 回以上叩くと、その間隔から手動で BPM を設定します（`TAP` 表示）。音声が止まっていても手動テンポでグリッドが回ります。\n  - **×2 / ÷2**: テンポを倍 / 半分にします（0.25〜4 倍の範囲）。ハーフタイム / ダブルタイムの曲に合わせるときに便利です。\n  - **AUTO**: 自動検出に戻します（倍率も 1× にリセット）。\n- **小節単位オートサイクル**: Auto-cycle の **Sec / Bars** トグルを **Bars** にすると、秒ではなく**小節（4 拍 = 1 小節）単位**でシーンを自動切替します（1〜256 小節）。BPM がロック / タップされている間だけ進みます。\n- **N バーごと自動ガチャ**: **🎲 Auto-gacha** を On にすると、指定した小節数ごとに look gacha のシードを自動で引き直します（1〜512 小節）。曲の展開に合わせて見た目が周期的に変わります。\n\n> 検出は低域のオンセットを手がかりにするため、キックのはっきりした 4 つ打ち系で最も安定します。ビートが取りづらいジャンルでは **TAP** で手動設定するのが確実です。\n\n## URL パラメータ\n\n`?key=value` 形式で起動時の設定を上書きできます（localStorage より優先）。\n\n| パラメータ | 例 | 説明 |\n| --- | --- | --- |\n| `scene` | `scene=aurora` | 起動時のシーン id（既定 `semantic-synth` / 2D: `bars` `waveform` `particles` `radial` `rings` `lissajous` / GPU: `fluid` `smoke` `lava` `aurora`） |\n| `bg` | `bg=transparent` | 背景（`black` / `transparent`） |\n| `ui` | `ui=hide` | コントロールパネルを最初から非表示にする（OBS 用） |\n| `gain` | `gain=2` | 入力ゲイン（0.5 – 4） |\n| `cycle` | `cycle=20` | オートサイクルの間隔（秒） |\n| `autocycle` | `autocycle=1` | オートサイクルを ON にする（`1` / `true`） |\n| `cyclemode` | `cyclemode=bars` | オートサイクルの単位（`seconds` / `bars`） |\n| `cyclebars` | `cyclebars=16` | 小節単位オートサイクルの間隔（1 – 256 小節） |\n| `autogacha` | `autogacha=1` | N バーごとの自動ガチャを ON にする（`1` / `true`） |\n| `gachabars` | `gachabars=32` | 自動ガチャの間隔（1 – 512 小節） |\n| `hue` | `hue=200` | 色相を固定（0 – 360、指定すると hue 固定モードになる） |\n| `seed` | `seed=neon-tiger-042` | look gacha のシード（見た目の個性を決める文字列。最大 64 文字） |\n| `device` | `device=<deviceId>` | 入力デバイス id を指定 |\n| `overlay` | `overlay=rings` | シーンを2層合成する（下＝`scene`、上＝`overlay`）。未指定/`none`/`off`/`0` で無効 |\n| `blend` | `blend=screen` | オーバーレイ合成モード（`normal` / `screen` / `multiply` / `overlay` / `difference` / `exclusion` / `color-dodge` / `hard-light` / `lighten` / `darken`）。オーバーレイ有効時のみ。未指定は `normal`（従来どおり） |\n| `mirror` | `mirror=1` | 中継に表示専用（mirror）として接続する。`room` または `bridge` と併用。応答は返さず同じコマンドを映像に反映するだけ |\n| `deck` | `deck=1` | Scene Deck 窓として開く（`1` / `true`）。Audio / Renderer は起動しない |\n\n例: 透過背景・パネル非表示・パーティクルシーンで起動\n\n```\nhttp://localhost:5173/?scene=particles&bg=transparent&ui=hide&gain=2\n```\n\n複数台の端末に同じ映像を出したいときは、1 台を通常の synth（`room` / `bridge` のみ）として接続し、残りを `mirror=1` 付きで開きます。mirror は表示専用で何台でも接続でき、中継からのコマンドを映像に反映するだけで応答は返しません。\n\n### シーンオーバーレイ（2層合成）\n\n`overlay` パラメータを指定すると、下のレイヤーの `scene`（既定 `semantic-synth`）の上に、もう1つのシーンを重ねて合成描画できます。両レイヤーとも同じ音声解析結果（gain / hue / variation）を共有するので、見た目の一貫性は保たれたまま2つのビジュアルが重なります。\n\n```\nhttp://localhost:5173/?scene=semantic-synth&overlay=rings\n```\n\nブレンドモードを付ける例:\n\n```\nhttp://localhost:5173/?scene=semantic-synth&overlay=rings&blend=screen\n```\n\n- **URL 指定のみ**: `scene` などと違い `overlay` は localStorage に保存されません。一度 `?overlay=rings` を開いただけでオーバーレイが固定されてしまわないようにするための仕様です。\n- **不正な値は警告のうえ無視**: 未知のシーン id や、ベースシーンと同じ id を指定した場合はオーバーレイを無効化し、コンソールに警告を出します（画面は通常描画のまま）。無効化は `none` / `off` / `0`（大小文字不問）でも明示できます。\n- **ベースシーンの切り替えは独立**: `1`–`9` / `[` `]` / シーンピッカーでベースシーンを切り替えても、オーバーレイは指定したまま維持されます。切り替え先がオーバーレイと同じシーンになった場合は、その間だけオーバーレイが一時的に抑制されます。\n- **パフォーマンス**: 2層分の描画コストがかかるため、GPU シーン同士を重ねるなど負荷の高い組み合わせでは fps に注意してください。\n- **`blend`（合成モード）**: 上レイヤー（オーバーレイ）に対して効きます。キャンバスが分かれている組み合わせ（2D ベース + GL オーバーレイ / GL ベース + 2D オーバーレイ）では CSS `mix-blend-mode` を上側キャンバスに適用します。2D+2D（同一キャンバス）ではオーバーレイ描画パスだけ `globalCompositeOperation` を切り替えます。GL+GL は共有コンテキストのため CSS が使えず、非 `normal` 指定時はコンソールに警告を出し既存の GL ブレンドのまま描画します。`blend` も URL 専用で localStorage には保存されません。不正値は警告のうえ `normal` にフォールバックします。オーバーレイが無いときはブレンドは適用されません。Bridge 接続時は CLI からも切り替えできます: `node scripts/vj-ctl.mjs blend screen`（`state` の `blendMode` で確認）。\n\n## OBS ブラウザソースとして使う\n\n1. OBS で **ソース → 追加 → ブラウザ** を選びます。\n2. **URL** に上記の URL パラメータ付きアドレスを入力します。オーバーレイ用途では透過＋パネル非表示が便利です:\n\n   ```\n   http://localhost:5173/?bg=transparent&ui=hide&scene=radial\n   ```\n\n   （`pnpm build` 後に `pnpm preview` やホスティング先の URL を使ってもかまいません。）\n3. **幅 / 高さ** を出力解像度に合わせます（例: 1920 × 1080）。\n4. `bg=transparent` を付けておけばキャンバスは透過のままなので、他のソースの上に重ねられます。\n\n### デスクトップ音声（曲）に反応させたい場合\n\nブラウザの「マイク入力」はマイクの音しか拾いません。BGM や DAW の音に反応させるには、**ループバック（仮想オーディオ）デバイス**を入力に選びます。\n\n- **Windows**: [VB-CABLE](https://vb-audio.com/Cable/) などの仮想オーディオデバイスを入れ、再生先を CABLE Input に、本ツールの Input device を **CABLE Output** に設定します。\n- **macOS**: [BlackHole](https://existential.audio/blackhole/) や Loopback を使い、同様に仮想デバイスを入力に選びます。\n- **Linux (PulseAudio / PipeWire)**: 出力デバイスの **Monitor**（例: `Monitor of ...`）を Input device に選ぶとデスクトップ音声を取り込めます。\n\n> OBS のブラウザソース自体は、ページ内で `getUserMedia` を使う本ツールでは「ページの音声を OBS で制御」設定とは無関係です。音声解析はあくまでページ側（ブラウザ）で行われます。OBS のブラウザソース内で `getUserMedia` を使う場合、OBS / OS 側でマイク（仮想デバイス）アクセスが許可されている必要があります。\n\n## ビルド\n\n```bash\npnpm build     # tsc -b && vite build（型チェック込み）\npnpm preview   # ビルド成果物をローカルで確認\n```\n\n`dist/` に静的ファイルが出力されるので、任意の静的ホスティングに配置できます。\n\n## 開発ツール\n\nSemantic Synth の VisualPatch（JSON）を送信前にローカルで検証する CLI を同梱しています。CLI から操縦・自動化する経路もあります: `scripts/vj-ctl.mjs` は 1 コマンドずつ叩く低レベル CLI、`scripts/vj-set.mjs` は本番用のセット（複数シーン）を Timeline に \"cue\" として仕込み、演奏中に手で進める高レベル CLI です。使い方の全体は `.claude/skills/vj-director/SKILL.md` を参照してください。\n\n### vj:validate\n\n```bash\npnpm vj:validate patch.json\n```\n\n`src/synth/validate.ts` / `src/synth/cost.ts` を Vite の SSR モジュールローダーで直接実行するので、検証ルールの複製はありません。\n\n### オフライン Patch プレビュー（`vj:preview`）\n\nLLM / 人間のディレクターが GLSL を読んで見た目を**推測**する代わりに、Patch をローカル GPU で描いて PNG にする CLI です。bridge や本番シーンには繋ぎません（`measure:coverage` と同じヘッドレス Chromium 経路）。\n\n```bash\n# カタログ全 Generator のコンタクトシート（いちばん使う）\npnpm vj:preview --contact-sheet artifacts/contact-sheet.png\n\n# seed から derive した Patch のフィルムストリップ（複数時刻）\npnpm vj:preview --seed take-1 artifacts/seed.png\n\n# 手書き / 生成した VisualPatch JSON\npnpm vj:preview path/to/patch.json artifacts/patch.png\n\n# 1 パラメータを min..max で横並びスイープ\npnpm vj:preview --sweep gamma.curve artifacts/sweep.png\n```\n\n- ヘッドレス Chromium が必要です。`nix develop` に入ると `CHROMIUM_BIN` が通ります（Playwright は単体では `CHROMIUM_BIN` を読まないので、ハーネス側が `launch()` に渡しています）。\n- source 以外のソロ描画は coverage 測定と同じく基準 source `grid` の上に載せます（`mod_coord` だけ source より前）。パラメータはカタログの default です。\n- 用途の中心は**コンタクトシートでカタログを目で見てから** Patch を組むこと。GLSL を先に読まない。\n\n### recipe（`vj-recipe.mjs`）— ムード + seed + tweaks を JSON で持ち回る\n\n`src/synth/validate.ts` は `VisualOperator` の `generatorVersion` が catalog の現行 version と厳密に一致することを要求し、catalog は各 generator の最新版しか保持しません。そのため生の Patch JSON をそのままリポジトリに置くと、generator の version が上がった瞬間に静かに壊れてしまいます。**recipe**（`recipes/*.json`）はこの問題を避けるため、Patch そのものではなく「ムード語 + seed（`vj-gen.mjs` へ渡す）+ 差分トークン列（`vj-tweak.mjs` 形式）」という作り直し可能な指示書だけを保存します。小さくて diff しやすく、catalog がどれだけ変わっても現行 catalog に対して作り直せます。\n\n- `recipes/*.json` … 個々の recipe（`{ name, mood, seed, tweaks, notes }`）\n- `recipes/setlists/*.json` … 複数の recipe を時間軸に並べたセットリスト（データのみ。実行 CLI は別途）\n\n```bash\n# 一覧表示（通信なし）\nnode scripts/vj-recipe.mjs list\n\n# 中身を見る（通信なし）\nnode scripts/vj-recipe.mjs show humid-qilou-night\n\n# mood+seed から作り直し、tweaks を重ねた draft を確認する（送信しない）\nnode scripts/vj-recipe.mjs apply humid-qilou-night --url wss://example.workers.dev/room/xxxx --dry-run\n\n# 検証を通ったら送る\nnode scripts/vj-recipe.mjs apply humid-qilou-night --url wss://example.workers.dev/room/xxxx\n```\n",
  "bytes": 19552,
  "sha": "68edcbb2fb1176003962c2d0e1213a3d0d49a792d115c290bad2a9e991129ae9",
  "repo_slug": "tainakanchu/auto-audio-visualizer-",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_tainakanchu_auto_audio_visualizer_knowle_cd722a5f/readme"
}