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.
This commit is contained in:
monkey-w1n5t0n 2026-06-28 04:22:53 +02:00
parent 711415e9ff
commit 7434b03a57
3 changed files with 38 additions and 1 deletions

View file

@ -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';

View file

@ -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')],
]);

View file

@ -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)',
};
}
}