Host a module in React
Embed a compiled module and its face with a headless React provider, parameter hooks and audio handles.
Import the headless host from maruaudio/module-player/react to play a built
module inside your own React application. You supply the module declaration,
its compiled Release, an optional face and your application controls. The
provider renders its children without adding a playback bar, styles or a
global session. React and React DOM are supplied by your application.
Add a module and face
Install maruaudio, React and React DOM in your application. Build the module
and copy its complete web package into your app's public assets, for example
public/modules/my-module/. Keep the Release, Programs, resources and hashed
assets together. The imported declaration must describe that same module.
import {
ModulePlayerProvider,
ModulePlayerFace,
useModulePlayer,
useModuleParameter,
} from "maruaudio/module-player/react"
import { AUDIO_MODULE } from "./my-module"
import MyModuleFace from "./my-module/ui"
const module = {
definition: AUDIO_MODULE,
releaseUrl: "/modules/my-module/release.json",
}
function Controls() {
const { phase, error, controller } = useModulePlayer()
const drive = useModuleParameter("drive")
return <>
<button onClick={() => controller.start().catch(console.error)}>
{phase === "starting" ? "Loading…" : "Start audio"}
</button>
<button onClick={() => void controller.stop()}>Stop</button>
<input aria-label="Drive" type="range" min={0} max={1} step={0.01}
value={drive.value}
onChange={event => drive.setValue(Number(event.target.value))} />
{error && <p role="alert">{error}</p>}
<ModulePlayerFace component={MyModuleFace} />
</>
}
export function App() {
return <ModulePlayerProvider module={module}>
<Controls />
</ModulePlayerProvider>
}
Replace the imports and parameter path with your module's exports. A face is
optional: remove ModulePlayerFace and draw every control yourself, or use
useModuleFace() to pass the standard AudioModuleFaceProps into a custom
layout. Import the face's own styles as required by its package. The host
does not import the UI kit, application fonts or global styles.
Mounting does not create an AudioContext or ask for device permissions. Start audio from a user gesture. An effect also needs an input source; an instrument needs its note or MIDI input. The face sends edits to the same host that processes audio.
Hooks and controls
| API | What it provides |
|---|---|
useModulePlayer() | Reactive phase, error, parameter values, preset identity, dirty state and live audio handles, plus the controller |
useModulePlayerController() | Stable imperative handles for event handlers and external integrations |
useModuleParameter(path) | One engine-unit value, its descriptor, setValue, beginEdit and endEdit |
useModuleFace() | Host-bound values, parameters, edit gestures, factory preset library, notes, levels and audio readiness |
useModulePlayerSession() | The canonical session snapshot and its controller for MIDI devices, CC mappings and active notes |
useModuleLevels(enabled) | Opt-in input/output meter sampling while running |
The controller exposes start, stop, setParam, getParam, selectPreset,
noteOn, noteOff, panic, setMonitorLevel, connectInput, loadResource,
saveState, restoreState, send, sendMidi and onMidiOutput.
beginEdit/endEdit report gestures through options.onParamEdit for a host
that records automation. send is for non-parameter events; write parameters
through setParam so the engine and face remain synchronized.
Parameter values use schema engine units, such as hertz or a numeric enum index. Bounds, steps and taper come from the module schema. The host passes engine values through the SDK event API; it does not normalize them a second time. Factory presets apply one parameter snapshot and retain selection/dirty identity. Saving user presets or offering generation, camera and file import services requires the owning application's services; this provider does not advertise absent capabilities.
Connect an audio source
After Start resolves, controller.getSnapshot().audio contains the active
context, SDK host, output analyser and input analyser (null for a module
without audio input). For example, a host
that has already acquired a microphone stream can connect it like this:
await controller.start()
const { context } = controller.getSnapshot().audio!
const source = context.createMediaStreamSource(stream)
const disconnect = controller.connectInput(source)
// When the input changes or the host closes:
disconnect()
// Stop stream tracks only if this application owns the stream.
connectInput removes only the connection it owns. The player never stops
caller-owned sources or media tracks. Start an input source such as a buffer
player in your app and connect it in the same way. Monitor level is host gain,
separate from the module's own parameters. Read the output analyser for a
custom visualizer, or use the opt-in meter hook.
Raw MIDI bytes go to sendMidi; its boolean result reports whether the
running Program accepts that dialect. Use noteOn/noteOff for note-based
interaction. onMidiOutput subscribes to the Program's emitted batches;
the application chooses how to route those events. Device selection and CC
mapping use the exposed session controller rather than a second MIDI handler
inside the face.
Supply a Release directly
A module package may already export a parsed Release and its file source.
Pass { definition, release, source } instead of { definition, releaseUrl }.
The source implements the SDK's browser Release file-source contract. URL
loading supports optional sha256 and fetchImpl on the module descriptor.
The SDK verifies artifact/file hashes and chooses the requested available
Program; stale or failed qualification is not silently bypassed.
For an evidence-empty draft, a measuring harness or an intentionally unqualified JS-only Release, supply an explicit SDK activation policy:
<ModulePlayerProvider module={module} options={{
prequalification: {
purpose: "unqualified-release",
artifactId: "program:js",
},
}}>
<Controls />
</ModulePlayerProvider>
development and qualification are the other explicit purposes. This
option names an exact Program and applies only to evidence-empty Releases.
Read the active SDK host's qualificationStatus for its actual status.
Ownership, state and teardown
The player creates its own AudioContext on Start and closes it on Stop or
unmount. Pass options.context to borrow an application-owned context; the
player resumes it on Start and never closes or suspends it. All connected
nodes must belong to that same context. options.destination selects the
output node; false leaves the output for your surrounding host to connect.
Keep the declaration, Release and file source stable across renders. Changing them replaces the player and disposes its old runtime. Inline option objects and callback updates do not restart an otherwise unchanged host. React StrictMode effect replay does not dispose a still-mounted player. Unmount cancels pending loads, releases owned connections and closes owned audio resources. Independent providers have independent state.
saveState() returns the module ID and parameter snapshot, including before
Start. saveState({ includeProcessor: true }) also requests opaque Processor
bytes from the running Program; it rejects when that Program has no codec.
Opaque Processor bytes are
bound to that Program's artifact identity. restoreState() refuses another
module or an incompatible Program. Parameters can be restored before Start;
opaque state needs an active host with the matching codec. The application
owns storage and serialization of returned byte arrays.
For a non-React host, createModulePlayer(module, options) exposes the same
controller. Call dispose() when finished. A rack authored as a NodeGraphPatch
uses the SDK rack host to connect declared module ports; mounting separate
providers or iframe players does not create that rack.
Serve your app over HTTPS or localhost. Cross-origin payloads need CORS and
appropriate content types. An iframe should embed your complete player page,
with its Start action and permissions, rather than the module's bare
ui/index.html.
See Module UI for face authoring and Check, build and export for producing the module payload.