VST3 without VSTGUI

After a couple months of dev on my instrument, I’ve grown frequently frustrated with VSTGUI. It’s not horrible in design, and I get where the authors are going with it and what they’re trying to do, but:

Poorly / incompletely documented.
Many things missing.
Custom graphics rendering library also poorly documented (should just use Skia and at least then I’d have a well documented !)

However, after giving it a try a couple times I’m still 90% unclear how to get to a place in my controller where I can just pop up a native platform window and render to it myself.

Does anybody have any example code of having done this? I’m assuming I’d have to ditch inheriting from EditController and friends, but would I still have access to the Parameter framework?

Following up on this, I was actually able to figure out what I needed to view, which was subclass EditorView, and return kResultTrue for isPlatformTypeSupported.

The challenge that I see now is that the framework and I guess the host really wants to own the window and keyboard events and so on.

What I was hoping to do was embed platform webview (using something like Saucer) and provide a bridge to the EditController via JS callbacks and then write the UI in TypeScript/HTML, etc. But the window ownership and event handling looks like this could end up being a bit less simple than I initially hoped.

And following up again, in case anybody follows this… here is my findings, since I didn’t find a clear single explanation elsewhere:

  • Subclass EditorView. Or, alternatively, CPluginView if you want to go further up the chain as EditorView doesn’t add much.
  • Create and destroy your own window via ::attachToParent and ::removedFromParent
  • Set your window as a child of the “systemWindow” there. Do not use the system window directly.
    It’s not yours. I haven’t tried on OSX or Linux (gtk) yet, but on Windows I was able to create a child window, embed it in the parent, and remove decoration, and this seemed to work. I’ll still have to investigate what this looks like on OSX and Linux.
  • I ran into issues with the above until I made sure that creation and management of my child window was entirely contained within my own thread, separate from the thread that is calling into ::attachParent. So far I have only tested this in the VST3PluginTestHost and Cakewalk.

Using this scheme I have been able to embed a Webview2 (Edge-Chrome yada yada) instance in as the UI for my app. I am now working on piping through access to parameters (and other controller functions) in through JS. And thus it should be possible for me to build out the entire UI for the app in JS/HTML while keeping the processor in native C++.

If I can make it elegant and clean and portable enough, I’ll likely release the pieces for this open source, in case others are interested.

1 Like

Cool stuff, but remember that all calls to and from the host must be done thru the main thread (the thread where the attached call is happening). See the note at the end of this page: Edit Controller Call Sequence - VST - Steinberg Developer Help

Yes this is the current piece of work, creating a little synchronous event bus for passing state and callbacks between the controller threads.

Arne, since you’re here… can you give me any insight on the UI thread behaviour beyond the diagram you linked to? Trying to work out a method for passing state back and forth between webview and the main UI thread and it’s not working out as well as I had hoped.

Specifically, I assume somewhere there is an event loop dispatching work to the UI thread (whatever calls ::notify, etc.). I guess the host is responsible for this somehow? Is this facility available through the API at all?

Mainly my concern is I’d like to be able to have a way to dispatch back to the UI thread from some other threads I might create on the controller side. In other frameworks I’ve worked in (chromium’s chrome::base for example) there is an explicit task executor model for managing this kind of thing (in chromium via sequenced task runners, e.g. ) which ties back eventually to the event loop to allow dispatching between threads.

I had hoped there might be the possibility for thread-hopping through IDependent/IUpdateHandler, but that appears not to be the case.

Is the expectation that each thread should be a separate Component and I should be communicating through that means? But is it possible for me to have multiple components speaking to the EditController, or Component connection always 1:1?

So many questions.

( Vielen Dank im Voraus)

Sorry, there’s no support for your use case in the SDK. On macOS it’s straight forward to do this via libdispatch. Don’t know if there’s something similar on Windows.

Ok thanks. I might be able to make do by re-writing the webview abstraction library I am using to make use of the existing win32 platform message pump instead of requiring its own thread. The trick will be making it cross platform and handle bidirectional communication.

It seems like in the long run Steinberg might want to consider adding some sort of notion of a “execute this callback back on UI thread” type of functionality to the SDK.

Brief update on this:

I now have a working implementation of using a webview as a VST frontend that runs both Windows & Linux. On Windows using Edge-Chromium (“WebView2”) and on Linux using webkit2-gtk.

A JS/native bridge lets me call into the EditController from the JS side, and vice-versa, and I have a little framework for piping message/message-attributes over as well. I have a whole front end for my VST written in HTML/CSS/TypeScript and it’s working great, very smooth, no performance concerns.

(I was able to work around the VST3 threading model constraints successfully. On Windows, the existing message pump seems to drive the WebView2 just fine. On Linux I tie into the Linux-specific IRunLoop piece and use a 60fps timer to drive the GTK main loop, on the same thread. I’ve used the ThreadChecker to confirm everything is scheduled on the correct thread and all is well.)

I plan on open sourcing all the pieces here once I’ve cleaned up a bit, ported to OS X and written some tests.