Avatar ambient sfx
Cloud-based agent skills for creating AI avatar talking-head videos and short-form reels (skills.sh format)
npx -y skills add puntorigen/avatar-skills --skill avatar-ambient-sfxAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 27 days oldThe repository was created 27 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Add a spatial ambient SFX layer to a finished avatar reel and deliver it as final-espacial.mp4, without overwriting final.mp4. Generates positioned ambient/foley beds + one-shots (birds, water, gulls, fire crackle, crickets, wind, etc.) with audio-theater (ElevenLabs Sound Effects), mixes them on a virtual stereo stage (pan + distance + movement) with mix_spatial.py, and overlays them ducked under the narration. Use when the user wants to "ambientar más" / add ambient or positional/spatial sound to a reel, or asks for sound design / atmosphere that follows different worlds/locations in a video. Builds on the audio-theater skill.
SKILL.md
12.4 KB, as published. Nobody here has run it
Avatar Ambient SFX (spatial sound design for reels)
Layer a spatial ambient sound bed over a finished reel so each "world"/location
(garden, water, fire, plaza…) gets its own positioned atmosphere, and deliver it as
final-espacial.mp4. The narration always stays on top; final.mp4 is never touched.
This skill is a thin orchestration on top of audio-theater (which provides the SFX
generator and the spatial mixer) plus one overlay script in this skill.
When to use
The reel is already finished (final.mp4 with narration + music + captions + fade), and the
user wants to "ambientarlo más" / add ambient, positional, or spatial sound that follows the
different worlds in the video. Best when the reel is a journey across distinct locations.
Prerequisites
- The finished reel folder must contain:
final.mp4,narration.mp3(narration-only track, the sidechain key), and ideallynarration.align.json(for the fade timing). audio-theaterscripts available at~/.cursor/skills/audio-theater/scriptsand an ElevenLabs key set (realistic foley). Verify:python3 ~/.cursor/skills/audio-theater/scripts/setup_key.py --show.ffmpeg/ffprobeon PATH.
Workflow
SFX=~/.cursor/skills/audio-theater/scripts
THIS=<this-skill>/scripts # overlay_ambient.py lives here
REEL=antiguo/reels/NNN_slug
AMB=$REEL/ambient
-
Map the worlds to the timeline. Read
reel_manifest.json(scenestart/end) and group scenes by location into time windows, e.g. garden0–11.9s, water11.9–32.3s, fire32.3–end. Note where the visible source sits in frame (left/right) — pan follows it. -
Author
$AMB/cues.json(you write it) + an empty$AMB/lines.json:{"duration": <reel_seconds>, "lines": []}One
ambientbed per world (with a ~0.5s pre-lap/overlap into the next for a soft cross), plusoneshotaccents (a couple of bird chirps, gulls flying across, etc.). Give every cue aspatialblock. Describe CONCRETE, discrete sounds (waves, birds, a clock tick, a candle pop, a page turn) — NOT abstract textures ("room tone", "airy shimmer", "hush", "starlight"), which ElevenLabs renders as broadband NOISE (see the "noise wash" lesson below). For intimate indoor scenes, prefer a few diegetic one-shots in the speech gaps over a continuous bed. See REFERENCE.md for the cue schema, the Cap.6 worked example, and the positioning/level rules. (cues.json is anaudio-theaterfile — full schema in~/.cursor/skills/audio-theater/SKILL.md.) -
Generate the SFX (ElevenLabs, realistic foley):
python3 $SFX/generate_sfx.py --cues $AMB/cues.json --out $AMB --backend elevenlabsFiles land in
$AMB/sfx/; durations are written back intocues.json. -
Spatial mix →
ambient_spatial.mp3(no dialogue/music in this project, so no ducking here — ducking under narration happens in the overlay step):python3 $SFX/mix_spatial.py --out $AMB --output-name ambient_spatial.mp3 --no-duck # intimate / quiet indoor beds: add --target-i -28 (default -16 over-amplifies near-noise beds)⚠
mix_spatialloudnorms the whole bed to--target-i(default -16 LUFS, sized for loud outdoor foley). For quiet intimate ambiences pass--target-i -28..-30so it doesn't boost near-silent beds +30 dB and surface their hiss (see the "noise wash" lesson below). -
Overlay onto the reel, ducked under narration →
final-espacial.mp4:python3 $THIS/overlay_ambient.py --reel-dir $REELAuto-discovers
final.mp4,ambient/ambient_spatial.mp3,narration.mp3,narration.align.json. Copies the video, mixes the ambient ducked under the narration (sidechaincompress), loudnorm's to MATCHfinal.mp4's own loudness (so the only audible change is the bed — our masters sit ~-21..-24 LUFS, not -16), and fades the audio out to match the fade-to-black. Tune presence with--ambient-gain-db(default-4; lower = subtler); force a fixed target with--loudnorm I=-16:TP=-1.5:LRA=11only if you really want it.- Bed cleanup is ON by default (
highpass 35Hz + lowpass 12kHz + afftdn nr=10): tames the ElevenLabs broadband hiss that quiet/abstract beds carry (these ambiences have nothing above ~12kHz). Disable for rich loud outdoor foley with--no-clean; tune with--clean-lowpass/--clean-nr. The overlay also prints a[bed-QA]warning if the bed looks like a broadband-noise wash (>8kHz within ~3dB of full) — heed it and re-author the content.
- Bed cleanup is ON by default (
-
QA (objective, not just by ear): confirm the panning and that narration still dominates.
# Per-channel RMS in a window (left vs right) — pan should match the frame: ffmpeg -loglevel info -i $AMB/ambient_spatial.mp3 -af "atrim=START:END,astats" -f null - 2>&1 | grep -iE "Channel|RMS level" # Integrated loudness should barely move vs final.mp4 (ambient is subordinate): ffmpeg -i $REEL/final-espacial.mp4 -af loudnorm=print_format=summary -f null - 2>&1 | grep -i "Input Integrated"⚠
astatsneeds-loglevel info(it's silenced by-loglevel error).
Positioning rules (pan follows the frame)
pan∈ [-1,+1] (L..R, 0=center),distance∈ [0,1] (0=close..1=far).- Position SFX where the source is on screen. If the fire is framed on the left, crackle pans left; put crickets/wind right. Water/waves usually center & close.
- Ambient beds stay stereo & fixed — they only honor a gentle
{pan}balance and{distance}. One-shots move — give a flying gull apath(e.g. L→R) so it crosses the stage. - Use soft overlaps (~0.5s) between adjacent world beds with
fade_in/fade_outso worlds cross-fade instead of cutting.
Level & naturalness lessons (hard-won)
- ⚠ THE "NOISE WASH" TRAP — concrete foley vs abstract textures (Cap.8/9/10 regression). Caps 6/7
(008/009) sounded great because their worlds are loud, concrete OUTDOOR foley (sea waves, birds,
fire crackle, gulls) — structured sounds ElevenLabs renders cleanly. Caps 8/9/10 (010/011/012) moved
to intimate/abstract INDOOR ambiences ("candlelit room tone", "airy shimmer", "hush", "glassy
starlight tone") → ElevenLabs renders those as very quiet, low-level BROADBAND NOISE (−41..−61 dB,
no structure). The pipeline then amplifies the hiss:
mix_spatialloudnorms the bed toward −16 LUFS (built for loud foley) → +25..+45 dB on near-noise beds, and any hot per-cuegain_db(e.g. +10) compounds it. Result = a uniform broadband hiss wash = the "ruido raro / molesto". Verified by spectrogram: 008 shows distinct events + a dark high-freq floor; 012 is a solid red wash from DC to the 16.5kHz MP3 brick-wall. Post-processing (denoise/lowpass/lower level) only makes it quieter, not pleasant — the content IS noise. Fixes, in order of importance:- Author CONCRETE, discrete diegetic sounds, not abstract textures. A candlelit study reads as a
few clock ticks, occasional candle/wax pops, a page turn, a wooden desk creak, a
single distant wind gust — placed as
oneshots in the speech gaps. Avoid continuous "room tone / shimmer / hush / starlight" beds (those = noise). If a world has no honest continuous sound, use only one-shots and no bed. - QA every bed's spectrogram (
ffmpeg -i sfx/X.mp3 -lavfi showspectrumpic=s=900x320:legend=1 X.png): it must show structured events + a dark floor above the content. A uniform fill up to 16kHz = reject/regenerate. The overlay's[bed-QA]line is a cheap heuristic for the same thing. - Don't over-amplify. For intimate beds, mix with a lower target (
mix_spatial.py --target-i -28..-30) so quiet beds aren't boosted +30 dB, and keep--ambient-gain-dblow (−6..−9). The overlay's default lowpass 12kHz + light denoise removes the brick-wall hiss; it is a safety net, not a substitute for concrete content.
- Author CONCRETE, discrete diegetic sounds, not abstract textures. A candlelit study reads as a
few clock ticks, occasional candle/wax pops, a page turn, a wooden desk creak, a
single distant wind gust — placed as
- Narration ALWAYS wins. Key the duck off
narration.mp3(not the full mix) so the bed dips only under the voice and stays present in the gaps. After overlay, the integrated LUFS should move only ~+0.2–1 LU vsfinal.mp4. - Match the source loudness — don't force -16. The series masters sit ~-21..-24 LUFS (Cap.7
final.mp4= -24.4). The overlay used to hard-codeloudnorm I=-16, which boosted the WHOLE mix (voice + music + ambient) ~+8 dB and madefinal-espacialsound much louder thanfinal.mp4— a confounded A/B and a false "loudness jumped" QA reading. Fix (now the default): the overlay measuresfinal.mp4's integrated loudness and matches it, so the bed is the only change (Cap.7: -24.4 → -23.9..-24.3 LUFS, +0.5 LU). TikTok normalizes playback anyway. - Balance
gain_dbper world by measuring RMS, not by guessing.mix_spatial.pyruns a globalloudnorm, so one hot source (e.g. fire crackle peaking at -1.7 dBFS) will crush the rest. Measure each world's RMS withastatsand raise quiet worlds until each is audible. - Validate the SFX source. ElevenLabs sometimes renders a bed too distant/quiet — the first
garden-birds render came back at RMS -75 dB (inaudible). Regenerate with a closer/denser
prompt and/or add 1–2
oneshotaccents. - NEVER prompt beds as "almost silent" / "very faint". ElevenLabs takes it literally and renders
the bed at -73..-83 dB (dead). For an intimate room (candlelit study), describe present,
up-close detail ("up-close candle flames fluttering with a faint wax crackle, a warm room tone,
a distant tiny clock tick") — let
gain_db/distanceset the level, the prompt sets the content. Cap.9: "almost silent" beds → inaudible; rewritten present → -38..-58 dB. - Continuous narration crushes the bed (the "I hear no ambient" trap). If the reel is wall-to-wall
voice (check
narration.align.jsonfor gaps), the overlay's sidechain duck suppresses the WHOLE layer almost the entire time. Two fixes, used together: (1) soften the duck so the bed breathes under the voice —--duck "threshold=0.06:ratio=2:attack=15:release=400"(the-4/defaultratio=3is too aggressive for continuous VO); (2) place diegetic one-shot accents IN the speech gaps — Cap.9 had a single 0.64s gap (18.84–19.48s), so the coin-clink (the action beat's sound) was moved tostart:18.95and rang out +8 dB in-clear instead of being masked at full speech. - QA the bed by A/B window, not just integrated LUFS. Measure the same windows in
final.mp4vsfinal-espacial.mp4(-ss W -t D -af volumedetect): a real gap should jump several dB; speech windows should barely move (voice still wins). Integrated LUFS hides an inaudible bed. - Transients lie. A crackle has low RMS despite high peaks — don't chase its RMS; set it by pan + level by ear.
- Naturalness > density. A dense/continuous bed sounds fake (e.g. nonstop bird trills).
Prefer a spaced/varied description ("a few different small birds… with natural gentle pauses"),
low
prompt_influence(~0.3), and a generousfade_in(~2.5s) so it doesn't slam in. - Don't overwrite
final.mp4— always deliverfinal-espacial.mp4(the spatial layer is an optional alternate, not the canonical master).
Output
$AMB/cues.json,$AMB/sfx/*.mp3,$AMB/ambient_spatial.mp3(the spatial layer).$REEL/final-espacial.mp4(video copied fromfinal.mp4, audio = original + ambient bed).
Additional resources
- Cue schema, the full Cap.6 worked example, and detailed positioning/level recipes: REFERENCE.md
- SFX/mix internals (cues.json fields, mix_spatial flags):
~/.cursor/skills/audio-theater/SKILL.md