Back to Website Growth Insights
Engineering Notes

A VST3 plugin opened as a black window, and the bug was in neither the plugin nor my code

One plugin drew its editor. The other opened a window with nothing in it. Here's the diagnosis, the fix, and the patch that went upstream.

Engineering Notes5 min read
Share

I was building a small rack-style DAW for Linux in Rust. It hosts VST3 plugins, and one of them refused to draw.

Not a crash, and no error in the log. The editor window opened at the right size and painted nothing.

The symptom

One drew its editor. One didn't.

Surge XT opened its editor and rendered correctly. sfizz opened a window and painted nothing. Same host application, same machine, same session, one after the other.

A blank window is a worse symptom than a crash. A crash gives you a stack. A window that opens at the correct dimensions and then does nothing tells you the plugin was found, loaded, instantiated and asked for its editor, and that something after all of that quietly didn't happen.

The variable

The difference was which toolkit drew the window

Two plugins, one host, opposite outcomes. That's a differential, and it's worth more than any amount of reading the code, because it narrows the search before you start.

Surge XT builds its interface on JUCE. sfizz builds its interface on VSTGUI. Everything else about how the host loaded them was identical.

  • Surge XT, JUCE-based: editor drew correctly
  • sfizz, VSTGUI-based: window opened, stayed black
The cause

VST3 on Linux expects the host to provide a run-loop

On Linux, a VST3 plugin editor doesn't own an event loop. It asks the host for one. The host is expected to expose a run-loop service so the plugin can register file descriptors and timers and be called back when it needs to paint or respond.

VSTGUI depends on that service. JUCE doesn't - it manages its own message handling and never asks.

That asymmetry is why the bug was invisible from one side. A host that never implemented the service works with JUCE-based plugins and fails silently with VSTGUI ones, and nothing in the logs says so.

Where the fault sat

The fault sat in the library in between

The dependency direction is the part worth getting right, because it's the part most people get backwards when this is described.

The host library sits underneath the host application. My application depended on it. The plugin doesn't - it never sees it. The run-loop is a service the host owes the plugin, and the library that was supposed to provide it on my behalf had never implemented it.

So the plugin was correct, my code was correct, and the thing between them was missing a contract neither of them could see.

The fix

Patched, then verified against reality rather than against itself

I implemented the run-loop service in a vendored copy of the library, so the host could hand plugins something that actually worked.

Then I verified it by capturing real audio off the system's PipeWire stream and listening to it.

That distinction matters more than the patch does. A unit test written by the person who wrote the fix tests that person's assumptions. It agrees with you by construction. Capturing what actually came out of the machine doesn't. It's the same habit that decides which accuracy figure gets published on the other thing I built.

Upstream

Upstreamed, and one thing still open

A vendored patch fixes one tree. It doesn't fix the problem.

So I forked the library, ported the patch onto the maintainer's current release, ran it against their full CI matrix, and opened a pull request.

PR #10, "Implement Linux IRunLoop so VSTGUI-based plugin editors work", merged on 27 July 2026. It has shipped in released versions since v0.8.0, so anyone building a VST3 host on that library already has it.

The patch is public: PR #10 on the rust-vst3-host repository.

One thing is still open, and it comes from the same mechanism. Audio can stutter while a VSTGUI-heavy editor is on screen, because the audio thread and the editor-servicing pump contend for a lock. It's diagnosed precisely, the workaround is closing the editor while you listen, and the proper fix, splitting the audio-critical and GUI-servicing locks, is scoped but not built. Still in development.

  • Forked the upstream repository
  • Ported the patch to their current release, not my vendored version
  • Verified against their full CI matrix
  • PR #10, merged 27 July 2026
Why this is on a web design site

The same move, one layer down

Around 1999 I hit the ceiling of what Dreamweaver would let me do and went down a layer, to HTML, CSS and JavaScript. This is that again, further down.

It's also what a hard performance job looks like. When a site won't get faster and every obvious cause has been ruled out, the answer is usually one layer below where anyone has looked, and it's rarely the thing being blamed.

Questions

Questions local business owners usually ask next

Next step

Contract and subcontract development

Root-cause debugging, audio and systems work, and performance jobs on the web. If you've got something that needs someone to go a layer down, start here.