Zynthian

Zynthian: plugin-hostd as its plugin host.

How to install it, how to use it, what it changes in Zynthian and where it stands. The method and every table are in the technical notes linked at the end.

01

What it is.

Zynthian is an open synthesizer and effects platform on a Raspberry Pi. It runs LV2 plugins through its own engines: mod-host, behind MOD-UI, which runs its effect chains, and jalv, which runs one plugin per process. plugin-hostd speaks mod-host's protocol and runs each plugin, LV2 or CLAP, in a worker process, so a plugin that crashes takes down only its own worker, which is put back with its settings. A whole chain can run in one worker instead of one process per plugin, and plugin-qualify measures each plugin on the device first, so the menus are built from what it found.

With it Zynthian gets a plugin-hostd engine beside jalv for LV2 and CLAP plugins in chains, plugin-hostd as a switchable replacement for mod-host under MOD-UI, plugin menus ordered and tagged from per-plugin qualification records, and chains whose plugins the daemon groups into as few workers as their pins allow.

02

How much it changes in Zynthian.

Nothing in Zynthian depends on it. It stays on jackd: no file that configures JACK changes, and no PipeWire is involved. As offered to Zynthian, the plugin-hostd engine stays off until a user turns it on, and MOD-UI keeps mod-host until a user switches its backend; going back is the same setting.

FreeMixer's forks of zynthian-ui and zynthian-sys have it on: plugin-hostd is the default plugin engine, in place of jalv, and MOD-UI's default backend, in place of mod-host, and the qualification sync is enabled. A plugin plugin-hostd refuses stays on jalv, listed in its menu, so nothing the user has disappears.

What runs when nobody uses it: the interface starts a daemon only when a chain holds a plugin-hostd plugin. What stays enabled is the qualification sync, two minutes after boot at the lowest CPU and disk priority, skipping every plugin whose files have not changed, and omx-panel's MOD GUI and complement syncs. All of them can be disabled; the first sync on a device with many plugins is long.

03

Install it.

The image

ZynthianOS Vangelis for the Raspberry Pi 4 and 5, with the forks of zynthian-ui and zynthian-sys, the OpenMixer apt repository, and plugin-hostd, omx-clap-host, omx-plugins, omx-panel and plugin-qualify installed and made the default. It is built from upstream's released Vangelis image with the fork's own update steps, as the fork's docs/openmixer-image.md describes, and will be a release of FreeMixer's zynthian-sys fork, in parts with their sums. It is not published yet: until it is, install the packages on a Vangelis you already run, as below.

The first boot does what any Zynthian card does once: it detects the kit, regenerates the SSH keys and the web certificate, creates the Wi-Fi access point, grows the file system to the card, and reboots once. Two minutes after boot the qualification sync starts in the background, at the lowest priority, and does not hold the boot; with the qualification records package installed it only checks that the plugin files are the ones the records describe.

On an existing Vangelis

A Vangelis system already running needs no new image. As root, add the OpenMixer repository and install the packages:

curl -fsSLO https://www.openmixer.org/deb/openmixer-release.deb
sudo apt install ./openmixer-release.deb
sudo apt install plugin-hostd omx-clap-host omx-clap-scan omx-panel

openmixer-release carries the signing key and the apt source of the running Debian release. This enables no service: mod-host, MOD-UI, jalv and the menus stay as they were until you switch them.

Turn it on, and back off

Three settings, each undone by setting it back:

# the plugin engine: in /zynthian/config/zynthian_envars.sh, then restart the UI
ZYNTHIAN_PLUGIN_ENGINE=plugin-hostd
# MOD-UI's backend
set_modhost_backend.sh plugin-hostd
# plugins in series sharing one worker: in /zynthian/config/plugin-hostd.conf
grouping on

04

Use it.

With the plugin-hostd engine on, a plugin's menu entry ends in "(PH)". Select a chain, open its options and use Add Audio-FX processor; the plugin's controller pages follow its own sections.

A group: plugins in series in one worker

With grouping on, a run of plugins in series in a chain shares one worker process and one JACK client instead of one process each. A group is what the chain has in series with nothing between: a parallel slot, the mixer strip or a jalv plugin ends it. A processor that runs alone has a Grouping entry in its options saying why.

Grouping is off unless you set it. It saves most of the memory of one process per plugin: 24 plugins in one chain take about 64 MB, against about 110 MB under mod-host.

A channel strip as plugins

EQ and dynamics per chain are ordinary effects in that chain; Zynthian's mixer keeps its level, pan and sends. Either add separate plugins (omx keyed-gate, omx eq8, omx comp: you choose each one and its order, and eq8 has eight bands), or one plugin, omx strip: input trim, filters, a gate, four EQ bands and a compressor in stereo, in one load and one worker, its five stages in any order.

At 48 kHz and 512 frames, omx strip costs about 5.9 µs per block; omx eq8 and omx comp together about 11.4 µs, with eight EQ bands against four.

Shared effects on a Mixbus

One effect on a bus instead of one per chain, fed from each chain with Zynthian's own sends:

  1. Add a chain and choose Mixbus. In its options, Add Audio-FX processor: omx reverb, then omx delay, each "(PH)".
  2. In each chain that should feed it, open the mixer processor's controller page and raise the send level for that Mixbus, pre or post fader.

A plugin-hostd plugin behaves in a Mixbus as in any chain. A slot holds one processor or several, and several are parallel; a worker runs its plugins in series in one JACK client, so with grouping on:

Slots of the chainWhat runs
A, BA and B in one worker, one JACK client
A, [B, C]a worker each: B and C are parallel, three clients
A, B, [C, D], E, FA with B, a worker each for C and D, E with F: four clients
A, mixer strip, Ba worker each: the strip is in the signal's way
A, a jalv plugin, Ba worker each, and the jalv plugin's own client

Send levels, pre and post fader, and pinning or moving a Mixbus are Zynthian's mixer and chain manager: none of them sends anything to the daemon. Measured at JACK ports, series, parallel and Mixbus chains give the levels they should: unity in series, +6.02 dB for two in parallel, the strip's -20 dB where it sits.

The Zynthian plugin menu sorted by tier, with latency and real-time safety tags
The plugin menu, sorted by qualification tier
The controller page of an instrument in Zynthian
A controller page, from the plugin's own sections
An instrument's MOD GUI on a MOD-UI pedalboard
The same instrument's MOD GUI, from the same layout
A Zynthian chain with an instrument and a compressor
A chain with an instrument and an effect

05

Instruments: voices across the cores.

Why

A Pi 5 has four cores. A synth plays many voices at once, and every block of audio has a deadline: all the voices must be computed before it is due. Run one after another, they all use one core. That core sets how many notes you can play, however idle the other three are.

Some synths split their voices into tasks and ask the host to run them (CLAP's thread-pool extension). Tasks like that can run side by side, if the host lets them.

How it works

omx-clap-host starts helper threads: one for each core the plugin's worker may use, minus one, because the audio thread takes the last core. Each helper is pinned to its core, runs at the audio thread's real-time priority and sleeps between blocks.

When the plugin asks for its tasks, the helpers and the audio thread take them one by one, and the call returns when the last is done. If the priority cannot be set, there are no helpers and the tasks run as before, in turn.

Who gets helpers

Not every plugin. Helper threads are given plugin by plugin, never everywhere. A plugin gets them only if its qualification found it real-time safe, with the helpers watched too, and faster with them. A task that allocates or waits on a lock would make every core miss the deadline.

Any other plugin runs as before: its tasks one after another on the audio thread. A plugin that gets helpers runs in a worker of its own, which plugin-hostd starts with the pool on.

How to switch it

It is an option of omx-clap-host:

# omx-clap-host: helper threads for the plugins that split their voices into tasks
omx-clap-host --thread-pool=qualified   # the default
omx-clap-host --thread-pool=on
omx-clap-host --thread-pool=off
  • qualified (the default): only plugins whose qualification grants it get helpers. plugin-hostd gives each worker the right setting, so on Zynthian you do not set it.
  • on: every plugin that uses the extension gets helpers.
  • off: every plugin runs its tasks in turn.

What it gives

Measured on a Raspberry Pi 5 at 48 kHz and 256 frames, so the block is due in 5333 µs. The plugin is a test plugin that splits each block into 16 tasks, because no plugin on the Pi asks for tasks yet, Surge XT included. The time is the mean cost of one block, with the helpers off and on:

Voices in each taskOff (µs)On (µs)Faster by
8527.9155.93.4x
241583.4418.73.8x
483335.5857.93.9x

At 512 frames, the 48-voice load takes 6.6 ms of the 10.7 ms block with the helpers off and 1.7 ms with them on. No block went over its deadline in any run.

This is the best case: equal tasks that share nothing. A real instrument has not been measured with it, so there is no Surge XT figure. Surge XT does not split its voices, and for it the cores come from running each instrument in a worker of its own: four instances with 16 notes each ran at 14 to 15 % of a core each.

Surge XT measured, in full

06

The headline figures.

Measured on a Raspberry Pi 5 running Zynthian, with an RME Babyface Pro as its audio interface and JACK at real-time priority 70 with 3 periods, Zynthian's own setting. Read digitally at the JACK server and its ports. plugin-hostd 0.18.0 and omx-clap-host 0.29.0 against mod-host 0.10.9, the host behind Zynthian's effect chains and MOD-UI, and jalv 1.6.9, which runs one plugin per process:

  • DSP load at 48 kHz, 16 plugins (omx comp, delay and drive): a plugin-hostd chain 6.75 % at 128 frames, against 9.36 % for omx-clap-host, 8.86 % for mod-host and 11.40 % for jalv; at 256 frames 6.38 % against 7.75, 7.38 and 8.49 %. 0 xruns for the chain in every repeat.
  • At 96 kHz the load is about twice the 48 kHz figure on every host and the order holds: the same 16 plugins at 256 frames, 12.64 % in a chain against 15.43 % for omx-clap-host, 14.60 % for mod-host (8 xruns) and 18.16 % for jalv (52 xruns). Xruns come back at 96 kHz where 48 kHz had none, fewest in the chain.
  • Memory: 55 MB for those 16 plugins in a chain, against 53 MB for omx-clap-host, 99 MB for mod-host and 690 MB for jalv's 16 processes.
  • Time to add 16 plugins one by one, the host running: 80 ms into a plugin-hostd chain, against 364 ms for mod-host, 397 ms for omx-clap-host and 13.8 s for jalv.
  • Instruments: Pianoteq 9 costs 13.4 to 14.7 % on every host at 48 kHz and 256 frames, since its own threads do the work; it takes 250 MB under plugin-hostd, 288 MB under mod-host and 279 MB under jalv.
  • The floor: 64 frames at 48 kHz has xruns on every host even with no plugin loaded, so it is the device's limit, not a host's. At 96 kHz, 128 frames runs with no xrun on omx-clap-host, plugin-hostd and mod-host with no plugin (jalv has no host without a plugin), and the third period is worth keeping: with 2, the heavier sets read higher on omx-clap-host and mod-host.
  • Qualification, from the earlier run named in the footer (plugin-qualify 0.18.5): of 1602 plugins tried on the device, 1079 run in a shared worker and 306 in a worker of their own. A new run with plugin-qualify 0.19.0 will replace these figures.

Every table and the methodThe benchmarks, row by row

Raspberry Pi 5 running Zynthian, RME Babyface Pro · JACK priority 70, 3 periods · 48 and 96 kHz, 128 and 256 frames · read digitally at the JACK server and ports · plugin-hostd 0.18.0 (protocol 0.3.0), omx-clap-host 0.29.0, omx-plugins 0.18.0, mod-host 0.10.9, jalv 1.6.9 · 5 November 2026 · qualification: plugin-qualify 0.18.5, records omx-qualification-rpi5-zynthian-vangelis 2026110101, 1 November 2026

07

Where it stands.

Proven on the device

  • The plugin-hostd engine runs Zynthian chains, LV2 and CLAP side by side, with the plugin menus built from the qualification records.
  • Controller pages follow the plugin's own sections and are named by what they hold.
  • Instruments and effects play through the plugin-hostd engine, read digitally at JACK ports with 0 xruns.
  • Presets, through plugin-hostd 0.18.0 and omx-clap-host 0.29.0: omx eq8 (CLAP) and mda Piano (LV2) list their presets, load them with their values and save one that loads back; Odin 2 (with pins off) and Surge XT's LV2 build list, load and save; Surge XT's CLAP build lists and loads its factory presets.
  • Grouping through the plugin-hostd engine in zynthian-ui passes its proof: a run in series groups as one client, and a parallel slot, the strip or another client ends it.
  • A lost worker comes back: after a killed worker, the daemon puts it back with its settings and the links it made itself in about 350 ms.

Released, to be checked on the device

  • Every plugin call saves and checks the registers a function must keep; a plugin that changes one is moved to a worker of its own.
  • A chain shares one JACK client per worker, and a mono plugin runs inside a stereo chain.
  • Plugins are added straight into a chain's worker, which is what makes a chain faster to build than mod-host.

Still open

  • Chains built from Zynthian's menu with grouping on, measured as the daemon's chains were.
  • A listening test through the audio interface: the figures so far are read digitally at the JACK ports.
  • Surge XT's CLAP build: a preset it saved does not load back yet, and its plugin folder cannot be pinned under Zynthian's pin settings. Preset listing and loading from Zynthian's menu for every instrument.
  • The round trip of a snapshot that holds a plugin-hostd instrument.
  • Mixbus chains and parallel slots measured with Zynthian's own mixer in the path on the device.

Coming next, planned

  • MPE in plugin-hostd chains: Zynthian sets up its MPE zone only for jalv chains today.
  • A bar group in omx-panel: a run of controls with the same range, such as an organ's drawbars or a graphic EQ's bands, shown as one row of bars on every panel.
  • Curves and meters on every omx-panel panel: exact for the omx dynamics and EQ, modelled from what the controls mean for other plugins.

08

Where the changes to Zynthian stand.

The engine and the system changes are carried on FreeMixer's forks of zynthian-ui and zynthian-sys. In zynthian-ui: the plugin-hostd engine and its client for plugin-hostd's protocol 0.3, plugin-hostd by default with a snapshot converter, menus from qualification, chains from the daemon's planner. In zynthian-sys: the OpenMixer repository, plugin-hostd as the mod-host of MOD-UI, MOD GUIs from omx-panel, OpenMixer in the default install. Merged and open pull requests are listed on each fork.

Upstream, one change is proposed so far: building MOD-UI's environment on Python 3.13, in zynthian-sys. The rest has not been proposed to Zynthian's maintainers yet.

openmixer

A software mixing console for Linux. The desk is the software; the browser is the surface.

Pages

Licence

OpenMixer is free software under the GPL-3.0-or-later. Every package in the workspace carries the same licence.

Roland, Midas, Behringer, RME and the product names used here belong to their respective owners. OpenMixer is an independent project and is not affiliated with any of them.