0 votes
by (180 points)

I made my images clickable via this code

<a data-passage="blah0" class="link-internal link-image"><img src="images/Animation2.gif" style="max-height:300px" ></a>

But I'm confused on how to incorporate code for my audio. Should I use an audio macro? Which part of thecode would I add the macro into? Or would I use code like 

<audio src="the URL of your sound effect" autoplay>

I've attempted both, but I don't think I was placing it correctly as it didn't work or play any audio. I attempted to cache the audio by using a StoryInIt passage but got errors saying my audio file didn't exist.

1 Answer

0 votes
by (44.7k points)

Audio can be a bit tricky to get a handle on, since there are a couple of ways things can go wrong.

First, you want to make sure your code can find the audio files properly.  Putting this bit of code at the beginning of your JavaScript section should help:

if (window.hasOwnProperty("storyFormat")) {
	// Change this to the path where the HTML file is
	// located if you want to run this from inside Twine.
	setup.Path = "C:/Games/MyGameDirectory/";  // Running inside Twine application
} else { 
	setup.Path = "";  // Running in a browser
setup.ImagePath = setup.Path + "images/";
setup.SoundPath = setup.Path + "sounds/";

You'll want to change "C:/Games/MyGameDirectory/" above to the directory where your HTML will go.  This also assumes your audio is in a sub-directory called "sounds", so you'll need to change that if it's not.

Next, you'll want to cache any audio you plan to play, so you'll need something like this in your StoryInit passage:

<<set _bgm = setup.SoundPath + "StartMusic.mp3">>
<<cacheaudio "start_bgm" _bgm>>

Just repeat those two lines for each audio file, only changing the "StartMusic.mp3" to the appropriate filename and "start_bgm" to the trackID you want to use for that file.

Speaking of filenames, not all browsers support all audio formats.  Generally speaking the MP3 format has the widest support, so I'd recommend going with that.

Also, some browsers, mainly Chrome, do not allow you to play audio until the user has interacted with the page, so you may want to either have the user click something to "opt-in" to playing the music, or only start it in the next passage they go to.

Which brings us back to playing the audio.  When doing that you probably should stop any other audio first, and then try to play the one you want using its trackID:

<<masteraudio stop>><<audio "start_bgm" volume 0.2 play loop>>

See the SugarCube Audio Macros documentation for details on the parameters passed to that macro.

If you want to be able to tell if a track is already loaded or if it's currently playing, here are two other functions you can add to your JavaScript section:

// Check to see if BGM is currently loaded
window.TrackExists = function (BGM) {
	var tracks = Macro.get("cacheaudio").tracks;
	if (tracks.hasOwnProperty(BGM)) {
		return true;
	return false;

// Check to see if BGM is the currently playing background music
window.isPlaying = function (BGM) {
	var tracks = Macro.get("cacheaudio").tracks;
	if (tracks.hasOwnProperty(BGM)) {
		var currentbgm = tracks[BGM];
		if (!currentbgm.audio.paused && currentbgm.audio.duration) {
			return true;
	return false;

Those functions would allow you to create a widget like this:

/* PlayTrack: Safely plays audio files in track list. */
/* Usage: <<PlayTrack start_bgm>>                     */
<<widget "PlayTrack">>
	<<set _trackList = {  /* Add your track IDs and filenames below. */
		"start_bgm" : "StartMusic.mp3",
		"middle_bgm" : "DramaticMusic.mp3",
		"end_bgm" : "EndMusic.mp3"
	<<set _nextTrack = $args.raw>>
	<<if def _trackList[_nextTrack]>>
		<<if !TrackExists(_nextTrack)>>  /* If track doesn't exist, load it. */
			<<set _bgm = setup.SoundPath + _trackList[_nextTrack]>>
			<<cacheaudio _nextTrack _bgm>>
		<<if !TrackExists(_nextTrack)>>
			<<run alert('Error: Cannot load file "' + setup.SoundPath + _trackList[_nextTrack] + '".')>>
			<<if !isPlaying(_nextTrack)>>  /* If track isn't already playing, play it. */
				<<masteraudio stop>><<audio _nextTrack volume 0.2 play loop>>
		<<run alert('Error: Unknown track "' + _nextTrack + '".')>>

Put that code into a non-story passage with "widget" and "nobr" tags.  (See the <<widget>> macro for details on how widgets work.)

Whenever you use it, the <<PlayTrack>> widget would load the audio track you gave it if it's not already loaded, and then play it if it's not already playing.  Just modify the _trackList in the widget to have the trackIDs and filenames you want.

Hope that helps!  :-)