If you haven't read the VST3 Controller overview, then please do so now and then hop back over here.
The Controller::createView() Chain Reaction

Everything in red is your responsibility.
The good news? Most of it is reusable boilerplate you'll write once and keep forever.
Why Not Just Use JUCE / pugl / VSTGUI?
You can. They work. But you still end up learning a vendor framework instead of learning VST3 itself.
Those frameworks wrap the VST3 layer with their own abstractions, which:
- hide how the host actually works
- introduce their own quirks
- make debugging host-specific issues harder
- lock you into someone else's UI/event system
And since each DAW has its own VST3 host implementation, knowing the real VST3 model lets you diagnose weird DAW behavior instantly instead of waiting for a framework update.
A few (three) things to know first
These three architectural truths will save you from self-inflicted chaos:
1. The Controller and Processor are not friends
They don’t share a thread.
They might not even share a process.
And depending on the host? They might not even run on the same machine.
So:
- No shared memory
- No custom IPC
- No "but I’ll just..." hacks
They only communicate through the host-managed message-passing interface (MPI).
You don't control how the host implements this. Some hosts queue, some batch, some throttle.
We'll go deep on MPI later.
2. IPlugView is ephemeral
The Controller destroys and recreates your view constantly.
Click your plugin → click another → click back = two creations and a destruction in 3 clicks!
So the rule:
Never store long-term state in your View.
Only the Controller (or shared state context) should hold anything that must survive UI churn (discussed in the VST3 Controller post also).
3. No non-const globals or statics
Let that be law.
Multiple instances of your plugin running in a DAW will trample each other and the results will be hilarious, but unusable.
Static const is fine. Static mutable is a war crime.
The Event Pump Problem
To build a stable UI layer, we need three things:
- A consistent event loop (i.e., your "game loop")
- Running on the same thread as the Controller
- Graceful startup/shutdown because this will happen a lot
The catch?
You must run your UI loop on the Controller's thread, but you cannot block that thread, because the Controller still needs to talk to the host.
Every OS handles event pumping differently:
- macOS uses the NSRunLoop + view hosting
- Windows needs a message pump tied to the HWND
- TimerQueue or SetTimer API
- Linux... I love Linux, but don't waste any priorities on implementing an X11 one.
Conclusion
For now, meditate on these facts:
-
The Controller and Processor can't share data directly.
They broke up in VST3 and only communicate through the host's arbitration layer. -
Views are temporary.
Your UI will be created and destroyed constantly.
Don't store anything important inside it. -
No static mutable state.
Trust me, you don’t want to debug multiple plugin instances racing each other.