SuperAPI AI Directory

plugdata-patch

Coding & development

View repository

Updated 10-16 03:51

About this skill

plugdata Patch Generator

You are generating Pure Data (.pd) patch files — plain-text files that define audio processing graphs. These files open in both plugdata and vanilla Pure Data.

The .pd File Format

Every patch is a sequence of semicolon-terminated lines. The format is deliberately simple so it can be generated by hand or by code.

Minimal Patch

#N canvas 0 0 450 300 12;
#X obj 100 50 osc~ 440;
#X obj 100 120 *~ 0.5;
#X obj 100 200 dac~;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 1 0 2 1;

This creates a 440Hz sine oscillator at half volume, output to both audio channels.

Core Syntax

Line Type Format Purpose
Canvas #N canvas X Y W H FONTSIZE; Define patch window (first line)
Object #X obj X Y TYPE ARGS; Place an object at (X,Y)
Message #X msg X Y CONTENT; Place a message box
Comment #X text X Y CONTENT; Place a text comment
Connect #X connect SRC SRC_OUTLET DST DST_INLET; Wire two objects together
Array #X array NAME SIZE float SAVE; Declare a named array
Array data #A 0 V0 V1 V2 ...; Array sample values
Subpatch start #N canvas 0 0 W H NAME 0; Begin nested subpatch
Subpatch end #X restore X Y pd NAME; End subpatch, place on parent

Object Indexing

Every #X obj, #X msg, #X text, #X floatatom, #X symbolatom and #X restore line gets a sequential 0-based index on its canvas. Comments count. #X connect lines reference these indices. #X connect, #X coords, #X array, #X declare and #A lines do NOT get indices.

#X text 50 10 sine at half volume;  // index 0 — comments take an index too
#X obj 50 30 osc~ 440;      // index 1
#X obj 50 80 *~ 0.5;        // index 2
#X obj 50 130 dac~;         // index 3
#X connect 1 0 2 0;         // osc~ out0 -> *~ in0
#X connect 2 0 3 0;         // *~ out0 -> dac~ in0

If you skip comments when counting, every later connection shifts. In the console that shows up as (text->...) connection failed or audio signal outlet connected to nonsignal inlet (ignored).

Signal vs Control

Objects ending in ~ process audio-rate signals. Objects without ~ process control-rate messages (bang, float, symbol, list). You cannot directly connect signal outlets to control inlets or vice versa. Use sig~ to convert control to signal, or snapshot~ to convert signal to control.

Patch Generation Workflow

When generating a patch, follow this process:

1. Understand the Goal

Determine what kind of audio processing the user wants:

  • Synthesizer — oscillators + envelopes + filters + output
  • Effect processor — audio input (adc~) -> processing -> output (dac~)
  • MIDI processor — notein/ctlin -> logic -> noteout/ctlout
  • Utility/analysis — measurement, conversion, routing
  • DAW plugin — use param/playhead objects for host integration
  • Embedded (Daisy) — restrict to hvcc-compatible objects only

2. Design the Signal Flow

Sketch the signal path from source to destination:

  • What generates the sound? (oscillators, noise, samples, audio input)
  • What transforms it? (filters, effects, envelopes, math)
  • Where does it go? (dac~, send~, array, MIDI out)
  • What controls it? (MIDI, GUI, parameters, logic)

3. Choose Objects

Read references/objects.md for the full object catalog. Key categories:

  • Oscillators: osc~, phasor~, noise~, saw~, tri~, pulse~, bl.saw~ (bandlimited)
  • Filters: lop~, hip~, bp~, vcf~, biquad~, svfilter~
  • Effects: free.rev~, plate.rev~, chorus~, flanger~, phaser~, drive~, compress~
  • Envelopes: line~, vline~, adsr~, envgen~, decay~
  • Math: +~, *~, -~, /~, clip~, wrap~, expr~
  • MIDI: notein, noteout, ctlin, ctlout, mtof, ftom
  • I/O: adc~, dac~, notein, noteout
  • Timing: metro, delay, pipe, tempo
  • GUI: knob, nbx, tgl, bng, vsl, hsl, scope~

For compilation targets (Daisy, DPF plugin), read references/hvcc.md — only a subset of vanilla objects + heavylib are supported.

4. Generate the .pd File

Use this template pattern:

Build an array of objects, each with {type, x, y, args}
Build an array of connections, each with {src, src_outlet, dst, dst_inlet}
Emit #N canvas header
Emit #X obj / msg / text lines in order (each one, comments included, takes the next index)
Emit #X connect lines using tracked indices

Positioning Guidelines

  • Vertical flow: Y increments of 50-70 pixels per object
  • Parallel paths: X offsets of 100-150 pixels
  • Signal path goes top-to-bottom (source -> processing -> output)
  • Control/modulation paths branch horizontally
  • Start X at ~50-100, start Y at ~30-50

Common Patch Patterns

Monophonic Synthesizer

#N canvas 0 0 600 500 12;
#X obj 100 30 notein;
#X obj 100 80 mtof;
#X obj 100 130 osc~;
#X obj 250 30 notein;
#X obj 250 80 stripnote;
#X obj 250 130 adsr~ 200 50 0.7 300;
#X obj 100 200 *~;
#X obj 100 270 lop~ 2000;
#X obj 100 350 *~ 0.5;
#X obj 100 420 dac~;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 6 0;
#X connect 3 0 4 0;
#X connect 4 0 5 0;
#X connect 5 0 6 1;
#X connect 6 0 7 0;
#X connect 7 0 8 0;
#X connect 8 0 9 0;
#X connect 8 0 9 1;

Audio Effect (DAW plugin)

#N canvas 0 0 600 400 12;
#X obj 100 50 adc~;
#X obj 100 120 lop~ 1000;
#X obj 100 190 *~ 0.8;
#X obj 100 260 dac~;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 2 0 3 1;

Parameter with DAW Automation

#X obj 200 50 param cutoff 1000;
#X obj 200 100 sig~;
#X obj 100 150 lop~;

The param object exposes a "cutoff" parameter to the DAW's automation system.

Subpatch (Abstraction)

#N canvas 0 0 300 200 mySubpatch 0;
#X obj 50 30 inlet~;
#X obj 50 100 *~ 0.5;
#X obj 50 170 outlet~;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X restore 100 200 pd mySubpatch;

Exposing Parameters for hvcc Compilation

#X obj 200 50 r gain @hv_param 0 1 0.5;

Format: [r NAME @hv_param MIN MAX DEFAULT [TYPE]] Types: float, bool, int, trig, dB, Hz, log, log_hz

Key Constraints

  1. Every line ends with ; — missing semicolons break the file

  2. Object indices are 0-based and sequential per canvas — #X obj, #X msg, #X text (comments!), #X floatatom, #X symbolatom and #X restore all count

  3. Signal/control boundary — ~ objects only connect to ~ objects unless bridged

  4. No spaces in file paths referenced by objects (soundfiler, readsf~, etc.)

  5. [dac~] takes channel args: [dac~ 1] = left, [dac~ 1 2] = stereo, [dac~ 1 2 3 4] = quad

  6. [adc~] similarly takes channel args for audio input

  7. hvcc compilation only supports vanilla + heavylib objects (~323 total) — no ELSE, Gem, Cyclone, or pdlua

  8. Send/receive (s/r, send/receive) provides wireless connections — useful for clean layouts and parameter distribution

  9. Gem in plugdata is built in, but its classes are registered only as Gem/<name>. Use [Gem/gemwin], [Gem/gemhead], [Gem/pix_film] and so on. The other option is a canvas-level #X declare -lib Gem; line, which makes the plain names global for the whole session and replaces ELSE objects with the same name (e.g. [circle]). A #X obj ... declare -lib Gem; box on its own loads nothing (see references/pd-format.md → Declaration)

  10. Gem video in plugdata: plugdata runs Gem on the audio thread. [pix_film]'s default AVF backend rebuilds its AVAssetReader on every frame, which measured about 64 ms per frame for 1080p H.264, so 30 fps playback pins the CPU at 100%. Play a light copy through the ffmpeg backend instead (about 1 ms per frame):

    ffmpeg -i in.mov -map 0:v:0 -an -vf "scale=960:540,setpts=PTS-STARTPTS" -fps_mode cfr -r 30 \
      -c:v libx264 -crf 21 -g 1 -bf 0 -pix_fmt yuv420p -video_track_timescale 15360 out_540p.mov
    

    Then send [backend ffmpeg AVF, open out_540p.mov RGBA( to [Gem/pix_film]. The flags matter:

    • -g 1 -bf 0 makes every frame a keyframe, so per-frame seeks are exact.
    • Gem's ffmpeg backend seeks to frame * timescale / fps, so keep the timescale a multiple of the frame rate.
    • -an and setpts stop an audio track or a video start offset from skewing Gem's frame numbering. Gem counts frames over the container duration, which the audio track can lengthen.

    Check that the frame count in the console's loaded file line matches the video's.

Reference Files

Read these when you need deeper detail:

  • references/pd-format.md — Complete .pd syntax specification with all element types, subpatches, arrays, and GUI objects
  • references/objects.md — Full object catalog organized by category (1,653 objects across 8 libraries)
  • references/else.md — ELSE library reference (595 objects): oscillators, filters, effects, modules, GUI, MIDI, tuning, sequencing, and a Cyclone migration table
  • references/cyclone.md — Cyclone library reference (206 objects): Max/MSP compatibility layer, non-alphanumeric operators, zl modes, and vanilla Pd differences
  • references/hvcc.md — Heavy compiler reference: supported objects, compilation targets, parameter annotations, and constraints
  • references/pd-lua.md — pd-lua API for writing custom Pd objects in Lua (inlets, outlets, DSP, GUI, clocks, arrays)

Library Selection Guide

For new patches, prefer this order: ELSE > vanilla > Cyclone.

  • ELSE — Active development, more features, better plugdata integration. Use for oscillators (saw~, bl.saw~), effects (free.rev~, chorus~), envelopes (adsr~), MIDI (note.in), GUI (knob, scope~)
  • Cyclone — Use only when porting Max/MSP patches or when you need a specific Max object not available elsewhere
  • Vanilla — Use for core objects (osc~, *~, dac~, metro, send/receive) and when targeting hvcc compilation

When to Use pd-lua

If the user needs custom logic that doesn't exist as a built-in object, pd-lua lets you write objects in Lua:

local myobj = pd.Class:new():register("myobj")
function myobj:initialize(sel, atoms)
    self.inlets = 1
    self.outlets = 1
    return true
end
function myobj:in_1_float(x)
    self:outlet(1, "float", {x * 2})
end

Save as myobj.pd_lua in the patch directory, then use [myobj] in the patch. Read references/pd-lua.md for the full API.

Checklist Before Writing a Patch

Before writing the .pd file, verify:

  1. Does the signal path flow from source to output?
  2. Are signal (~) and control domains properly separated?
  3. Are object indices correct for all connections, counting comments and subpatch #X restore lines?
  4. Is every line terminated with ;?
  5. Does the first line declare a canvas?
  6. If targeting compilation, are all objects hvcc-compatible?
  7. If it's a DAW plugin, does it use adc~/dac~ (for effects) or MIDI objects (for instruments)?
  8. Are positions reasonable for visual readability?