Soundorv0.13.0

Architecture

How a project becomes a plugin: build time and run time, rendering, threads and accessibility.

Soundor has a build-time toolchain and runtime implementations. The toolchain turns a project contract and UI source into generated bindings and bundles. Each runtime supplies the same UI-facing modules using its own platform services.

From project to runtime

flowchart TD
    Config["soundor.config.ts"] --> Toolchain["CLI: validate and generate"]
    Toolchain --> Contract["Types, manifests, and native bindings"]
    Source["src/main.tsx and npm dependencies"] --> Bundle["CLI: bundle UI"]
    Bundle --> ESM["bundle.mjs: ES module"]
    Bundle --> Native["bundle.js: lowered native module"]
    Contract --> JUCE["JUCE plugin: your C++ host and DSP"]
    Contract --> Web["Browser host: your TypeScript and Web Audio"]
    Native --> JUCE
    ESM --> Web
    JUCE --> Engine["Soundor Engine: Hermes, Yoga, Skia, ANGLE"]
    Web --> Browser["Browser JavaScript, DOM, Canvas, ARIA"]

The CLI bundles npm dependencies into the UI and leaves soundor:* imports for the runtime. It emits an ES module for the web and a module function lowered for Hermes. Production native builds compile that module to bytecode with the matching hermesc.

Packages

The toolchain, the React renderer, the runtimes and the engine are separate packages; a project installs the CLI, @soundor/react and its runtimes. Packages lists them all, for contributors.

The runtime contract

Every runtime implements the soundor:* modules and the supported Web globals. Generated TypeScript declarations come from the project configuration, so the UI sees the configured parameters and native methods rather than an untyped dictionary.

Matching semantics matter as much as matching signatures. The web runtime reproduces native parameter clamping and gestures, input routing, layout defaults, accessibility rules, and errors where applicable. A change to a shared behavior usually needs native, web, and test-runtime updates.

The browser naturally has additional APIs. Portable UI code should compile against Soundor's generated declarations without TypeScript's full DOM library. Browser-host files have their own DOM-enabled TypeScript configuration.

Native rendering

Each open native editor owns a RuntimeHost: a Hermes JavaScript world, UI surface, runtime modules, services, and pending work. React updates the surface's node tree through soundor:ui.

  1. Yoga computes logical layout when it changes.
  2. Skia rasterizes damaged UI content and 2D canvases into CPU layers.
  3. Canvas WebGL draws through ANGLE into GPU textures when a device exists.
  4. Soundor's compositor combines the layers and presents the frame.

The GPU compositor uses ANGLE on Metal or Direct3D 11 where available. Linux and fallback paths use CPU composition. GPU WebGL can still exist with CPU composition, where presentation requires readback. The compatible GPU composition path shares textures without WebGL readback.

Damage tracking and caching avoid repeating layout, rasterization, and texture uploads for unchanged content. Native opacity and transform animations can advance in the C++ timeline without a JavaScript callback for each intermediate frame. Paint animations repaint affected content. See the rendering reference and animation contract.

Threads and ownership

JavaScript, node mutation, event dispatch, and native animation timelines run on the UI/message thread. The generated editor calls RuntimeHost::tick() at 60 Hz to deliver pending parameter changes, run timers, and settle async work.

Audio callbacks execute your native DSP. Parameter listeners that may run on the audio thread only set lock-free flags; the UI thread later delivers subscriptions. JavaScript never executes on the audio thread.

Background I/O returns plain C++ results and an operation ID to the owning thread. JavaScript values stay with their context. Reload tears down the old host before creating a new one, including pending jobs and subscriptions.

Multiple plugins share a DAW process. Engine symbols have hidden visibility, dependencies are statically linked, and the engine version determines its inline C++ ABI namespace. Link-time checks reject incompatible headers and libraries. See Symbol isolation and ABI versioning.

Browser rendering and services

The same React renderer produces Soundor nodes backed by DOM elements. The web runtime provides Yoga-like layout defaults, Soundor event routing and focus behavior, and root/overlay stacking inside the plugin viewport.

One page or iframe owns one host context. The UI, native.ts, audio.ts, and host controls share it directly. Native API calls are ordinary TypeScript calls; there is no serialization boundary. Storage and the private file tree use IndexedDB, isolated by plugin ID.

The CLI still bundles src/main.tsx. Vite serves or builds the browser host and resolves the bundle's soundor:* imports. See the web runtime.

Accessibility and input

Soundor derives a semantic tree from node accessibility properties, including roles, labels, values, modal visibility, and logical ownership through portals. That tree drives native platform bridges and the web runtime's ARIA attributes.

Windows and Linux use AccessKit; macOS uses Soundor's Apple bridge. The engine also contains an iOS bridge, but Soundor has no iOS plugin build yet. JUCE supplies the native view and attachment point. It does not define Soundor's control semantics.

Pointer and keyboard routing also follow the Soundor tree. React's Modal and FocusScope add keyboard focus management. Details and examples live in the React reference, native accessibility reference, and Apple bridge guide.

Development and test boundaries

DevTools connects a browser dashboard to an authenticated local server. The native development Standalone connects through a worker-based bridge, while inspection and mutations run on the owning UI thread. Node references include a generation so reload invalidates stale requests. Release builds exclude the native debug bridge. See CLI development.

Application tests run in Node against an in-memory runtime. They use the real React renderer and shared semantic rules, with a virtual clock for animations. They cover observable UI behavior without doing layout, rendering, native execution, or audio processing. Native suites and browser tests cover the platform implementations. See Testing and Contributing.

On this page