Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Write 0 Lines of C++: Building Commercial Audio...

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Write 0 Lines of C++: Building Commercial Audio Plugins with Rust and Web GUI

This talk presents a modern alternative to C++-dominated audio plugin development. We will explore how to build commercial-grade, multi-format plugins (CLAP, VST3, AU, Standalone) without writing a single line of C++, by using Rust for DSP/logic and Web technologies for the GUI. Based on real-world adoption in NovoNotes products, we will cover the "CLAP First" architecture with clap-wrapper, solving async task management (run_loop), integrating WebViews (wxp), and comparing this approach with JUCE.

Avatar for NovoNotes

NovoNotes

June 23, 2026

Other Decks in Programming

Transcript

  1. Kohsuke Yuasa • Lead DSP development at NovoNotes for 5

    years • Co-maintainer of wxp and run_loop • Previously at Crypton Future Media, Inc. Hatsune Miku), now also at LabBase Inc. • Co-author of a C reference book "C Pocket Reference" @hotwatermorning
  2. Sota Ichikawa • NovoNotes plugin development for 4 years, co-maintainer

    of wxp and run_loop • Currently at DeNA, doing multimedia R&D with deep learning • Graduate research in acoustic signal processing at University of Tsukuba SIY1121
  3. • An audio plugin vendor founded by Satoshi Suzuki in

    2020. • Products: 8 products • Users: 30K • Specializes in spatial audio NovoNotesDev
  4. WRAC Stack • A technology stack consisting of WebView +

    Rust Audio + CLAP. • Already used by over 15,000 NovoNotes users via WRAC stack-based plugins. CLAP Rust Audio WebView
  5. The Industry Standard C / CMake / JUCE — chosen

    for: • Real-time performance • Cross-platform support • Multiple plugin format support
  6. Our Team • Small team 35 members) • Includes part-time

    engineers with other primary jobs • Diverse technical backgrounds
  7. Why Not C / CMake? C Requires high expertise across

    the whole team to write safe code CMake: High setup and maintenance cost — tough for a small team
  8. Why Not JUCE? • Per-engineer licensing → expensive for part-timers

    • Business lock-in risk (fixed costs, pricing changes) → We looked for alternatives.
  9. Rust as an Alternative • Satisfies real-time and cross-platform requirements

    • Strict compiler enforces quality across diverse engineers • Cargo is far easier to set up and maintain than CMake Remaining question: Can we support multiple plugin formats?
  10. Plugin Development in Rust? • Building audio plugins typically means

    using JUCE • Can we really do it in Rust without JUCE?
  11. Plugin Development in Rust? • Plugin formats like VST3 and

    AU assume C / C / Objective-C • Rust bindings per format? → too much work for a small team • Each format has different APIs → abstraction layer needed too
  12. Plugin Development in Rust? • With JUCE: one codebase covers

    multiple plugin formats • Without JUCE: we lose that benefit
  13. Plugin Development in Rust? • With JUCE: one codebase covers

    multiple plugin formats • Without JUCE: we lose that benefit → Use CLAP.
  14. CLAP as an Intermediate Layer • Develop CLAP plugins in

    Rust with clap-sys • Convert to VST3 / AU with clap-wrapper → Write CLAP once in Rust → ship as VST3 / AU clap-sys clap-wrapper
  15. CLAP (by u-he & Bitwig): • C-based API → highly

    portable • Simple core + extension model → great as an intermediate layer • Open source, permissive license • https://u-he.com/community/clap/
  16. clap-sys • Rust bindings for the CLAP API • Low-level

    — requires your own safety guarantees • https://github.com/micahrj/clap-sys
  17. clap-wrapper • Wraps CLAP plugins into VST3, AU, and more

    • Can link a statically built CLAP and wrap it • AAX, AUv3 (incl. iOS) support in progress • https://github.com/free-audio/clap-wrapper
  18. DSP Overview • DAW invokes the audio process function of

    VST3/AU • clap-wrapper forwards it to clap_plugin.process() • clap-sys forwards it to Rust code
  19. DSP Overview impl Processor for MyPluginProcessor { fn process(&mut self,

    context: ProcessContext<'_>) -> PluginResult<ProcessStatus> { // Write your dsp here! // context contains the audio buffer and // the input events for this block. Ok(ProcessStatus::ContinueIfNotQuiet) } }
  20. How to Build the Plugin GUI? • Multiple Rust-native GUI

    libs exist (egui, iced, etc.) • But our team chose not to use them for plugin GUI — for several reasons
  21. How to Build the Plugin GUI? 1. Rust GUI libs

    carry high technical uncertainty for plugin use • Hard to verify feature support and cross-platform OS, DAW) behavior 2. WebView-based apps/plugins have proven track record Electron, Tauri, JUCE 8 → WebView gives a clearer development outlook
  22. Rust + WebView Challenges • wry crate provides cross-platform WebView

    in Rust ◦ Wry is a cross-platform WebView rendering library by tauri team. • But using it as-is for plugins is cumbersome and lack of feature → We built some crates to solve this. wxp, run_loop
  23. wxp

  24. wry's API is Too Low-Level • IPC between Rust ↔

    JS exists but is verbose to use directly • Tauri provides nicer high-level APIs for standalone apps • We wanted the same for plugins → built wxp
  25. wxp WebView × Plugin) • Wraps wry with plugin-friendly higher-level

    APIs • API design inspired by Tauri • Adaptive IPC: switches method based on payload size • https://github.com/novonotes/wxp
  26. wxp Code Example use std::rc::Rc; use wxp::{WebContext, WxpCommandHandler, WxpWebViewBuilder}; let

    mut web_context = WebContext::new(std::env::temp_dir().join("my-plugin")); let handler = Rc::new(WxpCommandHandler::new()); // `webview` must be kept alive while the UI is shown (see Caveats below). let webview = WxpWebViewBuilder::new(&mut web_context) .with_command_handler(handler) .with_serve_zip("wxp-plugin", FRONTEND_ZIP) .build_as_child(&window)?;
  27. Async Processing from Plugin Code • WebView JS communication must

    happen on the main thread • Need a mechanism to trigger main-thread work from plugin code • JUCE has MessageManager for this → we built run_loop
  28. run_loop • Platform-agnostic event loop library • Non-blocking — different

    from tokioʼs current() • Inspired by irondash_run_loop / JUCE's MessageManager • Useful for timers and inter-thread communication within plugin code • https://github.com/novonotes/wxp/tree/main/crates/run_loop
  29. run_loop Use Case • WebView IPC must be called on

    the main thread → run_loop required • Display real-time audio data in GUI (e.g. level meters, knob automation) • Must trigger WebView IPC at 30Hz without blocking the host's main thread
  30. run_loop Code Example use std::time::Duration; let run_loop = RunLoop::current(); //

    Schedule execution after 10 seconds let handle = run_loop.schedule(Duration::from_secs(10), || { println!("10 seconds have passed"); });
  31. How to Build the Plugin GUI? wxp + run_loop enables

    plugin UI built with web technologies via Rust + WebView
  32. Development Experience • Rust: compile-time safety catches dangling reference and

    data races; Cargo makes trying new libraries easy • WebView: support CSS; hot reload for UI tweaks without rebuilding; DevTools for easy debugging
  33. Closing We presented the WRAC Stack — a good fit

    for teams with constraints like ours, for the following reasons: • Rust instead of C ◦ Ensures memory safety even when not everyone on the team is a C expert ◦ Cargo's low maintenance cost suits a small team • Web tech for GUI ◦ Our products often need rich GUIs, e.g. for spatial audio ◦ A mature UI technology → a clearer development outlook • Permissive license ◦ No vendor lock-in
  34. Try It Yourself: wrac-plugin-template • A template for implementing audio

    plugins with the WRAC stack. • You can copy this repository as a starting point for new projects. • https://github.com/novonotes/wrac-plugin-template