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
| Generator | Output dir | What it is | Typical length |
|---|---|---|---|
renderSweeper | media/sweepers (SWP_INTRO_*, SWP_VOX_*, SWP_DROP_*) | Voice tag over a real music bed with sidechain ducking | ~8.5–9 s |
renderVoiceDrop | media/sweepers (SWP_DROP_*) | Voice tag over a pink-noise whoosh | ~3.5–4 s |
renderStationId | media/sweepers (ID_*) | Station IDs | ~4–5 s |
renderStinger | media/sfx (SFX_<DESIGN>_*) | 16 layered sound-design stingers | 0.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=0 → stereotools=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.jsrefreshLibrary: 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.jsendpoints (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
- Fun jingles — karaoke jingles + sound-design FX module.
- Content pipeline — where imaging is regenerated each refresh.
- Smart scheduler — how imaging decks are scheduled.