Awesome
tomduncalf_juce_web_ui
tomduncalf_juce_web_ui is a JUCE module providing helper classes to make it easy to integrate a web-based UI (running in a JUCE WebBrowserComponent) with a JUCE application (e.g. an audio engine).
Please see https://github.com/tomduncalf/WebUISynth for an example integration with a simple synth engine.
Ultimately the integration will be cross-platform on both desktop and mobile, but right now it has only been tested on macOS and iOS.
Status
Experimental, Work in progress - I have a basic example working, but need to add more features, documentation and a tutorial. Should not be considered stable in terms of APIs.
See the project board for a list of tasks.
Features
-
"Batteries included" defaults, using MobX and React to make working with your audio engine as straightforward as possible – for example, Parameters are exposed in a reactive manner via React Context, making hooking up your parameters easy.
-
Great developer experience – integration and communication is made as straightforward as possible on both the JUCE and browser sides (see below), and features web developers love such as hot reloading and developer tools are all available. As the UI is "just a web page", your favourite libraries can be reused for functionality and styling too.
-
Good performance – in my initial tests, a web based UI seemed to perform similarly to a native JUCE UI in terms of CPU usage when playing back a sequence in a host with automation and a scope visualiser open. This area needs more research however!
-
Cross-platform (planned, currently Mac/iOS only) – every platform supported by JUCE now provides a mature WebView component, so web UIs should perform well across all platforms, both standalone and as a hosted plugin.
Key components:
-
A browser component which supports sending messages to and from a web browser, using JUCE's support for calling Javascript functions directly to call into the browser, and custom additions to JUCE to allow sending messages directly back to C++ (needs to be integrated into JUCE - for now, requires using my fork and is Mac/iOS only - but greatly improves performance e.g. with multi touch updates).
-
A base class for any classes which want to communicate with the browser, allowing sending messages and registering callbacks in a namespaced way.
-
Automatic one-way state synchronisation from JUCE ValueTrees to a JS representation using a ValueTreeSynchroniser and a TypeScript implementation of the ValueTreeSynchroniser protocol and ValueTree class.
The synchronisation is batched into one message per frame (at 60hz) to reduce redundant messages back and forth - however, there's room for more optimisation here!
-
A base class for plugins which automatically handles synchonising the plugins AudioProcessorValueTreeState to the browser, and corresponding models on the JS side which provide a representation of the parameters, with getters and setters for easy modification.
Auto-generated TypeScript definitions for your parameters (generated when run in debug mode) reduces the amount of manual integration to a minimum, and the use of MobX means keeping your UI reactive is simply a case of adding observer to React components and accessing
Parameters
from React's Context API. -
Lightweight mechanism for communicating between TypeScript and C++ by sending messages and registering callbacks.
For an example of C++ to browser communication, see the implementation of the Scope in C++ and TypeScript.
For an example of browser to C++ communication, see the implementation of setting parameters in C++ and TypeScript.
Documentation
Documentation is still TODO – once I am happy with the APIs and functionality, I will provide documentation and a tutorial but for now, there are some code comments in the source code and the example WebUISynth integration.
Motivation
Building user interfaces in C++ can be challenging, especially for developers coming from a web background. Advances in the web world over the last decade including declarative UI frameworks such as React, hot reloading, and the ever expanding capabilities of both CSS and JavaScript have made developing complex UIs considerably easier – going back to imperative code updating a UI and a full compile cycle each time you make a change can feel like quite a step backwards.
If you are happy to build for just one platform, native frameworks offer a better experience (on MacOS/iOS at least), but if you are building a cross-platform app, options are more limited. I have worked extensively with React Native in combination with JUCE (see my talk from ADC'19 about building an app with this stack), but on desktop React Native is still quite an immature option. Flutter is an interesting option in many ways, but it requires learning a new framework and language.
JavaScript (and in particular React) is widely known and fairly easy to learn. Browser engines are incredibly powerful and remarkably performant, so in theory there's no reason why they shouldn't be suitable for building UIs for audio apps and plugins – indeed, a developer from Output spoke about their experiences building their Arcade plugin using web UI at ADC'20.
The difficulty often comes with combining and interacting between two separate worlds – you have to worry about keeping state in sync, how to pass messages between JS and C++, etc. This module aims to solve those problems as elegantly as possible, so you can focus on writing great software.