SFX Reuse Guide -- Minimizing ElevenLabs API Credit Usage
Every second of ElevenLabs SFX generation costs approximately 40 credits. A 30-second ambience asset costs ~1,200 credits. The SFX/ library already contains 250+ shared assets -- reusing them costs zero credits.
The pipeline's SFX engine (sfx_common.py:ensure_shared_sfx()) follows a 3-tier priority:
- Cached -- shared asset already exists on disk in
SFX/--> return immediately (0 credits) - Source --
sourcefield in sfx config points to a file --> copy it (0 credits) - API -- no cache, no source --> call ElevenLabs API (~40 credits/second)
| Method | Credits | When to use |
|---|---|---|
| Cached (already generated) | 0 | Same effect reused across episodes |
source field (existing file) |
0 | Asset exists in library under a different name |
| API generation | ~40/second | Truly new sound needed |
Discovering Available Assets
xil-sfx-lib (recommended)
xil-sfx-lib # list all assets with duration and size
xil-sfx-lib --search "diner" # filter by keyword
xil-sfx-lib --search "coffee" # find coffee-related effects
xil-sfx-lib --json # machine-readable output
xil-sfx-lib -v # verbose: all metadata fields
Filesystem scan
Naming conventions
The library contains two naming styles:
- Cue-sheet asset IDs:
sfx-boots-stamp-01.mp3,amb-quarry-winter-01.mp3 - Descriptive filenames:
FOLYProp-Pouring_hot_liquid_i-Elevenlabs.mp3,BELLDoor-Bright_entrance_door-Elevenlabs.mp3
Both work identically with the source field.
Using source in sfx Config JSON
Add a source field to any entry in sfx_<slug>_<TAG>.json to skip API generation entirely. The file is copied to the episode stem directory at generation time.
Examples from production
{
"effects": {
"INTRO MUSIC": {
"source": "SFX/The Porch Light.mp3",
"volume_percentage": 40,
"play_duration": 10
},
"SFX: COFFEE BEING POURED INTO CERAMIC MUG": {
"prompt": "Coffee being poured into ceramic mug",
"duration_seconds": 2.0,
"source": "SFX/FOLYProp-Pouring_hot_liquid_i-Elevenlabs.mp3"
},
"SFX: DINER DOOR OPENS, BELL CHIMES": {
"prompt": "Classic diner door opening with small bell chime",
"duration_seconds": 5.0,
"source": "SFX/BELLDoor-Bright_entrance_door-Elevenlabs.mp3"
},
"AMBIENCE: RADIO BOOTH - SOFT EQUIPMENT HUM, SLIGHT STATIC, INTIMATE": {
"source": "SFX/Invitation to Action.mp3",
"prompt": "Radio booth ambience, soft equipment hum, slight static",
"duration_seconds": 30.0,
"loop": false
}
}
}
Rules
- Path: always relative to project root (starts with
SFX/) prompt: keep it for documentation even whensourceis set -- it has no effect on generation- Mixing fields:
volume_percentage,loop,play_duration,ramp_in_seconds,ramp_out_secondsall work the same withsourceas with API-generated assets - Duration:
duration_secondsis used for mixing calculations; it is not validated against the actual file length whensourceis set
Writing Cues Sheets with REUSE Markers
In the cues markdown file, mark assets that already exist in SFX/ as (REUSE) so xil-cues --generate skips them.
Heading format (MUSIC CUES and AMBIENCE sections)
### **MUS-THEME-MAIN-01 (REUSE)**
**Prompt:** Eerie indie folk theme, acoustic guitar **Duration:** 60 seconds **Used:** Cold open
### **MUS-STING-NEW-01 (NEW)**
**Prompt:** Brief hopeful musical release **Duration:** 5 seconds **Used:** Scene 1
Table format (SOUND EFFECTS section)
| Asset Name | Prompt | Placement |
| ----- | ----- | ----- |
| SFX-DOOR-BELL-01 (REUSE) | Diner door opening with bell chime | Karen's entrance |
| SFX-BOOTS-STAMP-01 (NEW) | Snow being stamped off boots on doormat | Karen entering |
Best practice: run xil-sfx-lib --search "keyword" before writing the cues sheet to check what already exists in the library.
Script Directions and sfx Config Keys
The parser (XILP001) extracts direction text verbatim as the text field in parsed JSON. This text becomes the key in the sfx config effects dict:
Script direction: [SFX: DINER DOOR OPENS, BELL CHIMES]
|
Parsed JSON text: "SFX: DINER DOOR OPENS, BELL CHIMES"
|
sfx config key: "SFX: DINER DOOR OPENS, BELL CHIMES": { "source": "SFX/..." }
When writing a new episode script, reuse the exact same direction text from previous episodes to match existing sfx config entries. This lets you copy source fields across episode configs without modification.
New Episode Workflow Checklist
-
Audit the library
Check for each sound effect you plan to use. -
Write the cues sheet -- mark every asset that exists in
SFX/as(REUSE). -
Dry-run the cues ingester
Review the audit report. Check credit estimate forNEWassets. -
Write the production script -- use direction text matching existing sfx config keys where possible.
-
Parse the script
This auto-generates a skeleton sfx config if one doesn't exist. -
Add
sourcefields -- for each effect that exists inSFX/, add a"source": "SFX/filename.mp3"entry to the sfx config JSON. -
Dry-run SFX generation
Verify reused assets showCACHED(0 credits) notNEW. -
Generate only what is truly new
Quick Reference
| Goal | Command / Action |
|---|---|
| List all SFX assets | xil-sfx-lib |
| Search for a sound | xil-sfx-lib --search "keyword" |
| Machine-readable asset list | xil-sfx-lib --json |
| Skip API for an effect | Add "source": "SFX/filename.mp3" to sfx config |
| Mark cue as reuse | Append (REUSE) to asset ID in cues sheet |
| Preview SFX credit cost | xil-sfx --episode TAG --dry-run |
| Preview cues credit cost | xil-cues --episode TAG --dry-run |
| Enrich sfx config from cues | xil-cues --episode TAG --enrich-sfx-config |
eleven_v3 Audio Events vs. SFX Stems
Certain sounds that previously required a [SFX: ...] direction and a generated
stem can now be handled inline by the TTS model using v3 audio tags embedded in
dialogue text:
| Old approach (SFX stem, costs credits) | v3 inline tag (free, in voice stem) |
|---|---|
[SFX: ADAM SIGHS HEAVILY] |
[sighs] embedded in Adam's dialogue text |
[SFX: NERVOUS LAUGH] |
[chuckles] or [laughs] in dialogue |
[SFX: SHARP INTAKE OF BREATH] |
[gasps] in dialogue |
When to still use SFX stems: for sounds between speakers, overlapping with ambience, or effects that need precise mix placement (volume, timing relative to other tracks). Inline tags render inside the single voice stem and cannot be mixed independently in the DAW layers.
See claude-scriptwriter-reference.md for the full v3 tag vocabulary.