SFX play frequency + overhaul batches (2026-10-12)
SFX play frequency + overhaul batches (2026-10-12)
Section titled “SFX play frequency + overhaul batches (2026-10-12)”Task 5 of the SFX organic overhaul (spec docs/superpowers/specs/2026-10-12-sfx-organic-overhaul-design.md).
Which of the 52 sound keys (audio/sfx_tiers.json) the player actually hears most, measured in
headless skilled-autopilot survival runs through the real AudioManager routing: per-frame
cap, retrigger cooldowns, camera gating of enemy cues, off-screen death handling. The order of the
replacement batches comes from these counts.
Tool: tools/sfx_freq/sfx_freq.gd (dev only).
godot --headless --path . -s res://tools/sfx_freq/sfx_freq.gd -- --runs=4 --ticks=36000# options: --invuln, --force_boss=warden|funzo|graviton|eye, --area=<id>, --seed0=<n> (default 2000)How it counts: SoakRun’s on_tick observer feeds each tick’s sim.fx_events to a
CountingAudio subclass of AudioManager (a 1280x720 view centred on the player), and counts
every play() / UI play. It also mirrors the one-shots main.gd fires straight from sim state
(warp = dash_cd edge, hurt = player_hard_hits change, one level_up per level gained, defeat
on game over), so warp_dash, king_hit, level_up and defeat are counted at their real rate.
orbit_ambience is a loop, not a one-shot, so it is reported as the share of ticks it sounds.
The observer draws no RNG and does not mutate the sim (pinned by
tests/test_soak_run.gd::test_on_tick_observer_sees_every_tick_and_keeps_determinism).
Ship cobalt, single player, no meta-shop state, player: skilled, seeds 2000-2003.
Caveats
Section titled “Caveats”- Mortal runs are short. With a fresh save the skilled autopilot dies in 0.7-1.9 min, so the
mortal table is early-game only. The invuln pass (20 min per run) is the better picture of a
full run’s mix, but it has no hits, so
king_hitanddefeatare 0 there. - The skilled autopilot’s weapon picks decide which weapon cues appear. Pulse (
pulse_zap) and Turret (turret_impact) were never picked in any run, so they read 0 here. They are not dead code (sim/weapon_pulse.gd,sim/weapon_turret.gdemit them). - Weapon-load attenuation (
set_weapon_load) only changes volume, not counts, so the tool skips it. - Menu/UI clicks (
ui_tap), Story room clears (wave_clear) and dead paths (pickups, powerups NUKE/FREEZE) never happen in a survival soak.
Table A: mortal play (frequency)
Section titled “Table A: mortal play (frequency)”cfg: --runs=4 --ticks=36000 (invuln off). All four runs died: 1.1 / 0.7 / 1.9 / 1.7 min, waves
3-5, levels 4-9, 5.4 game-min total.
| sound key | plays/min | total |
|---|---|---|
| aim_fire | 226.0 | 1212 |
| blaster_fire | 125.7 | 674 |
| beam_hum | 46.2 | 248 |
| enemy_death_light_1 | 41.4 | 222 |
| enemy_death_light_2 | 37.8 | 203 |
| enemy_death_light_3 | 37.5 | 201 |
| enemy_death_light_4 | 36.4 | 195 |
| drone_pulse | 25.9 | 139 |
| warp_dash | 25.0 | 134 |
| enemy_ranged_fire | 15.1 | 81 |
| enemy_dash_telegraph | 8.6 | 46 |
| king_hit | 8.4 | 45 |
| enemy_dash_release | 7.8 | 42 |
| scatter_explosion | 7.5 | 40 |
| level_up | 4.3 | 23 |
| melee_swish | 3.0 | 16 |
| special_barrage | 2.6 | 14 |
| drone_summon | 2.4 | 13 |
| enemy_death_heavy_1 | 0.9 | 5 |
| defeat | 0.7 | 4 |
| enemy_death_heavy_2 | 0.7 | 4 |
| enemy_death_heavy_4 | 0.6 | 3 |
| enemy_death_heavy_3 | 0.6 | 3 |
| orbit_ambience (loop) | 0.5% of ticks |
Table B: reachability (full-length invuln runs)
Section titled “Table B: reachability (full-length invuln runs)”cfg: --runs=4 --ticks=72000 --invuln. 4 x 20 min, waves 14-32, levels 53-61, 80 game-min total.
| sound key | plays/min | total |
|---|---|---|
| aim_fire | 279.9 | 22389 |
| blaster_fire | 200.8 | 16061 |
| beam_hum | 126.3 | 10102 |
| enemy_ranged_fire | 43.6 | 3486 |
| warp_dash | 36.7 | 2935 |
| enemy_dash_telegraph | 29.5 | 2362 |
| drone_pulse | 26.5 | 2123 |
| enemy_dash_release | 24.8 | 1980 |
| special_barrage | 17.2 | 1380 |
| melee_swish | 16.8 | 1340 |
| enemy_death_light_1 | 14.2 | 1137 |
| enemy_death_light_3 | 13.9 | 1114 |
| enemy_death_light_2 | 13.8 | 1102 |
| enemy_death_light_4 | 13.8 | 1101 |
| nova_whoosh | 9.7 | 779 |
| scatter_explosion | 8.8 | 707 |
| level_up | 2.8 | 226 |
| boss_missile_launch | 2.4 | 191 |
| drone_summon | 1.9 | 152 |
| enemy_death_heavy_1 | 1.9 | 149 |
| enemy_death_heavy_2 | 1.7 | 135 |
| enemy_death_heavy_4 | 1.5 | 122 |
| enemy_death_heavy_3 | 1.4 | 116 |
| funzo_zone | 0.7 | 53 |
| ghost_telegraph | 0.5 | 43 |
| accumulator_warn | 0.5 | 40 |
| funzo_confetti | 0.4 | 30 |
| boss2_summon | 0.3 | 22 |
| funzo_jester | 0.2 | 13 |
| graviton_singularity | 0.1 | 11 |
| victory | 0.1 | 9 |
| wave_start | 0.1 | 7 |
| eye_laser | <0.1 | 2 |
| graviton_pull | <0.1 | 1 |
| eye_blink | <0.1 | 1 |
| orbit_ambience (loop) | 1.0% of ticks |
Table C: forced bosses (boss-cue reachability)
Section titled “Table C: forced bosses (boss-cue reachability)”cfg: --runs=2 --ticks=10800 --invuln --force_boss=<boss> (2 x 3 min each). Boss-specific keys only;
the background mix matches Table B’s early waves.
| boss | boss keys heard (plays/min) |
|---|---|
| warden | special_barrage 24.0, boss_missile_launch 6.5, boss2_summon 0.5 |
| funzo | funzo_zone 29.7, funzo_jester 9.7, funzo_confetti 9.5 |
| graviton | none (a forced spawn skips the GRAVITON announce; no SINGULARITY within 3 min) |
| eye | beam_hum 194.0, eye_laser 10.3, eye_blink 4.0 |
boss_swing_impact and boss_missile_impact (both sim/boss_warden.gd) never sounded even with a
forced Warden: the swing hit and missile impacts did not land within these runs (or landed on an
invuln pilot whose hit block is skipped). Not investigated further here.
Never heard in any run (0 plays observed)
Section titled “Never heard in any run (0 plays observed)”pulse_zap, turret_impact, special_frost, enemy_lob, boss_swing_impact,
boss_missile_impact, boss2_artillery_land, boss2_cutter_beam, wave_clear, ui_tap,
special_freeze, special_rally, special_aegis, special_stomp. They still go in a batch below.
Replacement batches
Section titled “Replacement batches”Rule, applied in order (frequency = the higher of Table A and Table B):
- Frequent-tier keys with >= 5 plays/min, plus
aim_fire. - Remaining weapon-fire keys (every
SOUND_FOR_FXvalue for aWEAPON_FIRE_KINDSkind). - The 11 synth-tone keys, minus any already in batch 1 or 2.
- Telegraph tier.
- Everything left (boss tier + stragglers), split 5a/5b by frequency to stay within 12.
Amendment (the spec’s approach is frequency-ranked; the batch-1 tier filter was only a proxy for
that): plus any non-frequent-tier key at >= 10 plays/min in either pass, placed in the earliest
batch with room. That pulls warp_dash (36.7/min) into batch 1, which fills it at 12, and
special_barrage (17.2/min) into batch 2. Telegraph-tier keys above 10/min already have their own
batch 4 and stay there.
Batch 1: the most-heard (12)
Section titled “Batch 1: the most-heard (12)”| key | tier | plays/min (A / B) |
|---|---|---|
| aim_fire | player | 226.0 / 279.9 |
| blaster_fire | frequent | 125.7 / 200.8 |
| beam_hum | frequent | 46.2 / 126.3 |
| enemy_death_light_1 | frequent | 41.4 / 14.2 |
| enemy_death_light_2 | frequent | 37.8 / 13.8 |
| enemy_death_light_3 | frequent | 37.5 / 13.9 |
| enemy_death_light_4 | frequent | 36.4 / 13.8 |
| drone_pulse | frequent | 25.9 / 26.5 |
| melee_swish | frequent | 3.0 / 16.8 |
| nova_whoosh | frequent | 0 / 9.7 |
| scatter_explosion | frequent | 7.5 / 8.8 |
| warp_dash | player | 25.0 / 36.7 (amendment) |
Batch 2: remaining weapon fire (5)
Section titled “Batch 2: remaining weapon fire (5)”| key | tier | plays/min (A / B) | note |
|---|---|---|---|
| pulse_zap | frequent | 0 / 0 | 0 plays observed (Pulse never picked) |
| turret_impact | frequent | 0 / 0 | 0 plays observed (Turret never picked) |
| special_frost | frequent | 0 / 0 | 0 plays observed (drone chain zap + DevFlags-only Stasis Web; neither occurred) |
| eye_laser | boss | 0 / <0.1 | also Railgun’s lance; 10.3/min with a forced Eye. Railgun use must survive the swap |
| special_barrage | boss | 2.6 / 17.2 | amendment; generic reaction + Warden barrage/spiral bursts (24.0/min with a forced Warden) |
Batch 3: synth tones (10)
Section titled “Batch 3: synth tones (10)”special_frost already went in batch 2.
| key | tier | plays/min (A / B) | note |
|---|---|---|---|
| level_up | player | 4.3 / 2.8 | also ui_buy |
| defeat | boss | 0.7 / 0 | once per death |
| wave_start | boss | 0 / 0.1 | |
| victory | boss | 0 / 0.1 | |
| ui_tap | ui | 0 / 0 | 0 plays observed (menus only) |
| wave_clear | boss | 0 / 0 | 0 plays observed (Story room clear only) |
| special_freeze | player | 0 / 0 | 0 plays observed (FREEZE powerup, powerups off) |
| special_rally | player | 0 / 0 | 0 plays observed (Logistics drone OVERCHARGE) |
| special_aegis | player | 0 / 0 | 0 plays observed (Decoy Shield Burst) |
| special_stomp | player | 0 / 0 | 0 plays observed (NUKE powerup, powerups off) |
Batch 4: telegraphs (6)
Section titled “Batch 4: telegraphs (6)”| key | plays/min (A / B) | note |
|---|---|---|
| enemy_ranged_fire | 15.1 / 43.6 | |
| enemy_dash_telegraph | 8.6 / 29.5 | also the DASH! reaction |
| enemy_dash_release | 7.8 / 24.8 | |
| ghost_telegraph | 0 / 0.5 | |
| accumulator_warn | 0 / 0.5 | |
| enemy_lob | 0 / 0 | 0 plays observed |
Batch 5a: stragglers, most-heard first (9)
Section titled “Batch 5a: stragglers, most-heard first (9)”| key | tier | plays/min (A / B) |
|---|---|---|
| king_hit | player | 8.4 / 0 (invuln) |
| boss_missile_launch | boss | 0 / 2.4 |
| drone_summon | player | 2.4 / 1.9 |
| enemy_death_heavy_1 | frequent | 0.9 / 1.9 |
| enemy_death_heavy_2 | frequent | 0.7 / 1.7 |
| enemy_death_heavy_3 | frequent | 0.6 / 1.4 |
| enemy_death_heavy_4 | frequent | 0.6 / 1.5 |
| orbit_ambience | frequent | loop: 0.5% / 1.0% of ticks |
| funzo_zone | boss | 0 / 0.7 |
warp_dash and special_barrage were moved out of this batch by the amendment above (batches 1
and 2). king_hit (8.4/min mortal) sits just under the 10/min line, so it stays here.
Batch 5b: rare boss cues (10)
Section titled “Batch 5b: rare boss cues (10)”| key | plays/min (A / B) | note |
|---|---|---|
| funzo_confetti | 0 / 0.4 | |
| boss2_summon | 0 / 0.3 | also the FunZo / Eye arrival rise |
| funzo_jester | 0 / 0.2 | |
| graviton_singularity | 0 / 0.1 | also the shared ENRAGE sting |
| graviton_pull | 0 / <0.1 | |
| eye_blink | 0 / <0.1 | |
| boss_swing_impact | 0 / 0 | 0 plays observed |
| boss_missile_impact | 0 / 0 | 0 plays observed |
| boss2_artillery_land | 0 / 0 | 0 plays observed |
| boss2_cutter_beam | 0 / 0 | 0 plays observed |
Totals: 12 + 5 + 10 + 6 + 9 + 10 = 52, each key once.
Batch 1 prompt log (2026-10-12)
Section titled “Batch 1 prompt log (2026-10-12)”Generated with tools/sound_map/gen_candidates.py using the new --suffix (replaces the default
“soft rounded attack” suffix, which Chris’s “too weak” verdict pointed at), --sound-key (the
enemy.any.death event has a null sound_key, so the death-light family needs the tier key passed
in, which is also written into each candidate’s label) and --max-low 0.6 (rejects takes with more
than 60% of their energy below 250 Hz). ElevenLabs charged 6 credits per 0.5 s take, 7 at 0.68 s and
8 at 0.75 s. Total: 450 credits, hitting the 450 cap exactly. Candidate WAVs are in
audio/candidates/<event>/ and are not committed. Passing takes are in audio/sound_map.json as
v2_<n> candidates.
Finding: ElevenLabs goes bimodal for short weapon SFX. Prompts asking for “punchy body” or
“presence in the low-mids”, or naming “pressurised”/“barrel”/“hum”/“drone”, came back as near-pure sub-bass. The first
aim_fire run had centroids of 12-158 Hz with 99% of the energy under 250 Hz, which is exactly
Chris’s “too boomy”. These takes PASS the sfxlab pre-filter, because it has no low-end check, which
is why --max-low was added. Prompts asking for “crisp” went the other way, to 2-6 kHz. The
wording that worked was “muted”, “hollow”, “wooden”, “dull muffled”, “light”, together with
“warm midrange, muted highs”. Negated bass words in the suffix (“no sub-bass, no boom”) did not
stop the boom. Glass/crystal death prompts landed at a centroid above 8 kHz even with “muted highs”.
Only “dull muffled crack” and “mineral pebble” passed.
Suffixes used:
- S1 (aim1-3, blaster1): “clean defined transient, crisp midrange presence, small and tight, controlled low end, no sub-bass, no boom, no rumble, no long tail, no harsh fizz, dry, no reverb, single hit, no music”. aim1 used a “tight punchy body” variant, and aim3/blaster1 swapped “crisp midrange presence” for “warm midrange presence, muted highs”.
- S2 (one-shots from blaster2 on): “clean defined transient, warm midrange, muted highs, small and light, short, dry, no reverb, single hit, no music”
- W (swipes and whooshes): “clean defined onset, warm midrange, muted highs, light and tight, short, dry, no reverb, single swipe, no music”
- L1 / L2 (beam_hum, a 0.5 s retriggered sustain): L1 = “warm midrange presence, muted highs, seamless sustained loop, steady even level from start to end, no attack, no fade, controlled low end, no sub-bass rumble, no fizz, dry, no reverb, no music”; L2 = “warm midrange, mellow highs, seamless sustained loop, steady even level, no attack, no fade, dry, no reverb, no music”
| key | event id | final prompt (+ suffix) | dur | takes gen / passed | credits |
|---|---|---|---|---|---|
| aim_fire | weapon.aim.fire | “muted hollow puff of warm plasma breath, short thock like a soft wooden tube, light taut string twang, tiny warm analog synth blip tail” + S1 (warm variant). Run 1 gave 3 sub-bass passes, which were manually moved to _rejected. Run 2 (“crisp”) gave 0/4. |
0.5 | 12 / 3 (v2_9, v2_10, v2_12) | 72 |
| blaster_fire | weapon.blaster.fire | “light muted pluck of a taut metal string with a quick soft puff of air, short warm analog synth chirp tail” + S2. Run 1 (“compressed air from a hollow barrel”) gave 0/6, nearly all boom. Failed twice. | 0.5 | 12 / 1 (v2_10) | 72 |
| beam_hum | weapon.beam.beam | “soft warm buzzing tone of a bowed taut metal string with breathy plasma air, mellow analog synth tone” + L2. Run 1 (“steady warm hum … drone” + L1) gave 0/4, all 100% below 250 Hz. Failed twice. | 0.5 | 8 / 1 (v2_5) | 48 |
| enemy_death_light_1 | enemy.any.death (--sound-key) |
“small frosted glass bead cracking with a dull muffled crunch into grit, soft breathy puff, warm analog synth sigh” + S2. Run 1 (“small glass bead cracking softly…”) gave 0/3, centroid above 9 kHz. Failed twice. | 0.5 | 6 / 0 | 36 |
| enemy_death_light_2 | enemy.any.death (--sound-key) |
“brittle crystal shard snapping in two with a muted tinkle, short warm analog synth drop” + S2. Centroid above 9.6 kHz. Failed once; not retried (budget). | 0.5 | 3 / 0 | 18 |
| enemy_death_light_3 | enemy.any.death (--sound-key) |
“small mineral pebble crunching to dust, soft breathy puff, warm hollow analog synth blip” + S2 | 0.5 | 3 / 1 (v2_9) | 18 |
| enemy_death_light_4 | enemy.any.death (--sound-key) |
“thin glass shell popping with a dull muffled crack, brief sandy hiss, short warm analog synth pluck tail” + S2 | 0.5 | 3 / 1 (v2_10) | 18 |
| drone_pulse | player.drone_attack_pulse | “soft rounded pulse of warm plasma breath with a muted metal resonance ping, short warm analog synth blip” + S2. Not retried (budget). | 0.55 | 4 / 1 (v2_3) | 24 |
| melee_swish | weapon.melee.slash | “short thin swish of air from a light reed flicked fast, soft cut, tiny warm analog synth tail” + W. Run 1 (“quick muted swipe of air past a taut blade…”) gave 0/6, nearly all boom. Failed twice. | 0.5 | 10 / 1 (v2_8) | 60 |
| nova_whoosh | weapon.nova.nova | “soft expanding ring of warm plasma breath rushing outward, muted airy whoosh, warm analog synth bloom tail” + W | 0.75 | 4 / 4 | 32 |
| scatter_explosion | weapon.scatter.scatter_fire | “cluster of small muted plasma breath pops scattering outward, soft quick air puffs, short warm analog synth tail” + S2. All 4 peak 75-250 ms after onset, so they swell rather than hit; check by ear. | 0.5 | 4 / 4 | 24 |
| warp_dash | player.dash | “quick muted rush of air and warm plasma breath zipping past, taut string glide, warm analog synth sweep tail” + W | 0.68 | 4 / 2 (v2_1, v2_3) | 28 |
Round-robin coverage is short. blaster_fire and melee_swish have 1 passing take each, not the 3 variants needed. Death-light 1 and 2 have none. The next run should start from the wording that worked (“muted / hollow / dull muffled / mineral”) and needs roughly another 300 credits to fill blaster, melee and the death family to 3 takes or more each.
Round 2 (gap fill, 300-credit cap)
Section titled “Round 2 (gap fill, 300-credit cap)”The manifest write in gen_candidates.py is now append-only. It re-reads sound_map.json at write
time and replaces it atomically, because the Sound Map server was running from this worktree while
Chris assigned picks. His picks (weapon.aim.fire = c3, weapon.blaster.fire = c1) survived the run.
--max-low 0.6 stayed on. Every take cost 6 credits. Total: 300 credits (batch 1 total 750).
| key | event id | prompt (+ suffix) | takes gen / passed | credits |
|---|---|---|---|---|
| enemy_death_light_1 | enemy.any.death | a: “muffled hollow knock of a small clay shell breaking apart, soft dusty puff, short warm analog synth sigh” + S2 → 1/4 (v2_18); b: “tiny light clay bead cracking with a dull muffled tick, soft dusty puff, short warm hollow analog synth sigh” → 0/4 (all > 5 kHz); c: “small clay shell breaking apart with a dull muffled crack, soft dusty puff, short warm analog synth sigh” → 0/4 (> 4 kHz) | 12 / 1 | 72 |
| enemy_death_light_2 | enemy.any.death | a: “dull muffled snap of a thin slate chip splitting, soft gritty scatter, short warm analog synth drop” + S2 → 1/4 (v2_22); b: “small thin slate flake snapping with a dull muffled crack, light gritty scatter, …” → 0/4 (> 6 kHz); c: “dull muffled mid-pitched snap of a slate chip splitting, …” → 0/4 (2-6 kHz 37-45%) | 12 / 1 | 72 |
| blaster_fire | weapon.blaster.fire | a: “muted hollow pluck of a taut metal string with a quick puff of warm plasma breath, short warm analog synth chirp tail” + S2 → 0/6 (5 boom); b: “muted pluck of a taut nylon string with a soft brushed puff of air, short warm analog synth chirp” + S3 (“clean quick onset, mid-pitched, warm midrange, muted highs, small and light, short, dry, no reverb, single hit, no music”) → 0/6 | 12 / 0 | 72 |
| melee_swish | weapon.melee.slash | “short muted swish of air from a wooden rod flicked fast, soft hollow cut, tiny warm analog synth tail” + W | 6 / 2 (v2_12, v2_14) | 36 |
| drone_pulse | player.drone_attack_pulse | same prompt as round 1 + S2 → 3 boom, 1 instant-onset | 4 / 0 | 24 |
| beam_hum | weapon.beam.beam | same prompt as round 1 + L2 | 4 / 0 | 24 |
Findings:
- Size words flip the spectrum. “tiny / light / thin / small” sends a death prompt above 5 kHz. “Knock / hollow / clay” sends it to sub-bass. Clay and slate deaths sit between the two only about 1 time in 4.
- The blaster spectrum is fixed, but the onset is now the blocker. Blaster round b had 4 of 6 takes at centroid 270-980 Hz with no boom. They were rejected only because their attack measured 0.0-0.4 ms (the sound starts at sample 0). drone v2_8 and beam v2_11 were rejected the same way. A 2-3 ms fade-in in
to_wavwould rescue these takes cheaply. It would change what the click filter measures, so it is a call for Chris and was not done here. The rejected files are still inaudio/candidates/<event>/to audit.
Coverage after round 2:
| key | passing takes |
|---|---|
| aim_fire | 3 |
| blaster_fire | 1 |
| beam_hum | 1 |
| death_light_1 | 1 |
| death_light_2 | 1 |
| death_light_3 | 1 |
| death_light_4 | 1 |
| drone_pulse | 1 |
| melee_swish | 3 |
| nova_whoosh | 4 |
| scatter_explosion | 4 |
| warp_dash | 2 |
Rescue pass: 3 ms fade-in (0 credits)
Section titled “Rescue pass: 3 ms fade-in (0 credits)”Coordinator ruling: a take that starts at full amplitude on sample 0 clicks in-game, so a 3 ms
fade-in fixes a real defect; it does not game the filter, and Chris still judges every take by ear.
to_wav now applies a 3 ms linear fade-in to every future take. The new
gen_candidates.py <event> --rescue [--sound-key K] --max-low 0.6 makes no API call. It applies the
fade-in to each existing *_rejected.wav and re-runs the pre-filter and tier normalisation. Each
pass is written as <stem>_rescued.wav and appended as a candidate whose comment reads “rescued: 3
ms fade-in (from …)”. The rejected originals and all existing entries are left alone, and the
manifest is re-read before the write. Takes rejected for boom, brightness or crest stay rejected,
because the fade only changes the onset.
| key | event id | rescued (source run) | passing takes now |
|---|---|---|---|
| blaster_fire | weapon.blaster.fire | 7: v2_9, v2_11 (blaster2), v2_17 (round 2a), v2_20-23 (round 2b) | 8 |
| drone_pulse | player.drone_attack_pulse | 2: v2_4 (round 1), v2_8 (round 2) | 3 |
| enemy_death_light_4 | enemy.any.death | 1: v2_11 (death4 run) | 2 |
| beam_hum | weapon.beam.beam | 0 (v2_11 still fails crest 8.0) | 1 |
| aim_fire, melee_swish, nova_whoosh, scatter_explosion, warp_dash, death_light_1-3 | 0 (no attack-only rejections) | unchanged |
Explosions batch (2026-10-12): player_explosion, boss_explosion
Section titled “Explosions batch (2026-10-12): player_explosion, boss_explosion”Chris: new big-ship explosions that REPLACE the defeat/victory stingers on player and boss death.
Both new keys are boss tier (audio/sfx_tiers.json). Cap 400 credits; spent 376.
- Round 1 (244 credits, 0/8): “massive starship hull explosion … deep sub-bass boom … rumble” (3.0 s, 33 cr/take) and “colossal alien warship detonating, deep sub-bass … 80-250 Hz …” (2.5 s, 28 cr/take). Every take was pure sub (centroid ~40 Hz, -40 to -65 dB above 250 Hz): asking for sub-bass AND “muted highs” in one prompt yields sub only. Long takes cost ~11 credits/s, not 6/take.
- Round 2 (132 credits, 3/6 passed): mid/high layer only, suffix “no bass, no rumble, … warm midrange, natural decay”, 2.0 s: “close-up fireball igniting with a sharp crack, roaring flames, crystal and metal shards splintering …” and “heavy armoured hull tearing apart, chain of crackling secondary blasts, metal plates groaning …”.
- Hand-layered (spec §7, 0 credits): round-1 sub (lowpass 220 Hz, gain picked for ~80% <250 Hz share) under a
round-2 mid take (highpass 120 Hz), onsets aligned by silence trim; 4 candidates per key (
v2_8..11_layer), all pass. Lesson: for explosions, buy the sub and the mid/high as SEPARATE takes and layer them; one prompt can’t get both.