Audio
Use when giving a playable Godot game a coherent, prompt-derived audio identity — event SFX plus a looping music track — generated locally via ComfyUI + Stable Audio Open through tools/comfy.mjs. Derives an audio system from the concept, maps core-loop events to SFX, generates clips, wires AudioStreamPlayer nodes, records audio_pass, and advances status to "scored". Hands off to validator.From its SKILL.md
npx -y skills add qmertesdorf/GameForge --skill audioAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 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.
SKILL.md
12.1 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
Audio re-skin (local generative SFX + music)
Give a playable-or-better game an audio identity. Audio is orthogonal to the visual pass: a game may be playable, styled, or scored going in. The terminal status is scored; the audio_pass block — not the status string — is the source of truth for what was produced.
Failure attribution (the POC value, same as asset): a bad or absent clip is always attributable — a wrong event→SFX map, an unwired AudioStreamPlayer/signal, a clip that ignores concept.theme, or an infra failure in comfy.mjs (ComfyUI down) — never an unattributable gap. Each is a specific, fixable prose/recipe cause.
Inputs
manifests/<id>.jsonwith a populatedconceptblock (andconcept.theme); game status isplayable,styled, orscored.- The game on disk at
games/<id>/. - ComfyUI reachable with Stable Audio Open installed:
node tools/comfy.mjs --check. A failure here is infra, attributable to the stack — stop and ask the owner to start ComfyUI; never fake a clip.
Outputs
games/<id>/audio/*.wav— one committed clip per recipe (event SFX + a looping music track).- Added
AudioStreamPlayernodes (+ their*.wav.importsidecars) wired to the mapped events. - A populated
audio_passblock and terminalstatus = "scored"(after step 6).
Hard requirements
- Game logic is untouched — core loop, existing signals, and
selftest.gd(if present) behave exactly as before. Audio only addsAudioStreamPlayernodes +play()calls at existing signal points; it must not restructure the loop or break the self-test (the validator re-enforces this, Method 4). - Run
--importafter generating clips, before re-validation (step 5b) — without the.wav.importsidecars the validator's headless run can't load the streams. - No new tool or dependency — clips come from
tools/comfy.mjs; wiring is plain GDScript/scene edits. - Do not edit
concept/builder/asset. Consumeconcept.themeas-is (reading it is not editing it).
1. Derive the audio system (do this first, once)
Read concept.theme — the title's modality-neutral world (premise/tone/mood_keywords/setting) — as the primary anchor, with concept.genre for form. The audio identity expresses the same theme the visuals do, sonically. Author a shared audio_system:
model:"stable-audio-open-1.0".mood_prompt: one shared mood sentence threaded into every clip prompt for coherence (the audio analog of the visual prompt scaffold), derived fromconcept.theme'stone+mood_keywords— e.g. for a "cozy autumn-woodland" theme, "warm, organic, gentle woodland atmosphere".style_descriptors: 2–4 tags drawn from / consistent withconcept.theme.mood_keywords(e.g. a "cozy/organic/storybook" theme →["ambient", "soft", "acoustic"]; a "retro/hard-edged/arcade" theme →["chiptune", "8-bit", "upbeat"]).sonic_character: the SFX sound-material/timbre vocabulary derived fromconcept.theme— the audio sibling of the visual world bible, and the thing that keeps SFX on theme instead of defaulting to generic explosive transients. Name the materials and timbre: a cozy/organic theme → name a concrete warm instrument — "kalimba / thumb-piano (warm wooden pluck), soft organic taps, no electronic transients" (a generic "bell chime"/music-box/celesta reads as irritating metallic "tings" — name a specific warm instrument instead); a retro/arcade theme → "crisp 8-bit square/triangle-wave blips, clean chip transients". Recorded verbatim inaudio_pass.audio_system.sonic_character.mood_prompt/style_descriptorssteer the music mood;sonic_charactergoverns the SFX — both are required.
2. Map core-loop events to SFX
From concept.core_loop + the entity/signal set, list the discrete events that deserve a one-shot: typically a positive beat (collect/score), a negative beat (hurt/hit), a loss beat (game over), and the primary action (jump/hop/shoot). For each, record an events[] entry: { event, clip, node, signal } where node is the AudioStreamPlayer you will add and signal is the Godot signal (or call site) that triggers it. Be honest: if an event has no SFX, leave it out and note it.
3. Author recipes
One recipe per SFX + one music recipe. Each prompt = mood_prompt + the clip-specific description. Defaults confirmed at the audio feasibility gate:
- SFX:
kind:"sfx",format:"wav",duration_s1.0–2.0 (EmptyLatentAudioenforces a 1.0 s minimum — do not go below),loop:false,steps50–100 (cozy ≈55, chiptune ≈100),cfg~5–6. steps too low (≈8) is the explosive-SFX root cause — under-denoised broadband noise (a high zero-crossing rate) reads as a gunshot; 50–100 yields a clean tone. Each SFX prompt =mood_prompt+sonic_character+ the clip-specific event description; name the warm instrument (kalimba) for cozy themes, clean square/triangle chip for arcade. The envelope is now a real, deterministic post-process applied automatically bycomfy.mjsgenAudioto everykind:"sfx"clip (envelopeSfxWav: trim-to-event → loudness-normalize to RMS ~0.13 with a 0.97 peak clamp → fade-in 6 ms / fade-out 40 ms) — you do not hand-apply it, but you must still generate at 50–100 steps so the content is clean before the envelope shapes it. The negative prompt always excludes "music, melody, voice, speech", plus a theme-aware exclusion: a cozy/organic theme adds "explosion, harsh, distortion, aggressive, electronic, gunshot"; an arcade theme keeps chip transients but still excludes "explosion, noise burst". (cfg too high can clip the transient.) - Music:
kind:"music",format:"wav",duration_s20–40,steps~50,loop:true,import_settings:{loop:true, loop_offset:0}. Force a plucked/repeating MELODY, never an ambient pad — ambient/pad/sustained prompts collapse to a DRONE ("single-toned, annoying"). Usecfg8 and add aggressive anti-drone negatives:"drone, pad, sustained, monotone, single note, held note, atmosphere, texture". Register matters: a cozy theme wants a low, warm bed — add"high pitched, shrill, tinny, bright"to the negatives and name a warm instrument (fingerpicked nylon-guitar lullaby). A naturally-melodic style (chiptune) needs only a "mellow" framing atcfg~6. Target loop-friendly content (steady repeating melody, no hard intro/outro) — seamless looping is imperfect for generative output (known limitation). Note: WAV music is uncompressed (~5 MB / 30 s stereo); acceptable for a milestone, OGG is a future size optimization. - All recipes use
sampler:"dpmpp_3m_sde_gpu"(schedulerexponentialis baked into the template). - IP-safety: never name artists or copyrighted tracks; negative prompt excludes "voice, speech, lyrics, vocals" for music unless intended. Document this in
notes.
Reference settings (owner-confirmed by ear):
- cozy/organic SFX: kalimba/mbira warm-wooden-pluck; steps ~55, cfg ~6, dur ~1.2 s + auto envelope.
- cozy/organic BGM: fingerpicked nylon-guitar lullaby, low/warm register, simple repeating melody; steps ~50, cfg ~8; anti-drone + anti-high-pitch negatives.
- arcade/retro SFX: clean square-wave chiptune; steps ~100 + auto envelope.
- arcade/retro BGM: mellow chiptune melody, soft square/triangle; steps ~50, cfg ~6.
4. Generate each clip
node tools/comfy.mjs gen-audio <id> <clip-name> '<recipe-json>' → writes games/<id>/audio/<name>.wav. The file is canonical and committed; the recipe is provenance, not bit-exact (GPU/seed nondeterminism). Requires Stable Audio Open installed + the save_audio soundfile-WAV patch (see the feasibility notes — output is WAV regardless of any format field, on the pinned torch 2.11/cu128 stack).
5. Wire into the Godot scene
- Add one
AudioStreamPlayerper SFX (named per theevents[]node) and one for music. - Set import flags: for the music bed,
compress/mode=0(PCM) in its.wav.import— required by the rebuild below (a QOA/ADPCM import returns compressed bytes in.data, which the rebuild would copy as if raw PCM and corrupt). SFX one-shot. - Music — start it so it actually plays. A long IMPORTED
AudioStreamWAV(the ~20–40 s bed) silently refuses to play —play()leavesplaying=false, pos=0even on the real WASAPI driver, with explicitplay()and a valid 30 s stream — while short SFX from the same generator play fine. A freshly constructedAudioStreamWAVbuilt from the same PCM data does play. So rebuild the bed stream in code before wiring it:
Then setvar src := load("res://audio/bgm.wav") as AudioStreamWAV var w := AudioStreamWAV.new() w.format = src.format; w.mix_rate = src.mix_rate; w.stereo = src.stereo w.data = src.data w.loop_mode = AudioStreamWAV.LOOP_FORWARD w.loop_begin = 0 w.loop_end = src.data.size() / (4 if src.stereo else 2) # 16-bit frames p.stream = wautoplay = truebeforeadd_childso it starts on tree entry (deferred/awaitedplay()is a fallback). Autoplay/deferred alone does not fix the silent bed — the imported long stream must be rebuilt. (Confirm with the §7 probe: a wired, autoplaying bed can still be silent if not rebuilt.) A future cleaner fix is OGG/Vorbis for music, which sidesteps the WAV path entirely. - Levels: set the music
volume_dbdeliberately so the bed is audible but sits under the SFX — do not bury it. Setvolume_dbper-game from the bed's ACTUAL loudness, never a flat default. Music is NOT envelope-normalized (only SFX are), so raw bed loudness swings wildly by genre: a fingerpicked acoustic lullaby may come back ~RMS 0.04 (already ~11 dB under the ~RMS 0.13 SFX → it needs a small BOOST, e.g.volume_db +2, or it vanishes), while a dense chiptune bed may come back ~RMS 0.20 (~4 dB above the SFX → it needs a firm CUT, e.g.volume_db -12, to sit under). Measure the bed (or judge by ear at playtest) and pick the sign accordingly — a single flat default is wrong across genres. - SFX:
play()from the mappedsignal/call site, replayable (callplay()each event; for rapid repeats consider a small pool orAudioStreamPlayerper channel). - Keep wiring minimal and reuse the game's existing signal points; do not restructure the core loop.
5b. Import the clips (before re-validation)
Run the headless import pass so Godot makes each .wav.import sidecar + cached stream before the validator's headless run (the audio analog of the asset method's --import gotcha):
godot --headless --path games/<id>/ --import
Commit the generated *.wav.import sidecars alongside the clips (expected Godot output, like .png.import).
6. Record audio_pass and advance status
Merge an audio_pass block (method:"audio", audio_system — including sonic_character — recipes, events, notes) via the manifest tool, then node tools/manifest.mjs set-status <id> scored. State plainly in notes what was produced and anything skipped (mixed honesty). (method:"audio" is a constant provenance tag — the block name already says it's audio — not a branch key; unlike asset_pass.method (svg/raster), no consumer reads it.)
7. Hand off to validator
Run the validator's audio method to confirm files import, players reference valid streams, and SFX fire on events — and that the music bed is actually playing. Confirm MusicAmbient.playing == true with an advancing get_playback_position() a few frames in, not merely that the node exists and is wired (a wired, autoplaying bed can still be silent if the long imported WAV wasn't rebuilt per §5 — caught by this probe). The probe must run on the real audio driver (godot --path ... --script, NOT --headless, whose dummy driver reports playing=false even for a good stream).
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.