From 7434b03a57783f4938b19227ad4673a7854f2073 Mon Sep 17 00:00:00 2001 From: monkey-w1n5t0n Date: Sun, 28 Jun 2026 04:22:53 +0200 Subject: [PATCH] feat(manifold): add dedicated ParticleBackend (no-op transport, gates audio) Registers a named ParticleBackend in the BackendManager + barrel in place of the inline particles PassthroughBackend. Transport is a no-op (the flow-field canvas reads engine outputs directly via rAF); selecting it gates synth audio (setMuted) like the other non-synth backends and reports a clear ready status. --- manifold/src/backends/index.ts | 1 + manifold/src/backends/manager.ts | 3 +- manifold/src/backends/particle-backend.ts | 35 +++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 manifold/src/backends/particle-backend.ts diff --git a/manifold/src/backends/index.ts b/manifold/src/backends/index.ts index a0bea84..d7e30a4 100644 --- a/manifold/src/backends/index.ts +++ b/manifold/src/backends/index.ts @@ -18,6 +18,7 @@ export { OscBridgeBackend } from './osc-backend'; export type { OscBackendConfig } from './osc-backend'; export { NispsOscClient } from './osc-client'; export { PassthroughBackend } from './passthrough-backend'; +export { ParticleBackend } from './particle-backend'; export { useBackendManager, } from './useBackendManager'; diff --git a/manifold/src/backends/manager.ts b/manifold/src/backends/manager.ts index 4afe0c2..38cdb24 100644 --- a/manifold/src/backends/manager.ts +++ b/manifold/src/backends/manager.ts @@ -21,6 +21,7 @@ import type { BackendId } from '../dock/output-state'; import { WebMidiBackend } from './midi-backend'; import { OscBridgeBackend } from './osc-backend'; import { PassthroughBackend } from './passthrough-backend'; +import { ParticleBackend } from './particle-backend'; /** The slice of EngineApi the manager depends on (keeps it decoupled/testable). */ export interface ManagerEngine { @@ -47,7 +48,7 @@ export class BackendManager { ['midi', backends?.midi ?? new WebMidiBackend()], ['osc', backends?.osc ?? new OscBridgeBackend()], ['synth', backends?.synth ?? new PassthroughBackend('synth', 'Built-in Synth — audio plays in the engine')], - ['particles', backends?.particles ?? new PassthroughBackend('particles', 'Particle visualiser')], + ['particles', backends?.particles ?? new ParticleBackend()], ['cvgate', backends?.cvgate ?? new PassthroughBackend('cvgate', 'CV / gate (via VCV bridge)')], ['vcv', backends?.vcv ?? new PassthroughBackend('vcv', 'VCV bridge')], ]); diff --git a/manifold/src/backends/particle-backend.ts b/manifold/src/backends/particle-backend.ts new file mode 100644 index 0000000..fee1ef0 --- /dev/null +++ b/manifold/src/backends/particle-backend.ts @@ -0,0 +1,35 @@ +/** + * ParticleBackend — the output backend for the Particle System mode (the DEFAULT + * mode). + * + * Transport is a NO-OP: the flow-field visualiser (console/flow-field.ts, driven + * by ParticleStage) is a SEPARATE consumer of the engine spine — it reads + * `engine.getOutputs()` directly in its own requestAnimationFrame loop and maps + * the first 20 outputs onto the visual field. So there is nothing for the + * BackendManager to "send" here. + * + * What this backend DOES contribute: + * - It is a non-synth backend, so selecting it makes the BackendManager gate + * audio (`engine.audio.setMuted(true)`) — the synth is silenced while the + * particles are on screen, exactly like the MIDI / OSC modes. + * - It reports a sensible "ready" status for the Outputs panel. + * + * It extends PassthroughBackend (the shared no-op sink) purely to keep the + * empty-`send` / nothing-to-start behaviour in one place; the audio gate lives + * in the manager (`setActive`), not here. + */ +import { PassthroughBackend } from './passthrough-backend'; +import type { BackendStatus } from './backend'; + +export class ParticleBackend extends PassthroughBackend { + constructor() { + super('particles', 'Particle visualiser — outputs drive the flow field (no audio)'); + } + + override status(): BackendStatus { + return { + state: 'ready', + message: 'Particle visualiser — outputs drive the flow field (no audio)', + }; + } +}