Nine agent skills for building Makepad/Rust games, ported from majidmanzarpour/threejs-game-skills. Same director-routed workflow and premium bar; runtime rewritten for this fork's game crates. - makepad-game-director entrypoint, routing, continuity, asset probe - makepad-gameplay-systems loop, movers vs rigid bodies, input, camera, netplay - makepad-aaa-graphics-builder lighting, shaders, budget, visual scorecard - makepad-game-ui-designer HUD, menus, touch and XR UI - makepad-debug-profiler defect bisection and profiling - makepad-qa-release verification ladder, evidence, packaging - makepad-3d-generator CC0 model search, casts, procedural geometry - makepad-image-generator texgen textures, palettes, sky, icons - makepad-audio-generator sample bank, mixer, material impacts, 3D audio Written against the real APIs in libs/game/*, libs/sim and apps/arcade: the game.* verb table, GameRenderer adaptive quality, the packed 6-float GameMeshVertex layout, script_mod! splash styling, makepad-test driving, and the BUDGETS.md numbers. No paid generation API is required - the CC0 library plus seeded makepad-game-gen replaces them. Includes install.sh (Codex/Claude), validate-skills.sh and a repo-aware probe_assets.sh; both scripts verified against this checkout.
6.5 KiB
| name | description |
|---|---|
| makepad-audio-generator | Source, synthesize and wire audio for Makepad games: the CC0 sound library, WAV/Ogg Vorbis decoding, sample banks, mixer and voice priority, 3D positional audio, material-based impact sounds, and the audio director. Use for SFX, ambience, UI sounds, music jingles, footsteps, impacts, engine sound and 'it needs sound' requests. |
Makepad Audio Generator
makepad-game-audio is a complete sampled-audio stack: decode, bank, mix, and gameplay-driven emission. No external audio API is needed.
The crate
| Piece | Type | Role |
|---|---|---|
| Decode | decode(bytes) -> Result<Pcm, AudioError>, wav, ogg, vorbis |
WAV and Ogg Vorbis to PCM |
| Bank | SampleBank, SampleId |
loaded samples, looked up by id |
| Mixer | Mixer, VoiceSpec, VoiceHandle, Priority |
playback, voice management |
| Director | AudioDirector, SoundEvent, Category, Placement |
gameplay events → sounds |
| Materials | Material, MaterialPair, ImpactCurve |
what a collision sounds like |
| RNG | LocalRng |
seeded variation |
3D positioning lives in makepad-game-script::audio3d (Listener), with emission helpers in makepad-game-blocks::audio_emit. Script verbs: sfx, sfx_at, beep, jingle, plus ToneWave / AudioRequest in the script dispatch layer.
The crate has no dependencies — decoding, mixing and the Vorbis implementation (codebook, floor, mdct, residue) are all in-tree.
Sourcing
The CC0 library includes 556 sounds across seven Kenney packs — impact, interface, sci-fi, music jingles, UI, RPG and digital audio.
./apps/arcade/download_assets.sh
./apps/arcade/download_assets.sh --transcode # ogg -> wav, needs ffmpeg
Search by description through makepad-game-assets with AUDIO_CATEGORIES, the same index as models. On a score tie the kind the query implies wins — "metal clang" and "win music" are audio requests, "spaceship" is not.
Kenney audio ships .ogg only; the in-tree Vorbis decoder handles it, and --transcode produces WAV where ffmpeg exists.
Synthesis
For UI beeps, tones and simple jingles, synthesize rather than ship a file: beep and jingle verbs with ToneWave. Generate PCM directly for procedural engine notes and pitch-swept effects — deterministic, zero download weight, and infinitely variable.
Wiring audio to gameplay
Drive from real events. dynamics.take_mover_impacts() returns Vec<MoverImpact> — that is the correct source for impact sounds, decals and particle bursts. Detecting collisions a second time in gameplay code produces double-triggered sounds and drifting decals.
for impact in dynamics.take_mover_impacts() {
director.emit(SoundEvent {
category: Category::Impact,
placement: Placement::At(impact.pos),
// material pair + ImpactCurve select the sample and shape the gain
});
}
Material / MaterialPair / ImpactCurve are the point of this design: wood-on-stone and metal-on-metal should not be the same sample at the same volume, and impact energy should scale gain and brightness. This is what makes a world sound physical rather than annotated.
Voice management
Mixer with Priority. A game emits far more sound events than it can usefully play — a wave of enemies produces dozens of footsteps per second, and playing all of them is both expensive and mud.
- Cap concurrent voices and let
Priorityevict. Player feedback outranks ambience always. - Cull by distance before mixing, not after.
- Coalesce: many identical events in one frame become one voice, slightly louder.
- Debounce repeats of the same sample within a few tens of milliseconds — otherwise phase-cancelling copies produce a metallic artefact.
Uncapped voices are the standard audio performance and quality failure, in that order.
Variation
Repetition is what makes game audio sound cheap. With LocalRng:
- Pitch: ±5–10% random per trigger. The single highest-value line of audio code in most games.
- Gain: ±10%.
- Sample choice: 3–5 variants per common event, round-robin with a shuffle rather than pure random (pure random repeats audibly).
Seed it: LocalRng, not thread RNG, so networked clients agree.
3D audio
audio3d::Listener plus Placement. Attach the listener to the camera, not the player — the player hears what the camera sees, and decoupling them is disorienting.
Distance attenuation should be roughly inverse-square with a near-field clamp (or sounds at the listener's position become infinitely loud), plus a maximum range beyond which the voice is culled entirely. Pan by the listener's right vector. Keep UI sounds and player feedback non-positional — a hit confirmation that pans is a hit confirmation that gets missed.
The mix
Rough starting levels, then tune by ear at a realistic volume:
| Category | Level |
|---|---|
| Player feedback (hit, pickup, damage) | loudest |
| UI | just below |
| Nearby gameplay | mid |
| Distant gameplay | low |
| Ambience | quiet, continuous |
| Music | under everything, ducking on important events |
Duck ambience and music when player feedback fires. Provide master / SFX / music sliders in settings; they are expected, and they are the accessibility fallback.
Feedback stacking
A hit must land on several channels in the same frame: HUD change, camera shake, particle burst, and sound. Sound is the channel players notice missing first, and the one most often left until last.
Verification
makepad-game-audio is Cx-free, so it unit-tests headlessly:
cargo test -p makepad-game-audio
Assert: decode produces the expected sample rate, channel count and length; the mixer never exceeds the voice cap; priority evicts the right voice; the material table maps every pair a scene can produce; and no emit path can allocate unboundedly.
Then listen. Play the game for a few minutes: is anything repeating audibly, is anything clipping, does the mix hold when the action peaks, and is there silence where something should have been heard?
Checklist
- Every impact, pickup, and UI action has a sound
- Impacts driven by
take_mover_impacts(), not polling - Material pairs mapped for every surface combination in the scene
- Pitch and gain varied per trigger, seeded
- 3–5 variants for high-frequency events
- Voice cap enforced, priority set, distance culling on
- Listener on the camera
- UI and player feedback non-positional
- Ambience ducks under feedback
- Volume sliders present and persisted
- Nothing clips at peak action