@milkrust/web exposes a lightweight plugin surface in the browser SDK so hosts can add behavior without forking the engine.
loadMilkRustPackPlugins(manifest, { fetchImpl }) loads plugin entries from a normalized
pack manifest. It returns:
valid: whether every plugin loaded successfullyerrors: hard errorswarnings: non-blocking issuesplugins: loaded plugin descriptors
Each plugin descriptor can come from:
- data plugin (
kind: "data"): JSON payload loaded fromplugin.url - js plugin (
kind: "js"): module loaded with dynamicimport()
kind is normalized to accept js, javascript, and module.
Data plugins can carry config, deck wiring metadata, presets, and heuristics.
If a JS module load fails, the pack load returns valid: false with an errors entry and skips
that plugin while continuing to process other entries.
Install plugins through engine.loadPlugins(plugins) where plugins is the loaded array.
Supported hooks:
onPresetLoad(context)onPresetLoaded(context)onPresetChange(context)onFrameStart(context)onAudioFrame(context)onBeat(context)onAutomationStep(context)onRenderFrame(context)onInput(context)onExport(context)
Hooks receive a mutable context object and may return a plain object to merge additional context.
{
"kind": "playlist",
"presetIds": ["warm-scope", "blue-shape"],
"shuffle": false
}const packed = await loadMilkRustPack('examples/sample-pack/');
const plugins = await loadMilkRustPackPlugins(packed);
engine.loadPlugins(plugins.plugins);export const onFrameStart = ({ automation, presetName }) => {
if (automation?.mode === 'beat') {
// plugin logic ...
}
return { presetName };
};export default { onFrameStart };- Hooks are best-effort and run with error isolation (exceptions are logged and ignored).
- Dynamic js plugins are loaded from user-accessible URLs and are not sandboxed.
- Plugin execution does not alter the Rust runtime contract; it can only mutate SDK-facing context and local orchestration decisions in the web layer.