Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/LWJGL/lwjgl3/llms.txt

Use this file to discover all available pages before exploring further.

OpenAL is a cross-platform 3D audio API modelled after OpenGL. It lets you place sound sources and a listener anywhere in 3D space, and the library handles distance attenuation, panning, and Doppler shift automatically. LWJGL 3 provides a complete Java binding to OpenAL and the ALC (context) layer. The recommended implementation is OpenAL Soft, which supports Windows, macOS, Linux, and a wide variety of audio hardware.
OpenAL Soft must be present on the target system. On Linux it is usually available via the package manager (libopenal). On Windows and macOS you can bundle the native library with your application using the LWJGL build customiser.
1

Open a device

The device represents a physical or virtual audio output. Pass null to let OpenAL Soft choose the default device:
import static org.lwjgl.openal.ALC11.*;
import static org.lwjgl.openal.AL10.*;
import org.lwjgl.openal.ALC;
import org.lwjgl.openal.ALCCapabilities;

long device = alcOpenDevice((CharSequence) null);
if (device == 0L) {
    throw new IllegalStateException("Failed to open an OpenAL device.");
}

// Build the ALC capabilities object — required before any ALC call
ALCCapabilities deviceCaps = ALC.createCapabilities(device);
if (!deviceCaps.OpenALC10) {
    throw new IllegalStateException("OpenALC 1.0 not supported.");
}
You can also pass a specific device name string — ALUtil.getStringList(NULL, ALC_ALL_DEVICES_SPECIFIER) returns all available output device names.
2

Create a context and make it current

A context holds all OpenAL state. An application normally creates one context per device:
import java.nio.IntBuffer;
import static org.lwjgl.system.MemoryUtil.*;

// Pass null for default attributes (sample rate, refresh rate, etc.)
long context = alcCreateContext(device, (IntBuffer) null);
if (!alcMakeContextCurrent(context)) {
    throw new IllegalStateException("Failed to make OpenAL context current.");
}

// Build the AL capabilities object — required before any AL call
import org.lwjgl.openal.AL;
import org.lwjgl.openal.ALCapabilities;

ALCapabilities caps = AL.createCapabilities(deviceCaps);
AL.createCapabilities probes the context for supported AL extensions and populates the ALCapabilities object that LWJGL consults when you call AL functions.
3

Generate sources and buffers

OpenAL separates audio data (buffers) from playback state (sources). A buffer holds raw PCM samples; a source has position, velocity, pitch, gain, and a reference to a buffer.
// Generate one buffer and one source
int buffer = alGenBuffers();
int source = alGenSources();
Multiple sources can share the same buffer, allowing the same sound effect to play simultaneously from different positions with independent gain and pitch.
4

Load audio data into the buffer

alBufferData uploads raw PCM samples to the GPU-side buffer. The ALCDemo sample loads an Ogg Vorbis file using STB Vorbis and passes the decoded PCM directly:
import static org.lwjgl.openal.AL10.*;
import org.lwjgl.stb.*;
import java.nio.ShortBuffer;

// Decode an Ogg Vorbis file to interleaved 16-bit PCM
try (STBVorbisInfo info = STBVorbisInfo.malloc()) {
    // stb_vorbis_open_memory, stb_vorbis_get_info, etc.
    ShortBuffer pcm = decodedSamples; // short[] interleaved by channel

    // Choose the format based on channel count
    int format = info.channels() == 1
        ? AL_FORMAT_MONO16
        : AL_FORMAT_STEREO16;

    alBufferData(buffer, format, pcm, info.sample_rate());
}
Common format constants:
ConstantDescription
AL_FORMAT_MONO88-bit mono
AL_FORMAT_MONO1616-bit mono (most common for decoded audio)
AL_FORMAT_STEREO88-bit stereo
AL_FORMAT_STEREO1616-bit stereo
3D positional effects (distance attenuation, panning) only work with mono sources. Stereo buffers are played back as-is without spatial processing.
5

Attach the buffer to the source

alSourcei(source, AL_BUFFER, buffer);
AL_BUFFER is a source property of integer type, set via alSourcei. After this call the source references the buffer but does not yet play it.
6

Position the listener and source

OpenAL uses a right-handed coordinate system. The listener represents the player’s ears; all distance and panning calculations are relative to it.
// Listener at the origin, facing -Z, up is +Y
alListener3f(AL_POSITION,    0.0f, 0.0f,  0.0f);
alListener3f(AL_VELOCITY,    0.0f, 0.0f,  0.0f);

// FloatBuffer overload accepts orientation as two 3D vectors (at, up)
float[] orientation = {
     0.0f, 0.0f, -1.0f,   // forward ("at")
     0.0f, 1.0f,  0.0f    // up
};
alListenerfv(AL_ORIENTATION, orientation);

// Place the source 5 units to the right
alSource3f(source, AL_POSITION,  5.0f, 0.0f, 0.0f);
alSource3f(source, AL_VELOCITY,  0.0f, 0.0f, 0.0f);
alSourcef(source,  AL_GAIN,      1.0f);
alSourcef(source,  AL_PITCH,     1.0f);
Relevant spatial constants:
ConstantUsed with
AL_POSITIONalListener3f, alSource3f
AL_VELOCITYalListener3f, alSource3f (for Doppler)
AL_ORIENTATIONalListenerfv (listener only)
AL_GAINalSourcef, alListenerf
7

Play the source

alSourcePlay(source);

// Poll the source state to detect when playback has finished
while (alGetSourcei(source, AL_SOURCE_STATE) == AL_PLAYING) {
    Thread.sleep(100);
}
AL_SOURCE_STATE returns one of AL_INITIAL, AL_PLAYING, AL_PAUSED, or AL_STOPPED. You can pause, resume, stop, and rewind with alSourcePause, alSourcePlay, alSourceStop, and alSourceRewind.For looping sounds, set alSourcei(source, AL_LOOPING, AL_TRUE) before calling alSourcePlay.
8

Clean up

Release resources in reverse order. OpenAL objects are integers; sources and buffers must be deleted before destroying the context.
// Stop playback before deleting
alSourceStop(source);

alDeleteSources(source);
alDeleteBuffers(buffer);

// Detach context, then destroy it
alcMakeContextCurrent(0L);
alcDestroyContext(context);

// Close device last
alcCloseDevice(device);

Streaming audio for long tracks

Loading an entire audio file into a single buffer works well for short sound effects, but for music or dialogue you should stream the data through a queue of rotating buffers:
// Generate several small buffers and enqueue them on the source
int[] streamBuffers = new int[4];
alGenBuffers(streamBuffers);

// Fill each buffer with a chunk of decoded audio and enqueue it
alSourceQueueBuffers(source, streamBuffers);
alSourcePlay(source);

// In your update loop, check for processed buffers and refill them
int processed = alGetSourcei(source, AL_BUFFERS_PROCESSED);
while (processed-- > 0) {
    int buf = alSourceUnqueueBuffers(source);
    // decode next chunk into buf
    alSourceQueueBuffers(source, buf);
}
This technique keeps memory usage constant regardless of track length, at the cost of a background thread (or per-frame update) to keep the queue fed.

EFX — effects and reverb

OpenAL Soft supports the EFX extension (ALC_EXT_EFX), which adds environmental reverb, chorus, distortion, and other DSP effects. Check deviceCaps.ALC_EXT_EFX before using EFX functions. The EFXTest.java sample demonstrates reverb presets.

Build docs developers (and LLMs) love