Skip to main content

Imaging Engine (imaging-engine.js)

Since 2026-08-10 the station's sweepers, voice drops, IDs and stingers are generated by a dedicated pro-radio imaging module. imaging-engine.js is self-contained (it only requires paths.js) and replaces the old voice-only sweepers and single-source SFX designs with proper broadcast imaging: de-essed, ducked, beat-aligned voice over real music beds, plus a 16-design layered stinger library — every file two-pass R128 mastered to −14 LUFS / −1.5 dBTP so it sits at the same loudness as the music.

What it generates

GeneratorOutput dirWhat it isTypical length
renderSweepermedia/sweepers (SWP_INTRO_*, SWP_VOX_*, SWP_DROP_*)Voice tag over a real music bed with sidechain ducking~8.5–9 s
renderVoiceDropmedia/sweepers (SWP_DROP_*)Voice tag over a pink-noise whoosh~3.5–4 s
renderStationIdmedia/sweepers (ID_*)Station IDs~4–5 s
renderStingermedia/sfx (SFX_<DESIGN>_*)16 layered sound-design stingers0.26–1.63 s

All outputs are Break-tagged (artist/title/genre = Break) so on-air metadata stays clean.

Sweeper recipe (renderSweeper)

The apad on the voice side is critical: sidechaincompress truncates to its shorter input, so without it the bed tail was cut to ~3 s and the sweeper lost its "swell back" ending.

Voice-drop recipe (renderVoiceDrop)

Same voice chain over a anoisesrc=pink whoosh, ~3.5–4 s.

Stinger designs (STINGER_DESIGNS, 16)

RISER, RISER_DOWN, WHOOSH_UP, WHOOSH_DOWN, ZAP, BLIP_UP, BLIP_DOWN, GLITCH, DROP, METALLIC, SWEEP, SHIMMER, PULSE, LASER, SUBDROP, IMPACT.

Each is a multi-layer lavfi build (2–3 sources: aevalsrc tones + pink/white noise) → amix=normalize=0stereotools=mlev=1:slev=1.4 (wide) → alimiter → two-pass R128.

Beat-aligned placement

Song-specific sweepers slice the actual song via detectBpmAndOnsets + beatAlignedStart — the voice lands on a real musical pulse (intro lead-in ~1.5 s in, energy drop ~45% into the track).

Wiring & endpoints

  • Wired into content-pipeline.js refreshLibrary: the sweepers block archives old imaging (imaging.archiveOldImaging(['SWP_','ID_','SFX_'])pro_archive) then runs the generators, so decks never bloat (sweepers went 1125 → 25, sfx 31 → 35 with 16 new stingers).
  • server.js endpoints (each resyncs decks + pushes the rotation): POST /api/imaging/generate, POST /api/imaging/sweepers, POST /api/imaging/stingers.
  • MCP tools: generate_sweepers, generate_stingers.
  • CLI: node scripts/run-imaging.js [--sweepers N] [--ids N] [--vox N] [--stingers N] [--no-archive].

See also