ForgeLeaf Logo

Consent to our cookie policy 🍪

We use a privacy-friendly, cookie-free analytics tool (Umami). Some cookies are used for functionality only. You may check our Privacy Policy.
A monitor showing code with two speakers on the side

Adding Audio to Your Game with Forge 🎶

This is the third article in the Forge series. In the previous post, we learned how to render textures to the screen. Now, let’s add another layer of life to our projects: audio.

Forge comes with a simple yet powerful Audio API, making it easy to load, play, and control both music and sound effects.


Understanding Forge Audio 🎧

Forge provides two main types of audio objects:

Audio.MusicStreamed from disk during playbackBackground music or long tracks (30+ seconds)
Audio.SoundFully loaded into memoryShort effects (clicks, jumps, UI sounds)

Loading audio takes a lot of memory, that’s why tis distinction helps optimize performance. Large files are streamed to save memory, while small recurring effects are kept ready for instant playback to reduce latency.


Project Setup 🧱

As always, we’ll start by creating a new project using the Forge Starter Template:

git clone https://github.com/ForgeLeaf/ForgeStarterTemplate.git
cd ForgeStarterTemplate

Now, as usual, create your assets folder within the project root directory, and you’re ready to download both assets and place move them into the assets folder.

You can download the ZIP file here (Credits are listed below!)

The Hero Immortal.mp3 file is our music and the Click_Soft_00.wav is our sound effect.

Your assets folder should look like this:


Creating our Variables 📖

As usual, we have to define our assets within our code. For large games, I’d recommend having a structured and optimized asset manager, but for now… We’ll simply declare our objects within the Application struct 😁.

So, just import the github.com/ForgeLeaf/Forge/Audio package, run go mod tidy and declare these two variables in the Application struct:

type Application struct {
	music *Audio.Music
	sound *Audio.Sound
}

Loading our Music 🎵

Ok, now that we’ve declared our music and sound, we’ll first focus on loading the music object. As usual, we go to our Create() method.

This time we have to keep in mind that our audio is large and we don’t want to let the full game await the loading of our audio objects, that’s why Forge allows you to asynchronously load your audio objects, we do that by placing our calls to Audio.NewMusic(...) and Audio.NewSound(...) in a goroutine.

So, it should look like this for your music:

func (application *Application) Create(driver *Forge.Driver) {
	go func() {
		application.music, _ = Audio.NewMusic("assets/Hero Immortal.mp3")
	}()
}

Just like our texture constructor method, the audio constructors return an audio object and an optional error. We’ll ignore the error for now 🤫

Now, we’d like to instantly play our music after it loads and we’ll add this code snippet into our goroutine:

application.music.SetLooping(true)
application.music.SetVolume(0.5)
application.music.Play()

Let’s break it down:

  1. We set the music mode to looping, so the music never stops.
  2. We set the volume to 50% to not play it loud.
  3. We tell the music object to start streaming and playing the audio.

Before running the code: When working with music objects, remember to close the music object when the game exists, so go to your application destroy callback, the Destroy() method, and add this snippet:

func (application *Application) Destroy(driver *Forge.Driver) {
	application.music.Close()
}

You’ll end up with something like this:

It’s nice to see our game come to live 🤗


Loading our Sound 🎤

Similar to our music, we’ll most of the times want to load our sounds asynchronously in a goroutine. So, let’s jump to our existing goroutine and add this line after constructing our music object:

application.sound, _ = Audio.NewSound("assets/Click_Soft_00.wav")

We’ll ignore the optional error and just pretend that everything works ideally out-of-the-box. Unlike a music object, a sound has its memory managed by the go garbage-collector, so we don’t have to close or dispose anything manually when our application exists.

Now, let’s make our sound play whenever we touch the screen… I know we haven’t discussed input yet but let’s make it fun and we’ll pretend that you already know what a MouseButtonCallback is 🙃

Right after our goroutine call in Create(), we put this self explanatory code snippet:

driver.Input.SetMouseButtonCallback(func(button Input.MouseButton, action Input.Action, modifiers Input.ModifierKey, x, y float32) {
	if button == Input.MouseButtonLeft && action == Input.ActionPress {
		application.sound.Play(1)
	}
})

Let’s break it down quickly:

  1. We register a callback whenever a mouse button action occurs.
  2. When the callback is called, we check if the button is Input.MouseButtonLeft and action is Input.ActionPressed to validate that the user pressed the left mouse button.
  3. We play our sound with volume set to 100%.

You’ll end up with something like this:

And that’s it 🎉

You now know how to create and use audio objects in Forge!


The Full Example Code 🪄

package main

import (
	"runtime"

	"github.com/ForgeLeaf/Forge"
	"github.com/ForgeLeaf/Forge/Audio"
	"github.com/ForgeLeaf/Forge/Input"
)

type Application struct {
	music *Audio.Music
	sound *Audio.Sound
}

func (application *Application) Create(driver *Forge.Driver) {
	go func() {
		application.music, _ = Audio.NewMusic("assets/Hero Immortal.mp3")
		application.sound, _ = Audio.NewSound("assets/Click_Soft_00.wav")

		application.music.SetLooping(true)
		application.music.SetVolume(0.5)
		application.music.Play()
	}()

	driver.Input.SetMouseButtonCallback(func(button Input.MouseButton, action Input.Action, modifiers Input.ModifierKey, x, y float32) {
		if button == Input.MouseButtonLeft && action == Input.ActionPress {
			application.sound.Play(1)
		}
	})
}

func (application *Application) Render(driver *Forge.Driver, delta float32) {}

func (application *Application) Resize(driver *Forge.Driver, width, height float32) {}

func (application *Application) Destroy(driver *Forge.Driver) {
	application.music.Close()
}

func init() {
	runtime.LockOSThread()
}

func main() {
	if err := Forge.RunSafe(&Application{}, Forge.DefaultDesktopConfig()); err != nil {
		panic(err)
	}
}

Compare it to your version and tell me if it works! That’s it! You’ve successfully drawn your first texture with Forge. If you’ve faced any issues, please message me using the form below.

You can find the repository for this tutorial here: https://github.com/ForgeLeaf/WorkingWithAudio-Forge


Audio Credits🔗

🎵 Music: “Hero Immortal” by Trevor Lentz, available on OpenGameArt.org.

🔊 Sound Effect: “Click_Soft_00.wav” by Little Robot Sound Factory, available on OpenGameArt.org.


Common Issues

If you’re getting a black screen:

  • Check if you imported the image/png package.
  • Check if you’re rendering within batch.Begin() and batch.End().
  • Check if you’re applying the viewport projection to the batch.

If the code isn’t compiling:

  • Check if you’ve misspelled any imports.
  • Run go mod tidy and rebuild.
  • Clear IDE cache.
  • Check for syntax errors.

If your program is crashing before rendering anything:

  • Check whether you’ve imported the correct go-gl version or not (Correct one is go-gl/gl/v3.3-core/gl).

If you’re facing any other issues, contact me below!

Give us some feedback!

Thanks for reading our article. Could you please consider giving us some constructive, anonymous feedback?

Related articles

Working with ECS in LibGDX 🤩

Bismillah, dear readers!I hope you enjoyed the long weekend and managed to recharge. Today we’ll explore building a simple game using simple‑world‑gdx (my lightweight ECS for LibGDX), together with simple‑batch‑gdx…
Read more

Forge Framework

Forge is our free and open source game framework for the GoLang programming language. It is feature-rich and constantly improved. We designed Forge to be performant and simple for beginner, its power lies within its ecosystem and community. Forge is lightweight in nature and can be easily extended to fit most of your needs. Write you game logic in your way and let the framework handle compatibility.
Community

Connect with our community

ForgeLeaf Logo
Join the ForgeLeaf community — follow updates, contribute, and grow together.

Subscribe to our Newsletter

Stay Updated on the Latest Dev Insights. Join our mailing list to receive new articles, tutorials, and project updates.
ForgeLeaf Logo
Made with ❤️ in 🇩🇪
Follow us on social media
Subscribe to our Newsletter
© 2025 ForgeLeaf Studio