Apps SDK(プラグイン開発)

AIと読む
全ドキュメントを1ファイルで(llms-full.txt)

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名前欄が開きます。CreateApps フォルダの中に新規アプリを作成。Elsewhere… を選ぶと置き場所を自分で指定できます
Add既存のフォルダをアプリとして登録します(Apps フォルダの外でもOK。dev タグが付き、次回起動以降も残ります)
FolderApps フォルダを OS のファイラで開きます

名前欄の下には「どこに作られるか」が常に表示されます。 作成したフォルダ名は入力した名前から作られ(使えない文字は - に置換)、同名がある場合は日時が付きます。

各アプリの行は、左端の状態ドット(緑=実行中/黄=起動中・リロード中/赤=失敗/灰=停止)と名前と Open ボタン。 を押すと二次操作が開きます。

項目動き
Stop / Reload停止/再読み込み
Consoleそのアプリ専用のコンソールウィンドウを開きます
Folderそのアプリのフォルダを OS のファイラで開きます
Infoマニフェストの内容(id / name / version / apiVersion とホスト側の版 / capabilities)と、実行中なら所有リソースの内訳・診断情報
Revealそのアプリが内部に持っている隠しノードグラフに潜ります(戻るのはノードエディタの Back)
UnlinkAdd で登録したものだけに出ます。フォルダは消えません。一覧から外すだけです
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.mdAPIリファレンス全文
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.mdNODE_CATALOG.md を読んで、〜するアプリにして」と頼むのが標準的な進め方になります。

公式のサンプルアプリ(A/Bデッキミキサー、背景ルーパー、コントロールサーフェス、MIDIギャラリー等)は SynapseRack-Apps-SDKsamples/ にあります。 フォルダごとコピーすれば動く形なので、パターンを真似したいときはこちらを読ませるのが早いです。

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 } でフリーラン)、 shapesine / triangle / saw / squarefollowsourceaudio.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.txtAIエージェント用。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 } が入ります(codeunknown_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_appread_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から値を書かないでください。 requestAnimationFramesetValue を回すのは、この仕組みが避けようとしている往復コストそのものです。 連続変調はホスト側のバインディング(.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つ起動する、といった使い方向けです)。

関連

このページへのリンク

同じページを参照しているページ