mcpField
P
procedural-sfx

procedural-sfx

github.com/bianbianzhuCreative & media182 installsUpdated 7w ago
Repository

About procedural-sfx

Procedural SFX is a skill for creating sound effects for animation, explainers, UI, motion graphics, and game prototypes. It synthesizes sounds, mixes them by event timing, and provides analysis, mastering, and video muxing workflows; recorded audio can also be included.

Key features

  1. 01Synthesize effects from noise, waves, and filters
  2. 02Use built-in recipes or create custom ones
  3. 03Place and mix sounds from an event list
  4. 04Analyze audio metrics and masking
  5. 05Master audio and mux it into video

Use cases

  1. 01Add synchronized effects to animation
  2. 02Create UI and motion-graphics sounds
  3. 03Generate repeatable audio for game prototypes
  4. 04Mix synthesized effects with recorded audio

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bianbianzhu/procedural-sfx --skill procedural-sfx

After installing, type/procedural-sfxin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

LICENSE
agents/
openai.yaml
assets/
events.example.json
recipe_template.py
references/
design-method.md
event-sync.md
mixing.md
recipes.md
troubleshooting.md
scripts/
analyze.py
check_env.py
master.sh
mix.py
mux.sh
recipes.py
render_sfx.py
requirements.txt
sfxkit.py

Details

From the project README

Procedural SFX

Make every sound effect from noise, sine waves, filters and envelopes, then place each one on the exact frame where it happens. You get sounds with no third-party sample licences that render identically on every run and stay in sync with the picture automatically.

This works best for stylised work: animation, explainers, UI, motion graphics, game prototypes. For photoreal film foley (a specific real gun, real footsteps on real gravel) a recorded sample is still better. The mixer accepts audio files too ("type": "file"), so hybrids are fine.

Setup

pip install -r scripts/requirements.txt   # numpy, scipy, soundfile
python scripts/check_env.py               # verifies them; ffmpeg >= 4.2 is optional (only for master.sh / mux.sh)

All scripts live in scripts/ and run from anywhere. Paths below are relative to this skill's directory. On a mistake they exit 1 with error: … and a fix: … line; act on the fix line and rerun.

Workflow

  1. Spot the sounds. List every moment that needs a sound: time, what happens, what material is involved (plastic, wood, metal, air, electricity). Material decides the recipe, not the action's name: "a coin landing" and "a key dropping" are both small metal impacts. Plan the silences as well. A beat of quiet before a big hit makes it land harder than extra volume does.

  2. Pick or design each sound. Run python scripts/render_sfx.py --list to see the built-in recipes. If one fits, render it with arguments and audition it. If none fits, write a new recipe in a project file based on assets/recipe_template.py.

    • Read references/design-method.md before writing a new recipe. It explains the attack/body/tail layering and which number changes what you hear.
    • Read references/recipes.md for what each built-in does, its parameters, and variations such as gun types, surfaces and distances.
  3. Check each sound by numbers. You cannot hear the output, so measure it: python scripts/analyze.py sound.wav --bands. Compare attack, ring time, spectral centroid and band balance against what the sound should be. references/design-method.md has rough targets. For sounds that repeat, render_sfx.py <recipe> --variants 4 prints metrics per take and flags takes that are identical or barely differ.

  4. Write the event list. events.json is one entry per sound with time, type, args, gain and pan. assets/events.example.json is a runnable example; voice lines and recorded audio go in as "type": "file" events. Take the times from the same constants that drive the animation rather than retyping them. See references/event-sync.md for the schema, exporting from different animation stacks, and anticipation offsets such as a whoosh that starts before its impact.

  5. Mix. Run python scripts/mix.py events.json -o mix.wav [--recipes my_recipes.py] [--music score.wav] [--bed rumble] [--stems stems/] [--audition audition/]. It validates the whole events file first, then prints levels per bus, a masking report that flags events buried under louder sounds, and the true peak before and after its limiter (ceiling −1 dBTP). Investigate every CHECK line: fix it, or accept it knowingly and say why. See references/mixing.md for gain staging, ducking and fixes for masking.

  6. Master for delivery. Both scripts measure the mix, apply one linear gain to reach the loudness target (−14 LUFS for web/social; others in references/mixing.md §5) and print the re-measured result. They never compress; if the target would push the true peak over the limit they stop and name the loudest reachable target.

    • With a video: sh scripts/mux.sh video.mp4 mix.wav out.mp4 [LUFS=-14] [dBTP=-1]. The mix replaces the video's own audio. If the video already has music or dialogue that must stay, extract it (ffmpeg -i video.mp4 -vn -c:a pcm_s24le original.wav) and pass it to mix.py --music original.wav before muxing; mux.sh warns when the video has audio. Every video frame is kept, and the mix is fitted to the video's length before it is mastered (set "dur" to the video length and nothing needs fitting). Output .mp4, .m4v, .mov or .mkv.
    • Audio only: sh scripts/master.sh mix.wav final.wav [LUFS=-14] [dBTP=-1].
  7. Hand off for listening. Numbers catch broken sounds but not ugly ones. The mix report ends with a "not ear-tuned yet" table: every placed sound whose recipe status is starting point or new, one row per recipe and argument set. Run mix.py with --audition DIR and it renders each of those on its own (same args and take as in the mix) and fills in the file column; give events a "name" ("angry cat") and it fills in the sound column. Paste that table into the hand-off as it is: sound · recipe · args · status · times · file to audition. Ask the user to listen to every row that isn't tuned. If something is off, references/troubleshooting.md maps complaints like "too thin", "clicks at the end" or "sounds robotic" to fixes, and has the final QA checklist.

Scripts

ScriptPurpose
scripts/sfxkit.pyLibrary: noise, envelopes, filters, sweeps, saturation, echo, radio FX, true-peak meter and limiter, add() for placing sounds, wav I/O
scripts/recipes.py18 built-in recipes + load() that merges your project recipe files
scripts/render_sfx.pyRender one recipe, variants, or all recipes to wav for audition
scripts/analyze.pyMetrics per file: peak, RMS, attack, ring time, spectral centroid, band energy
scripts/mix.pyevents.json → stereo mix with buses, ducking, limiter, stems, masking report
scripts/master.shAudio-only delivery: measure, one linear gain to the LUFS target, true-peak check (ffmpeg)
scripts/mux.shmaster.sh, then mux under a video as AAC (ffmpeg)
scripts/check_env.pyCheck dependencies

Built-in recipes

Impacts: click, clack, crash, thump, step (hard/wood/soft), creak · Motion: whoosh · UI: ding, pop, beep · Weapons & sci-fi: gunshot (pistol/rifle/shotgun), burst, explosion, laser · Ambience & drama: rumble, ignite, roar, heartbeat. Each has a status, shown by render_sfx.py --list: tuned (ear-tuned in finished work) or starting point (measured, never ear-tuned: gunshot, burst, explosion, laser, and step on wood/soft). Recipes from your own --recipes files are new. Details in references/recipes.md.

Principles

  • One recipe = one normalised sound; gain lives in the event list. Recipes end with norm(x) * v, and loudness is set only by gain in events.json. Mixing is then a matter of editing one table.
  • Randomness only through noise(), rand(), uniform(). mix.py seeds each event from its type, time and args, so adding an event never changes how other events sound, and every render is byte-identical.
  • Vary repeated sounds. Identical repeats (footsteps, gunfire, typing) sound fake. Randomise pitch by ±5–10%, level by ±20% and pan slightly.
  • Timing comes from the animation, not from your ears. When sync is off, the fix is almost always in how the event times were produced, not a nudge in the mix.

Comments

Markdown supported