Goal: deliver a plugin GUI that exposes all 400 engine parameters across three named surfaces. Done: the Front/Detail/Back editor is implemented, approved, and host-tested on 2026-08-18 in two of its four plugin formats, VST3 in Ableton Live 12.3.2 and Bitwig and CLAP in Bitwig; it builds in all four; it is a working foundation and an interim compromise, not the production-final interaction hierarchy. Open: release acceptance still covers engine performance, preset and event transitions, default output level, audible crackle and pop sources, and continued UX/UI and legibility refinement.
The signal path
The Front surface shows the signal path. The instrument runs three oscillators into a pair of wavefolders, through two cascaded filters, into a voltage-controlled amplifier. Envelopes and LFOs can drive any parameter in that chain through the modulation matrix on the Back surface. The six Front panels follow that route left to right: 01 OSCILLATORS (bone), 02 WAVEFOLDERS (vermillion), 03 FILTERS (slate), 04 VCA plus NOISE (ochre), 05 EFFECTS (teal), 06 OUTPUT (neutral). Each panel carries a DETAIL affordance that opens the full-canvas workspace for that module.
Controls are conventional: rotaries, sliders, dropdowns, and explicit ON/ACTIVE/BYPASSED states with typed values visible. Front owns 82 parameters. The implementation carries 72 to 73 simultaneous controls at 1600 by 1200, rather than the 65 in the approved design specification. The specification reached 65 partly by hiding eight effects controls that are alternatives to nothing; hiding them in the live editor would make them unreachable, so reachability took precedence.
Module workspace
Selecting a panel from the Front opens the Detail surface: a full-canvas workspace for that module. Detail carries 246 controls. Its membership is derived from the live AudioProcessorValueTreeState rather than authored by hand, so a parameter added to the engine cannot silently fall out of the editor. Its presentation is authored: 73 semantic subgroups with no alphabetical wall. Eight stepped-float categoricals appear as labelled choices built from the consuming DSP tables rather than continuous knobs.
The modulation matrix
The Back surface is the full 24-slot modulation matrix as a dedicated third surface. Each slot exposes a source, a target, and a signed depth that sets both amount and direction. Back carries 72 controls. The 12 sources and 20 current targets reflect the live engine state. A target is a parameter a voice can offset where it consumes it, so the list holds exactly the 20 that have such a site: cutoff and resonance on both filters, and eight controls on each of the two wavefolders. The effects rack, the master section, and the console sit after the voices are summed, where no single voice can reach them, so they are not targets at all. The interface states that boundary directly.
Responsive operation
The editor has two supported native sizes. At 1600 by 1200 the six Front panels lay out at full target size. At 960 by 720 the same six panels remain and all three surfaces remain reachable; the dense panels run rotaries at the 32 px minimum floor rather than 40 px, because a 15 px label plus a 32 px target exactly fills the panel cell at that width.
The responsive layout adapts the control geometry to the available space rather than scaling one fixed canvas. Both sizes expose all 400 controls across Front, Detail, and Back.
Verification
Host automation updates stored values and is reflected in all connected controls. Editor close and reopen, and full state serialization, restore all parameter values, bypass states, matrix slot contents, and all eight LFO mode and Division pairs. All 400 controls are keyboard focusable and report names, roles, and current values. The instrument carries 401 parameters in total: the 400 host-automatable controls plus voices, a non-automatable session ceiling placed in the header beside the preset selector, belonging to no surface deliberately. This keeps the ownership census accurate. This is state v12; sessions saved before v12 load at the previous ceiling of 8 and keep their sound.
The editor loads in the VST3, AU, CLAP, and Standalone plugin formats; VST3 was validated by load test in Ableton Live 12.3.2 and Bitwig, CLAP by load test in Bitwig, AU validation returned SUCCEEDED with attribution to the specific build not established due to contended install custody, and the Standalone artifact is a valid arm64 Mach-O that was not launched on a live audio device. The verification pass covers keyboard operation, focus visibility, minimum hit regions, host gestures, state restoration, and both supported layouts. Operational text is at least 15 px, headings at least 18 px, minimum touch targets 32 by 32 px. Eleven captures, zero collisions. Three deterministic capture runs returned byte-identical results. All 34 factory programs render byte-identically to the pre-GUI engine: the editor changes operation without changing audio. The suite stood at 318 Catch2 cases for this pass: 317 passed plus one expected Release-only skip in Debug with 2,221,771 assertions, and 318 of 318 in Release with 2,221,773 assertions. It has since grown to 323 cases as later work added tests. RTSan clean. The editor was visually and operationally tested in Ableton Live 12.3.2 (VST3) and Bitwig (VST3, CLAP) on 2026-08-18; the restored-solve integrated binary awaits deliberate installation and final host acceptance alongside the open engine work.
The CLAP build comes from the same tree as the others. One juce_add_plugin target emits VST3, AU, and Standalone, and CLAP is produced beside them by clap-juce-extensions, a bridge that wraps the same processor rather than reimplementing it. One consequence runs through everything that follows: the formats share a single processBlock, so a format cannot change the sound by itself. What can differ is how a host reaches the parameters, stores the state, and calls the threads, and that is where the comparison is worth making.
Two of the differences are friction in the bridge rather than gaps in the standard. The first is parameter range. CLAP carries an explicit minimum and maximum for every parameter, but the bridge normalizes each value to the range zero to one unless told otherwise, which made values read wrongly in a CLAP host while the same build was correct as VST3. Setting CLAP_USE_JUCE_PARAMETER_RANGES to ALL in the build configuration passes the real ranges through, and that is how Eidolon builds. The second is parameter identity. CLAP requires a stable integer id that must never change, JUCE identifies a parameter by a string, and the bridge hashes one into the other. I checked the whole set rather than assuming it: the 400 automatable parameter names hash to 400 distinct ids with no collisions. The assertion that would catch a collision is compiled out of a release build, so this is a check to repeat if the parameter set grows, not a result that stays true on its own.
The third difference is the interesting one, because it is a real capability the instrument declines to use. CLAP can deliver a parameter change as a sample-accurate event inside the audio call. Eidolon reads its parameters once per block, as it does in every format, so that precision is flattened to the value standing at the block boundary. Automation therefore feels the same in all three formats, and for a fixed parameter set, identical MIDI, and the same block size, the three produce bit-identical audio by construction rather than by luck. Taking the finer timing would mean consuming CLAP's events directly, which the JUCE abstraction does not expose.
The same pattern holds across the extension model. CLAP is a plain C interface assembled from optional extensions, and the bridge maps the ones JUCE already expresses: parameters, state, editor, note and audio ports, latency, and tail. Two CLAP capabilities have no JUCE path and go unused. Its host thread pool is one, so the eight voices run serially in every format, and its native voice information is the other. Neither absence is a defect, but naming them is the honest answer to what parity buys here. Eidolon matches VST3 and AU on everything those formats expose and leaves the CLAP-only capabilities on the table. One further asymmetry belongs on the record: a state the instrument rejects is still reported to a CLAP host as a successful load, because JUCE's state method returns nothing the bridge could pass back. VST3 and AU go through that same method, so the observable behaviour matches across formats, and CLAP is only the one whose bridge names the limitation in its own source.
Evidence
These three clips are deterministic offline renders, not screen recordings. A headless render tool constructs the plugin's audio processor, applies the same parameter values the editor writes when a control is moved, and renders the result to a file. Rendering any clip twice produces a byte-identical result. The screenshots above are the evidence of the controls; the audio here is the evidence that the controls reach the engine.
Filter cutoff from the Front surface
A modulation route from the Back surface
Six factory programs
CPU: where it stands
The instrument ships with a voice ceiling defaulting to 6. Raising that ceiling to 8 pushes several programs past comfortable CPU headroom. The ceiling costs nothing sonically: all 34 renders are byte-identical at either setting.
Running under 40% of one core at eight voices is unreachable without changing the audio. That is why the ceiling exists.
4× per-voice oversampling is 63 to 73% of every voice's cost, and that figure went unmeasured until four optimization passes had already run inside it. The workload is latency-bound, not instruction-bound: three correct instruction-count reductions measured slower.
One result shipped: a SIMD additive-oscillator rewrite that cut P32 by 12.75% and the full 34-program corpus by 3.07%, with every output sample byte-identical.
The largest single win found was declined, and the decision carries a stated, measured price. A replacement wavefolder solver measured 17.39 to 18.43% on P01 and was more numerically accurate than the solver currently in the instrument. I declined it by ear because it changed the sound of three factory programs. The instrument currently runs P01 at 47.02 to 50.45%, 2.69 to 2.89 times what the rejected solver would have cost. That decision is recorded as D-030. An instrument is not a benchmark.
During the measurement campaign, planning forecast that roughly 14 of 19 polyphonic programs would run under 40% at ceiling 6. The measurement at that point returned 7 of 19. Class averages do not predict per-program outcomes when several programs sit within a few points of the threshold. The shipping tree has not been remeasured in full since that pass; those figures describe a specific measurement, not the current state.
Work in progress: whether the retained solver can be made faster without changing a single output sample is measured but unattempted.
Reflection
Presenting 400 parameters to this standard was a slog. The result is a working editor and a useful compromise: each parameter has a home across three surfaces, but the interaction hierarchy is not the final word on how 400 controls should live together. That work continues.
The development method felt right throughout. Measurements localize mechanisms and protect regressions; controlled comparison, host interaction, and listening decide whether the combined instrument is usable and musically right. Every question that mattered got settled by that sequence, and my ears feel vindicated in it.
The development approach, including AI tool use throughout the project, is documented on the Tooling Disclosure page.
References
- JUCE.
AudioProcessorValueTreeStateclass reference (processor state, parameter layout, and control attachments). - JUCE.
AudioProcessorParameterclass reference (host-visible parameters and automation gestures). - W3C Web Accessibility Initiative. Understanding Success Criterion 2.1.1: Keyboard (keyboard operation).
- W3C Web Accessibility Initiative. Understanding Success Criterion 4.1.2: Name, Role, Value (programmatic control identity and state).
- W3C Web Accessibility Initiative. Understanding Success Criterion 2.5.8: Target Size (Minimum) (usable pointer targets).