MultiLoader 1.21+ · Part 14

Custom Sounds (MultiLoader 1.21+)

BEGINNER MULTILOADER 1.21-1.21.1 12 min read · Aug 10, 2026
MultiLoader 1.21+ · 28 parts
1 Getting Started with MultiLoader 1.21+ 2 Setting Up RegistrationUtils 3 Creating Items (MultiLoader 1.21+) 4 Creating Blocks (MultiLoader 1.21+) 5 Data Generation: Block & Item Models (MultiLoader 1.21+) 6 Data Generation: Block Loot Tables (MultiLoader 1.21+) 7 Data Generation: Crafting Recipes (MultiLoader 1.21+) 8 Data Generation: Block & Item Tags (MultiLoader 1.21+) 9 Custom Food Items (MultiLoader 1.21+) 10 Custom Tools (MultiLoader 1.21+) 11 Custom Armour (MultiLoader 1.21+) 12 Block Entities (MultiLoader 1.21+) 13 Config Files (MultiLoader 1.21+) 14 Custom Sounds (MultiLoader 1.21+) 15 Events and Listeners (MultiLoader 1.21+) 16 Networking and Custom Packets (MultiLoader 1.21+) 17 Data Generation: Advancements (MultiLoader 1.21+) 18 Data Generation: Language Files (MultiLoader 1.21+) 19 Custom Entities (MultiLoader 1.21+) 20 Ore Generation (MultiLoader 1.21+) 21 Introduction to Mixins (MultiLoader 1.21+) 22 Custom Particles (MultiLoader 1.21+) 23 Menus & Screens (MultiLoader 1.21+) 24 Key Bindings (MultiLoader 1.21+) 25 Custom Potion Effects (MultiLoader 1.21+) 26 Custom Enchantments (MultiLoader 1.21+) 27 Custom Commands (MultiLoader 1.21+) 28 Custom Biomes (MultiLoader 1.21+)

Adding custom sounds involves three steps: registering a SoundEvent in the common module, providing the OGG audio file, and generating the sounds.json descriptor that maps the event to the file. The same event reference then works identically on Fabric and NeoForge.

NOTE
Complete the Setting Up RegistrationUtils tutorial before continuing. We add a newRegistrationProvider for sound events.

Sound Registry

Create a SoundRegistry class in your common registry package:

java
public class SoundRegistry {
public static final RegistrationProvider<SoundEvent> SOUNDS =
RegistrationProvider.get(Registries.SOUND_EVENT, Constants.MOD_ID);
public static final RegistryObject<SoundEvent, SoundEvent> EXAMPLE_AMBIENT =
SOUNDS.register("example_ambient", () ->
SoundEvent.createVariableRangeEvent(
ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "example_ambient")));
public static void init() {}
}

createVariableRangeEvent creates a sound whose audible range scales with the volume you pass when playing it, which is appropriate for most ambient and effect sounds. Use createFixedRangeEvent(location, range) instead for UI sounds that always need to be heard at a consistent distance regardless of volume.

Call SoundRegistry.init() from CommonClass.init():

java
public class CommonClass {
public static void init() {
ArmourMaterialRegistry.init();
SoundRegistry.init();
ItemRegistry.init();
BlockRegistry.init();
BlockEntityRegistry.init();
CreativeTabRegistry.init();
}
}

Audio File

Minecraft requires audio in OGG Vorbis format, mono or stereo, at any standard sample rate. Place your file at:

text
src/main/resources/assets/examplemod/sounds/example_ambient.ogg

You can convert audio to OGG using free tools such as Audacity (export as OGG Vorbis) or FFmpeg (ffmpeg -i input.mp3 output.ogg).

WARNING
Do not use MP3 files. Minecraft's sound engine only reads OGG. Using the wrong format will cause the sound event to register successfully but produce no audio, with no obvious error message.

Datagen: Sound Definitions

Instead of writing sounds.json by hand, use a SoundDefinitionsProvider in your NeoForge datagen setup. Create ExampleSoundDefinitionsProvider in your NeoForge data package:

java
public class ExampleSoundDefinitionsProvider extends SoundDefinitionsProvider {
public ExampleSoundDefinitionsProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
super(output, Constants.MOD_ID, existingFileHelper);
}
@Override
public void registerSounds() {
add(SoundRegistry.EXAMPLE_AMBIENT.get(), definition()
.subtitle("subtitles.examplemod.example_ambient")
.with(sound(ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "example_ambient"))
.volume(1.0f)
.pitch(1.0f)));
}
}

Register the provider in gatherData:

java
generator.addProvider(true,
new ExampleSoundDefinitionsProvider(output, existingFileHelper));

After running datagen, sounds.json is written to common/src/generated/resources/assets/examplemod/ and looks like this:

json
{
"example_ambient": {
"subtitle": "subtitles.examplemod.example_ambient",
"sounds": [
{
"name": "examplemod:example_ambient",
"volume": 1.0,
"pitch": 1.0
}
]
}
}

Add the subtitle translation key to en_us.json:

json
"subtitles.examplemod.example_ambient": "Example ambient sound"

Playing Sounds

Sounds are played through the Level object. There are two variants to be aware of:

java
// Plays for all nearby players (server-side call, automatically synced to clients)
level.playSound(null, pos, SoundRegistry.EXAMPLE_AMBIENT.get(),
SoundSource.AMBIENT, 1.0f, 1.0f);
// Plays only for a specific player (pass the player as the first argument
// to suppress it for that player on the server side, useful for right-click actions)
level.playSound(player, player.blockPosition(), SoundRegistry.EXAMPLE_AMBIENT.get(),
SoundSource.PLAYERS, 1.0f, 1.0f);

The four numeric arguments are: volume (1.0 is full, higher values increase range), pitch (1.0 is normal, 0.5 is an octave lower, 2.0 is an octave higher), and the position is the sound origin.

TIP
The SoundSource category controls which volume slider in the audio settings affects this sound. Use BLOCKS for block interaction sounds, MOBS for entity sounds, and AMBIENT for environmental audio.

Testing In-Game

Run datagen, then launch the client. Trigger the sound from a command block or a temporary event handler and confirm it plays. Check the subtitle overlay (enable it in Accessibility Settings) to verify that the subtitle text appears correctly.

If no sound plays, verify the OGG file exists at the correct path in your resources, that the name in sounds.json matches the file name exactly (without the .ogg extension), and that the sound event's registry name matches the key in sounds.json.

You can find the source for this tutorial here:

View Source on GitHub
NEXT IN SERIES

Events and Listeners (MultiLoader 1.21+)

Keep game logic in common static handler methods and call them from loader-specific glue classes, using NeoForge @SubscribeEvent and Fabric API callbacks for player login and block break events.

Continue →