Wiki home
The Design Canvas

Inside your plugin UI

Your plugin interface is a real Svelte app inside your project. The Design Canvas is a visual editor over that source code — and you can edit the same files by hand or let the AI do it.

For plugin makers building and customizing their plugin's interface.

Your UI is just Svelte

There is no proprietary UI format. Your plugin's interface is a small Svelte 5 + Vite app that lives inside your project's ui/ folder, and Patchwerk runs it as a live dev server while you work. The Design Canvas is a window onto that running app — when you drag a knob on the canvas, Patchwerk edits ui/src/Plugin.svelte for you.

That means there are three equally valid ways to change your UI, and they all converge on the same file:

You do thisWhat happens
Drag, resize, or restyle on the Design CanvasPatchwerk rewrites the matching element in ui/src/Plugin.svelte
Edit ui/src/Plugin.svelte in any code editorThe canvas hot-reloads and shows your change
Ask the AI for a layout or restyleIt writes the same file with the same conventions

What lives in ui/

ui/
  src/
    Plugin.svelte        ← your plugin's face — this is the file that matters
    App.svelte           ← shell that mounts Plugin.svelte and wires DSP bindings
    app.css              ← global styles
    components/base/     ← your editable copies of built-in components
    lib/patchwerk/       ← the API that connects UI to the audio engine
  vite-plugins/          ← Patchwerk-owned plumbing (don't edit)
  vite.config.ts
  package.json

Plugin.svelte and anything under components/base/ are yours — edit freely, commit them, refactor them. The vite-plugins/ folder and the bridge files under src/lib/ are infrastructure Patchwerk re-syncs every time you open the project, so hand edits there get overwritten.

How a canvas drag becomes a code edit

Every visual edit runs through two layers:

  1. Instant preview — the live app's DOM is patched immediately, so the move feels native.
  2. Authoritative source write — the same edit is applied to ui/src/Plugin.svelte as a structured code transform, not a blind text replace.

The source file is always the truth. The preview is a fast mirror you can rebuild any time by reloading. A useful rule: if a change would survive a full reload (position, props, text, structure), it was a source edit; if it wouldn't (selection, zoom, panel state), it was just editor state.

Tip Give every element an id. The canvas finds, selects, and edits elements by their id attribute — anonymous <div>s are invisible to it. Use kebab-case names that say what the thing does: cutoff-knob, filter-section, not div-3.

Binding controls to your DSP

Controls connect to the audio engine through exposed parameters. Expose a parameter on a graph node (via the node's param menu, or ask the AI), then bind it with the dsp prop:

<Knob  dsp={{ node: 'filter-1', param: 'cutoff' }} min={20} max={20000} label="Cutoff" unit="Hz" />
<Fader dsp={{ node: 'master',   param: 'gain' }}   min={-60} max={6} label="Master" unit="dB" />

The control picks up its live value, range, and label from the graph and writes changes straight to the engine. For read-only displays — meters, scopes, spectrum analyzers — bind a telemetry stream instead:

<VUMeter telemetry={{ node: 'vu-meter-1', kind: 'envelopeFollowerLevel' }} />
<Scope   telemetry={{ node: 'scope-1',    kind: 'scope' }} />

For custom visuals in <script>, the same data is available imperatively: patchwerk.param(nodeId, paramId) gives you a reactive value / set() pair, and patchwerk.telemetry(nodeId, kind) streams engine data at ~60 Hz.

Note Bindings reference graph node IDs. If you rebuild the graph, old IDs go stale and the knobs silently stop doing anything — re-point your dsp props at the new nodes.

The component library is an eject system

The Components panel offers built-in audio controls — Knob, Fader, XYPad, Keyboard, ADSREditor, FilterResponse, Scope, FFTAnalyzer, VUMeter, and more. Placing one doesn't lock you into a black box: Patchwerk copies its full source into ui/src/components/base/<Name>.svelte and imports that local copy from Plugin.svelte.

That copy is yours. Restyle the knob's arc, change the drag behavior, add a glow — it's plain Svelte in your project. When Patchwerk ships an updated version of a component, untouched copies update in place; if you've modified a file, the new upstream version lands next to it as a .new file so your edits are never clobbered.

Hand-editing and hot reload

Because the UI is a normal Vite app, normal workflows apply. Open ui/src/Plugin.svelte in your editor of choice, save, and the canvas hot-reloads in place — no rebuild, no restart, parameter values intact. Two conventions keep hand edits and canvas edits compatible:

  • Inline style for position and size (position:absolute; left:…; top:…; width:…; height:…) — the canvas inspector reads and writes these.
  • Classes (e.g. Tailwind) for appearance — colors, borders, typography, rounding.

Your UI must fit the plugin's fixed canvas size (set in project settings) — that's the exact window your plugin gets inside a DAW.

Edit mode vs Preview mode

The canvas toolbar switches between two modes:

  • Edit — clicks select elements, drags move them, the inspector edits props, and drawing tools (rectangle, frame, ellipse, text) insert new elements. Your controls don't react; you're manipulating the layout.
  • Preview — the editor gets out of the way and pointer input goes through to the real UI. Turn the knobs, play the keyboard, and hear the engine respond — exactly what users get in their DAW.

Flip to Preview early and often. It's the fastest way to catch a control that looks right but binds to nothing.