Guides

3D Audio System

Spatial audio positioning and tracking in FMOD API

3D Audio System

FMOD API provides sophisticated 3D spatial audio capabilities that automatically integrate with Minecraft's world space.

🎯 Spatial Audio Features

FeatureDescriptionPerformance
🌍 World SpacePosition sounds anywhere in the worldReal-time updates
👂 Auto ListenerFollows player position/rotation<0.1ms overhead
📏 Distance AttenuationRealistic volume falloffConfigurable curves
🎭 Doppler EffectPitch shifts for moving sourcesOptional

📍 Basic 3D Positioning

Simple Positioned Sound

// Play sound at specific world coordinates
Vec3 position = new Vec3(100, 64, 200);
FMODAPI.playEventSimple("event:/yourmod/ambient_sound",
                        position.x, position.y, position.z);

Entity-Based Positioning

// Play sound attached to an entity
Entity entity = player;
FMODAPI.playEventAtEntity("event:/yourmod/footstep", entity);

Block-Based Positioning

// Play sound at block position
BlockPos blockPos = new BlockPos(50, 70, 100);
FMODAPI.playEventAtBlock("event:/yourmod/machine_hum", blockPos);

🎮 Automatic Listener Tracking

Player Position Sync

The 3D listener automatically tracks:

  • Position - Player's exact world coordinates
  • Rotation - Camera orientation (yaw/pitch)
  • Velocity - Player movement for Doppler effect
  • Up Vector - Camera roll (for advanced effects)
// Happens automatically every tick
@SubscribeEvent
public static void onClientTick(ClientTickEvent event) {
    Player player = Minecraft.getInstance().player;
    if (player != null) {
        // FMOD listener updates automatically
        Vec3 pos = player.position();
        Vec3 forward = player.getLookAngle();
        Vec3 velocity = player.getDeltaMovement();

        FMODAPI.updateListener(pos, forward, velocity);
    }
}

📐 Distance Attenuation

Default Behavior

// Sound audible from 1-64 blocks away
FMODAPI.playEventSimple("event:/yourmod/sound", x, y, z);
// Min distance: 1 block (full volume)
// Max distance: 64 blocks (silent)

Custom Distance Settings

// Custom attenuation curve
int instanceId = FMODAPI.playEvent("event:/yourmod/sound", position);
FMODAPI.setEventMinMaxDistance(instanceId,
    5.0f,   // Min distance (full volume within 5 blocks)
    128.0f  // Max distance (silent beyond 128 blocks)
);

Attenuation Curves

Curve TypeUse CaseBehavior
LinearRealistic outdoorVolume decreases linearly
InverseIndoor spacesSlower falloff near source
CustomSpecial effectsDefined in FMOD Studio

🔊 Advanced 3D Features

Moving Sound Sources

// Update sound position dynamically
int instanceId = FMODAPI.playEvent("event:/yourmod/projectile", position);

// In your entity's tick method
@Override
public void tick() {
    super.tick();
    Vec3 currentPos = this.position();
    FMODAPI.updateEventPosition(instanceId,
        currentPos.x, currentPos.y, currentPos.z);
}

Velocity for Doppler Effect

// Enable Doppler effect for moving sounds
Vec3 position = projectile.position();
Vec3 velocity = projectile.getDeltaMovement();

int instanceId = FMODAPI.playEvent("event:/yourmod/projectile",
                                    position, velocity);
// Pitch shifts as projectile moves toward/away from player

Occlusion and Obstruction

// Manual occlusion control
int instanceId = FMODAPI.playEvent("event:/yourmod/sound", position);

// Calculate line-of-sight obstruction
float occlusion = calculateOcclusion(player.position(), position);
FMODAPI.setEventOcclusion(instanceId, occlusion); // 0.0 = clear, 1.0 = fully blocked

🌍 World Space Coordinates

Coordinate System Mapping

Minecraft coordinates map directly to FMOD 3D space:

  • X → Left/Right (West/East)
  • Y → Up/Down
  • Z → Forward/Back (North/South)

Multi-Dimension Support

// Sounds don't carry across dimensions
if (soundDimension != playerDimension) {
    FMODAPI.stopEvent(instanceId);
}

🎛️ Reverb and Environment

Zone-Based Reverb

// Apply reverb based on location
if (isInCave(player.position())) {
    FMODAPI.setGlobalReverb(ReverbPreset.CAVE);
} else if (isUnderwater(player.position())) {
    FMODAPI.setGlobalReverb(ReverbPreset.UNDERWATER);
} else {
    FMODAPI.setGlobalReverb(ReverbPreset.OFF);
}

Distance-Based Effects

// Apply lowpass filter for distant sounds
float distance = position.distanceTo(listener);
if (distance > 32) {
    float cutoff = Math.max(500, 22000 - (distance * 100));
    FMODAPI.setEventLowpass(instanceId, cutoff);
}

📊 Performance Optimization

Distance Culling

// Automatic culling of distant sounds
FMODAPI.setMaxAudibleDistance(96.0f); // Don't process sounds beyond 96 blocks

Instance Limiting

// Limit concurrent 3D sounds
FMODAPI.setMax3DInstances(64); // Max 64 positioned sounds at once
// Oldest/quietest instances automatically culled

Update Frequency

// Reduce update rate for distant sounds
float distance = soundPosition.distanceTo(listenerPosition);
if (distance > 64) {
    // Update position every 5 ticks instead of every tick
    if (tickCount % 5 == 0) {
        FMODAPI.updateEventPosition(instanceId, x, y, z);
    }
}

🎯 Practical Examples

Ambient Zone System

public class AmbientZone {
    private final Vec3 center;
    private final float radius;
    private int ambientInstance = -1;

    public void update(Player player) {
        float distance = player.position().distanceTo(center);

        if (distance < radius && ambientInstance == -1) {
            // Player entered zone - start ambient sound
            ambientInstance = FMODAPI.playEvent(
                "event:/ambient/forest", center
            );
        } else if (distance >= radius && ambientInstance != -1) {
            // Player left zone - stop ambient sound
            FMODAPI.stopEvent(ambientInstance);
            ambientInstance = -1;
        }
    }
}

Projectile Sound

public class SoundProjectile extends AbstractArrow {
    private int soundInstance = -1;

    @Override
    public void tick() {
        super.tick();

        if (level().isClientSide) {
            if (soundInstance == -1) {
                soundInstance = FMODAPI.playEvent(
                    "event:/projectile/whoosh",
                    position(),
                    getDeltaMovement()
                );
            } else {
                FMODAPI.updateEventPosition(soundInstance,
                    position().x, position().y, position().z
                );
                FMODAPI.updateEventVelocity(soundInstance,
                    getDeltaMovement().x, getDeltaMovement().y, getDeltaMovement().z
                );
            }
        }
    }

    @Override
    public void remove(RemovalReason reason) {
        if (soundInstance != -1) {
            FMODAPI.stopEvent(soundInstance);
        }
        super.remove(reason);
    }
}

Dynamic Machine Sound

public class MachineBlockEntity extends BlockEntity {
    private int machineSound = -1;

    public void tick() {
        if (level.isClientSide && isActive()) {
            if (machineSound == -1) {
                machineSound = FMODAPI.playEvent(
                    "event:/machines/generator",
                    getBlockPos()
                );
            }

            // Update intensity based on machine state
            float intensity = getWorkProgress();
            FMODAPI.setEventParameter(machineSound, "intensity", intensity);
        }
    }

    @Override
    public void setRemoved() {
        if (machineSound != -1) {
            FMODAPI.stopEvent(machineSound);
        }
        super.setRemoved();
    }
}

🐛 Troubleshooting

Sounds Not Positioned Correctly

  • Verify coordinates are in world space, not relative
  • Check listener position is updating
  • Ensure sound instance is still valid

No Distance Attenuation

  • Verify min/max distances are set correctly in FMOD Studio
  • Check 3D settings are enabled for the event
  • Ensure sound is played with position, not as 2D

Performance Issues

  • Reduce max audible distance
  • Implement distance-based update frequency
  • Limit concurrent 3D instances

3D audio works automatically! Just provide coordinates and FMOD API handles positioning, attenuation, and listener tracking for you.

FMOD API for Minecraft © 2025 alexiokay • Audio powered by FMOD Studio by Firelight Technologies Pty Ltd