Apps SDK(プラグイン開発)
SynapseRack の中で動く自分専用のツールを、HTML と JavaScript で作れる仕組みです。 アプリは SynapseRack のウィンドウとして開き、そこからノードの生成・接続・パラメータ操作や、 Layer の操作、映像の出力(MediaOut / Spout / Syphon)などを行えます。
ビルド手順はありません。フォルダに index.html を置いて、保存したら勝手に読み直されます。
そしてアプリの雛形には、AIエージェント向けのAPIリファレンスがそのまま同梱されます。
「フォルダごとAIに渡して相談する」ことを前提にした作りになっています。
アプリとは何か
1つのアプリ=1つのフォルダです。中に次の2つがあれば、それはアプリです。
| ファイル | 役割 |
|---|---|
synapse-app.json | マニフェスト。id / name / version / entry / apiVersion / capabilities / multiInstance |
index.html(= entry) | 入口。ここから読み込んだJSが window.synapse 越しに SynapseRack を操作します |
entry を書き換えれば入口のファイル名は変えられます(既定は index.html)。
アプリの置き場所
インストール済みアプリの置き場所は、Source Directory と同じ根っこの下にある Apps フォルダです。
<Source Directoryの親パス>/SainaWorks/SynapseRack/Apps/
既定では Windows が C:/SainaWorks/SynapseRack/Apps、macOS が ~/SainaWorks/SynapseRack/Apps になります
(親パスの変え方は VJ素材を読み込むパス(Source Directory)を指定する と同じ Settings.json です)。
このフォルダの「直下」だけが見られます。
Appsの直下のフォルダのうち、synapse-app.jsonを持つものがライブラリに載ります。 起動時とAppHubを開いたときに走査され、さらにフォルダの追加・削除・リネームは監視されているので、 アプリフォルダを放り込めばその場で一覧に出ます(Ableton の .ablx を放り込む感覚です)。
配布されたアプリを入れるときも、このフォルダにフォルダごとコピーするだけです。
始め方
Apps メニュー
メニューバーの Apps に、次の内容が並びます。
| 項目 | 動き |
|---|---|
| App Hub… | AppHub ウィンドウを開きます |
| (アプリ名の一覧) | クリックで起動。すでに動いているアプリは頭に ● が付き、クリックすると窓が前に出ます |
| Open Apps Folder | 上記の Apps フォルダを OS のファイラで開きます |
AppHub
アプリの管理はすべてこのウィンドウで行います。上部のツールバーは3つです。
| ボタン | 動き |
|---|---|
| New | 名前欄が開きます。Create で Apps フォルダの中に新規アプリを作成。Elsewhere… を選ぶと置き場所を自分で指定できます |
| Add | 既存のフォルダをアプリとして登録します(Apps フォルダの外でもOK。dev タグが付き、次回起動以降も残ります) |
| Folder | Apps フォルダを OS のファイラで開きます |
名前欄の下には「どこに作られるか」が常に表示されます。
作成したフォルダ名は入力した名前から作られ(使えない文字は - に置換)、同名がある場合は日時が付きます。
各アプリの行は、左端の状態ドット(緑=実行中/黄=起動中・リロード中/赤=失敗/灰=停止)と名前と Open ボタン。
⋯ を押すと二次操作が開きます。
| 項目 | 動き |
|---|---|
| Stop / Reload | 停止/再読み込み |
| Console | そのアプリ専用のコンソールウィンドウを開きます |
| Folder | そのアプリのフォルダを OS のファイラで開きます |
| Info | マニフェストの内容(id / name / version / apiVersion とホスト側の版 / capabilities)と、実行中なら所有リソースの内訳・診断情報 |
| Reveal | そのアプリが内部に持っている隠しノードグラフに潜ります(戻るのはノードエディタの Back) |
| Unlink | Add で登録したものだけに出ます。フォルダは消えません。一覧から外すだけです |
| Hot Reload | ファイル変更で自動リロードするか(既定ON。インスタンスごとの設定で、Stop すると既定に戻ります) |
新規アプリの中身と、AIに相談する進め方
New で作ったフォルダには、synapse-app.json に加えて次の10ファイルがコピーされます。
| ファイル | 中身 |
|---|---|
index.html / main.js / style.css | 動く雛形。Webウィンドウに色を描いて MediaOut に出し、Hueコントロールを1本生やすところまで実装済み |
synapse-sdk.js | 便利レイヤー(window.SynapseSDK)。使わなくてもアプリは書けます |
synapse.d.ts | 全APIの型定義 |
SYNAPSE_API.md | APIリファレンス全文 |
NODE_CATALOG.md | 全ノードの id・ポート・設定可能メンバの一覧 |
SHADER_EFFECTS.md | シェーダーエフェクト(Preview機能)の解説 |
SAMPLES.md | 公式サンプルアプリ集への案内 |
DESIGN.md | 「SynapseRackの中に居るように見える」UIの作法 |
この同梱物一式が、そのままAIエージェントへの入力です。
SYNAPSE_API.mdは冒頭に「このファイルだけを読んだAIアシスタントが、一発で動くindex.htmlを書けるように書いてある」と明記された、 AI向けの文書です。AppHub の Folder でフォルダを開き、そのフォルダを Claude Code / ChatGPT などのエージェントに渡して 「このフォルダのSYNAPSE_API.mdとNODE_CATALOG.mdを読んで、〜するアプリにして」と頼むのが標準的な進め方になります。
公式のサンプルアプリ(A/Bデッキミキサー、背景ルーパー、コントロールサーフェス、MIDIギャラリー等)は
SynapseRack-Apps-SDK の samples/ にあります。
フォルダごとコピーすれば動く形なので、パターンを真似したいときはこちらを読ませるのが早いです。
APIの概観
すべての呼び出しは window.synapse.<グループ>.<メソッド>() で、Promise を返します。
synapse-sdk.js を読み込むと、同じことをハンドルオブジェクト経由で短く書けます。
1. 接続して「準備完了」を伝える
const app = await SynapseSDK.connect(); // window.synapse を待って ping する
// ...ここでアプリが使うものを全部作る...
await app.ready(); // 作り終えたら1回だけ呼ぶ
app.ready() は必須です。リロード時、前回作ったリソースは「まだ請求されていない」状態で生かされ、
新しい boot() が同じ id で作り直すか、ready() が来るか、タイムアウトするまで待ってから片付けられます。
これが「編集して保存 → 作り直されるが、ユーザーが引いた配線は切れない」を成立させています。
2. ノードを作る・値を書く・つなぐ
NODE_CATALOG.md の表に載っている id をそのまま type に渡します。
// 作る(返り値の id がそのノードのmoduleId)
const lfo = await synapse.modules.create({ type: 'LFO' });
const blend = await synapse.modules.create({ type: 'BlendRT' });
// 設定可能メンバに値を書く(パスはカタログの settable 列の名前。ドット区切り不可)
await synapse.modules.set(blend.id, 'mixInput', 0.5);
// つなぐ(位置引数。ポート名はカタログの in/out の id。省略時は 'Out' / 'In')
await synapse.modules.connect(lfo.id, 'Value', blend.id, 'Mix');
同じカタログは実行時にも引けます: const types = await synapse.modules.types();
modules.createは「同じ id で呼び直すと同じもの」にはなりません。 2回呼べばノードが2つできます。冪等にしたいなら返ってきたmoduleIdを自分で覚えておくか、 後述のrender/web/output/layers側(idを渡すと get-or-create)を使ってください。
3. Layer を触る/Layer の絵をテクスチャとして取り出す
const selected = await synapse.layers.selected();
await synapse.layers.setOpacity(selected.id, 0.5);
// そのLayerの出力を textureId として取り出す(id を渡すのでリロードしても同じもの)
const deckA = await synapse.layers.createTextureSource(selected.id, { id: 'deck-a' });
textureId は映像の共通通貨です。Layerの出力も、Webウィンドウの描画結果も、ミキサーの結果も同じ文字列で表され、
ミキサーの入力にも output.publish にも、アプリ内プレビューにもそのまま渡せます。
4. 自分の絵を SynapseRack の映像ソースとして出す
const canvas = await app.webWindow('canvas', {
title: 'Canvas',
html: '<canvas id="c"></canvas><script>/* requestAnimationFrame で描く */</script>',
width: 960, height: 540
});
await app.publishMedia('main-out', { source: canvas, name: 'My Output' });
publishMedia(= output.publish)は MediaOut ノードを立てます。
安定した id を渡しておけば、アプリをリロードしても、プロジェクトを開き直しても同じノードが再利用され、
ユーザーがそこから引いた配線も繋ぎ直されます。
5. 連続的な変化はホスト側に任せる
const mixer = await app.mixer('mix', { type: 'crossfade', inputs: [deckA, canvas], value: 0.5 });
const fader = await app.control('fade', { label: 'Fade', min: 0, max: 1, value: 0.5, midi: true });
fader.onChange((v) => mixer.value.set(v));
await mixer.value.lfo({ rate: '1', shape: 'sine', min: 0, max: 1 }); // 1小節で1周
await mixer.value.follow({ source: 'audio.bass', min: 0, max: 1, smooth: 0.5 });
rate は小節単位の音楽的分割('1/4' '1/2' '1' '2' '4'。{ hz: n } でフリーラン)、
shape は sine / triangle / saw / square、follow の source は
audio.level / audio.bass / audio.mid / audio.high です。
controls.register(SDKでは app.control)に midi: true を付け、対応するDOM要素を
data-synapse-control="<id>" か anchor で結び付けると、SynapseRack のMIDIラーンモードで、
あなたのページ上のUIがそのまま学習対象になります。専用の「MIDI待受」ボタンを自作する必要はありません。
ノードの id とポート名を調べる
modules.create / modules.connect は正確な id とポートidを要求します。調べ方は4つあります。
| 手段 | 使いどころ |
|---|---|
アプリフォルダの NODE_CATALOG.md | オフラインで完結。AIに読ませる前提の一覧(id / タイトル / 入力 / 出力 / settable) |
await synapse.modules.types() | 実行時に同じ内容を取得。手元のバージョンの実物と必ず一致します |
ノードリファレンス (/ja/docs/nodes/) | 人間が読む用。ポート表と解説 |
https://synapserack.com/mcp / https://synapserack.com/llms-full.txt | AIエージェント用。MCPサーバーとして繋ぐか、全文をまとめて読ませる |
MCPサーバーには search_docs / get_doc / list_nodes / get_node があり、
ドキュメントとノードリファレンスをそのまま引けます。
実行・リロード・デバッグ
ホットリロード
.html / .js / .css / .json を保存するだけで、実行中のアプリが約1秒で自動リロードされます(node_modules と .git は対象外)。VS CodeでもAIコーディングツールでも、保存の仕方を気にする必要はありません。
止めたいときは AppHub の Hot Reload をOFFにします(そのインスタンスの間だけ有効)。
コンソール
アプリの console.log / エラー、ブリッジのログ、ホットリロードの通知は、すべて1つのコンソールに集まります。見る場所は2か所です。
- アプリのウィンドウ下部の「Console」ボタン — その場で開閉できます
- AppHub の
⋯→ Console — 独立したウィンドウ(アプリを停止しても「App stopped.」表示になって残ります)
どちらにも Copy があり、表示中のログをそのままクリップボードに入れます(AIに貼って渡すための導線です)。 Clear は表示を消すだけで、内部のバッファは残ります。 アプリウィンドウ側にはさらに Debug トグルがあり、ONにするとブリッジの通信そのものもログに流れます。
エラーは構造化されています。失敗した呼び出しは Error を投げ、err.synapse に
{ code, message, method, hint } が入ります(code は unknown_method / invalid_params / not_found / internal)。
原因を聞くときは、この中身ごと渡すのが最短です。
MCPサーバー(開発ツール)
AppHub の一番下の Developer を開くと、MCP Server のトグルとポート欄があります。
ONにすると http://127.0.0.1:8765/(既定ポート)で MCP サーバーが立ち、
AIコーディングアシスタントが動いている SynapseRack に対して直接次の操作をできるようになります。
| ツール | 内容 |
|---|---|
list_apps | 実行中のアプリインスタンス一覧 |
invoke | 実行中のアプリに対して、ドキュメントにある任意のメソッドを呼ぶ |
read_console | そのアプリのコンソールを読む(severityで絞れます) |
reload_app | リロード |
graph_state / graph_node_types / graph_create_node / graph_delete_node / graph_connect / graph_disconnect | ユーザーの見えているノードグラフを読む・編集する |
「編集 → reload_app → read_console でエラー確認 → invoke で結果を検証」というループが回せるので、
AIに書かせたコードを目視で確かめ続ける必要がなくなります。
セキュリティ上、既定はOFFです。毎回の起動でOFFに戻ります。 ループバック(127.0.0.1)にのみバインドされますが、認証はありません。 ポート番号だけは次回のために保存されますが、有効/無効は保存しません(開けるのは毎回の明示的な選択であるべき、という設計です)。 使い終わったらOFFにし、ポートフォワード等で外に出さないでください。
保存のされ方
アプリはプロジェクト(.synapse)と一緒に保存されます。プロジェクトを開き直すと、
SynapseRack がフォルダからアプリを自動的に起動し直します(Max for Live のデバイスがセットと一緒に保存されるのと同じ感覚です)。
保存に絡む仕組みは3つです。
| 仕組み | 何が残るか |
|---|---|
app.setState(obj) / app.onRestore(fn) | アプリのUI状態。プロジェクトと一緒に保存され、app.ready() の直後に返ってきます |
output.publish などの安定した id | 出力ノードの同一性。ユーザーがそこから引いた配線ごと復元されます |
synapse.storage(get/set/delete/list) | プロジェクトに依存しないアプリ単位の保存領域。プリセットやキャッシュ、最後に使った設定向け |
制約・注意点(実装から)
毎フレームJSから値を書かないでください。
requestAnimationFrameでsetValueを回すのは、この仕組みが避けようとしている往復コストそのものです。 連続変調はホスト側のバインディング(.value.lfo()/.value.follow()/.value.bindMidi())に任せ、 JSからはスライダーやボタンの操作といった離散的なイベントだけを送ってください。
App の低レイヤーノードは、ノード追加UIに既定で出ません。*
AppMediaPlayer/AppTextureSource/AppOffscreenLayer/AppStackMixer/AppTextRender/AppFxChainの6種は、 右クリックのノード追加メニューとサイドパネルから隠されています(非開発者にとっては一覧が煩雑になるだけのため)。 出したい場合は グローバル設定 → Library → App SDK Modules → Show App SDK modules をONにしてください。 この設定はプロジェクトには保存されません。アプリ側のmodules.create/render.*/modules.types()は、この設定に一切影響されません (設定がOFFでもアプリからは作れます)。
ウィンドウ同士の直接のやりとりはできません。 メインウィンドウと
web.createWindowで開いた子ウィンドウは、それぞれ独立したブリッジを持ちます。 取り込んだアセットのidや登録したコントロールはウィンドウをまたいで参照できません。 共有したい値はsynapse.storageを経由してください(textureIdとキー付きリソースはインスタンス全体で共通なので、そのまま渡せます)。
controls.registerだけは厳密な get-or-create ではありません。 同じidで登録し直すと、前のコントロールのメタデータを上書きします(boot()の再実行では問題になりません)。
ミキサーは
crossfade(入力2つ)のみです。render.createSessionは互換のための空実装なので、新しいコードでは呼ばないでください。
オフスクリーンのプレイヤーは既定でミュートです。 音を出すのは明示的なオプトインで、そもそも音声を扱えるのは Unity 標準の動画再生バックエンドだけです。
SDKは必須ではありません。
synapse-sdk.jsは定型文を減らすだけの薄い層で、 できることはすべてwindow.synapseから直接呼べます。
複数起動(multiInstance)
既定ではアプリは1つだけ動きます(2回起動しても窓が前に出るだけ)。
synapse-app.json に "multiInstance": true を足すと、同じアプリを何個も並べて動かせるようになり、
AppHub の行が「New Slot」+スロットごとの行に変わります。
スロットごとに独立するのは、内部グラフ・公開した出力(表示名に (2) が付きます)・MIDIのスコープ・
ウィンドウ・app.setState / app.onRestore です。synapse.storage だけは全スロットで共有されます。
自分が何番目かは app.ping() の slot で分かります(Layerごとに1つ起動する、といった使い方向けです)。
関連
- ノードリファレンス —
modules.createに渡す id とポート名の一覧 - MIDIマッピング — アプリのコントロールもこのラーンモードの対象になります
- VJ素材を読み込むパス(Source Directory)を指定する —
Appsフォルダの場所を決めている設定












