libgdx-audio-lifecycle
npx machina-cli add skill kyu-n/gdx-claude-skills/libgdx-audio-lifecycle --openclawlibGDX Audio
Quick reference for libGDX audio APIs. Covers Sound, Music, platform gotchas, and common audio patterns.
Audio: Sound vs Music
| Sound | Music | |
|---|---|---|
| Loading | Fully into memory | Streamed from disk |
| Use for | Short effects (<1MB on Android) | Background tracks, long audio |
| Concurrent | Multiple instances via play() | One stream per Music object |
| Auto pause/resume | No | Yes (libGDX handles automatically) |
Formats: WAV, MP3, OGG all supported. OGG not supported on iOS — use WAV or MP3.
Sound API
Sound snd = Gdx.audio.newSound(Gdx.files.internal("click.wav"));
// play() returns instance ID (long).
long id = snd.play(); // default volume
long id = snd.play(volume); // volume: [0, 1]
long id = snd.play(volume, pitch, pan); // pitch: [0.5, 2.0], pan: [-1, 1]
long id = snd.loop(); // same overloads as play()
long id = snd.loop(volume, pitch, pan);
// Per-instance control (pass the id from play/loop)
snd.setVolume(id, volume);
snd.setPitch(id, pitch);
snd.setPan(id, pan, volume); // NOTE: pan+volume together
snd.setLooping(id, true);
snd.stop(id);
snd.pause(id);
snd.resume(id);
// All-instance control (no id)
snd.stop();
snd.pause();
snd.resume();
snd.dispose(); // MUST call when done
In non-trivial projects, prefer loading audio via AssetManager rather than raw Gdx.audio.newSound(). AssetManager handles disposal via unload() and supports async loading.
Gotchas:
- Pan only works on mono sounds (stereo sounds ignore pan).
- Android: uncompressed PCM must be <1MB for Sound. Use Music for larger files.
play()on an already-playing Sound plays it concurrently (new instance).- Behavior when exceeding platform limits is backend-dependent — sounds may silently fail to play. Do not rely on the return value to detect this.
play()called increate()can silently fail on Android before the audio system is fully initialized. Defer initial sound playback to the firstrender()frame or use a boolean flag.
Music API
Music bgm = Gdx.audio.newMusic(Gdx.files.internal("theme.ogg")); // OGG not supported on iOS — use MP3 for cross-platform
bgm.play();
bgm.pause();
bgm.stop(); // resets to beginning
bgm.setVolume(volume); // [0, 1]
float v = bgm.getVolume();
bgm.setPan(pan, volume); // [-1, 1], [0, 1]
bgm.setLooping(true);
boolean playing = bgm.isPlaying();
boolean looping = bgm.isLooping();
bgm.setPosition(seconds); // seek (float, in seconds)
float pos = bgm.getPosition();
bgm.setOnCompletionListener(music -> {
// Called when music finishes playing
});
bgm.dispose(); // MUST call when done
In non-trivial projects, prefer loading audio via AssetManager rather than raw Gdx.audio.newMusic(). AssetManager handles disposal via unload() and supports async loading.
Gotchas:
- OnCompletionListener does NOT fire when looping is true. If you need looping + completion callback, set looping=false and restart in the listener.
- libGDX automatically pauses/resumes Music on Android pause/resume. You do NOT need to manually pause music in
ApplicationListener.pause(). Doing so is redundant. - Only one OnCompletionListener per Music instance (last set wins).
setPosition()is unreliable on some Android devices for MP3 — seeking uses bitrate estimation and can be off by several seconds. OGG seeking is more accurate, but OGG is unsupported on iOS. For cross-platform seeking accuracy, consider WAV (large files) or accept MP3 imprecision.
Common Patterns
Shared Music Across Screens
Store Music in the Game class, not in individual Screens. Dispose only in Game.dispose().
public class MyGame extends Game {
public Music bgm;
@Override
public void create() {
bgm = Gdx.audio.newMusic(Gdx.files.internal("theme.mp3")); // use MP3 for iOS compatibility
bgm.setLooping(true);
bgm.play();
setScreen(new MenuScreen(this));
}
@Override
public void dispose() {
bgm.dispose();
getScreen().dispose();
}
}
Platform Differences
| Behavior | Desktop (LWJGL3) | Android | iOS (RoboVM) |
|---|---|---|---|
| OGG support | Yes | Yes | No |
| Music auto-pause | On minimize only | Yes | Yes |
| Sound size limit | None | <1MB PCM | None |
| MP3 seeking accuracy | Good | Unreliable | Good |
Common Mistakes
- Manually pausing Music in pause() — libGDX does this automatically. Redundant code confuses readers.
- Using Sound for long audio — Sound loads entirely into memory. Use Music for anything over a few seconds.
- Expecting OnCompletionListener with looping — It won't fire. Use looping=false + manual restart.
- Using OGG on iOS — Will fail silently or crash. Use WAV/MP3.
- Playing Sound in create() on Android — Audio system may not be ready. Defer to first
render()frame. - Relying on Music.setPosition() accuracy with MP3 on Android — Seeking can be off by seconds. Use OGG for accuracy (but not on iOS).
- Checking Sound.play() return value for failure detection — Behavior is backend-dependent. Don't rely on it.
Source
git clone https://github.com/kyu-n/gdx-claude-skills/blob/master/skills/libgdx-audio-lifecycle/SKILL.mdView on GitHub Overview
Learn how to work with libGDX Sound and Music APIs, including loading, playback, and lifecycle management. This guide covers platform quirks, AssetManager usage, and practical patterns for debugging audio issues.
How This Skill Works
Sound is loaded into memory, while Music streams from disk. Use Sound for short effects and Music for long tracks, with per-instance control via IDs for Sound and a single stream per Music object. AssetManager is recommended for loading and disposal.
When to Use It
- Android/iOS audio initialization: defer first playback until render() after the audio system is ready.
- Choosing Sound vs Music: short effects vs long background tracks.
- Managing multiple hit sounds with per-instance IDs (play/loop) to apply individual volume/pan.
- Platform format gotchas: OGG not supported on iOS; prefer WAV/MP3 for cross-platform compatibility.
- AssetManager workflow: load asynchronously, unload to dispose when no longer needed.
Quick Start
- Step 1: Decide between Sound for short effects and Music for long tracks; prepare assets accordingly.
- Step 2: Load assets with AssetManager and obtain a Sound or Music instance; store IDs as needed.
- Step 3: Play, loop, adjust volume/pan, and dispose assets via AssetManager when done.
Best Practices
- Prefer AssetManager for loading and disposing audio assets.
- Use Sound for short effects (<1 MB on Android) and Music for longer tracks.
- Pan only works on mono sounds; stereo sounds ignore pan.
- Delay first playback on Android until the render() loop to avoid init timing issues.
- Understand OnCompletionListener behavior: it fires only when looping is false.
Example Use Cases
- Load a click sound via AssetManager and play on a button press.
- Play looping background music with volume and pan, while letting Android pause automatically on app pause.
- Play multiple sound instances concurrently using Sound.play() and adjust per-instance volume.
- Seek a Music track with setPosition() and restart on completion when looping is disabled.
- Dispose audio assets through AssetManager unload when no longer needed.