Using HTML5 Audio in Twine

HTML5 audio is rather messy at the moment. While there are standards in place, and the Web Audio specification is supported in both Firefox and Chrome as of their latest versions, the licensing issues surrounding the file formats themselves prevent some browsers from playing them. To achieve some manner of cross-browser support, you need to test for Web Audio support, if you want access to its advance features, as well as which file formats, dictated by the browser, the operating system, and sometimes even the hardware involved, can be played. Luckily, there are several JavaScript libraries out there right now which solve some, but not all, of these issues.

For this post, I’m going to cover a library I’ve used in the past and generally recommend: Howler.js. It has good testing for the majority of file formats most platforms use and presents a simple interface for doing basic audio tasks.

To use it with Twine (and other HTML5 projects), though, we need to be aware of the following issues:

  • Multiple files will be needed. Unlike image files, which can now be easily linked to and used with Twine 1.4, sound files are potentially much larger and also require hosting from some location. They will need to be uploaded and stored in the same folder structure as assumed by the code.
  • Multiple formats will be needed. Because of the licensing issues surrounding formats like OGG and MP3, it is best to have at least three versions (WAV, MP3, and OGG) of every sound you want played for desktop browsers. For mobile users, I highly recommend adding M4A to the list too.
  • Loading can take time. Because of their potentially larger size, audio files maybe not always play exactly when you want, or even at all. And because of bandwidth concerns on mobile browsers, sometimes files greater than a certain size are ignored too. (There are ways to mitigate the bandwidth and loading issues, but they fall far outside the scope of this post, unfortunately.)
  • Be careful about copyright. Make sure, if this Twine project is going to be public, that you own or otherwise have the rights to use the audio in question. It’s not worth the problems that come from ignoring this, believe me. And there are numerous Creative Commons licensed tracks from places like SoundCloud to choose from as well.

We are also going to use a technique that comes with the ability to be abused, so I am giving this warning right here: dynamically loading JavaScript in Twine, as I will demonstrate, is a potential security risk. Make sure you know from where the file or library comes from and that you trust the source.

(Note: I won’t be covering how to create audio files or convert them to different formats in this post. However, if you don’t already have it, I highly recommend Audacity for recording. It also has the ability, through File -> Export, to create OGG, MP3, and WAV files too. And if you have FFmpeg installed — requires separate download — it can convert files to M4A as well.)

The first step will be to create a new Story in Twine. Do that now. Then, go ahead and save it in a known location (as we will be using its folder in just a moment).

Next, we will need to get the Howler.js code itself. For that, visit here.


Follow the link to the GitHub source.


On the right hand side, there will be a link to “Download ZIP.” Do that.

Once the download is complete, extract the files.

Jan18--3Copy the “howler.min.js” source to the folder where you saved the Twine file.


Within Twine, create two Script passages.

macros['loadJS'] =
handler: function(place, object, parameters)
var se = document.createElement("script");
se.type = 'text/javascript';
se.src = parameters[0];
var hT = document.getElementsByTagName("HEAD")[0];
if(se.innerText) {eval(se.innerText);}
else {eval(se.textContent);}
view raw loadJS.js hosted with ❤ by GitHub

In the first, copy the above code. It is what will dynamically load JavaScript, including the Howler.js library.

macros['playAudio'] =
handler: function(place, object, parameters)
var sound = new Howl({
urls: parameters,
view raw playAudio.js hosted with ❤ by GitHub

In the second, copy this next above code. It is the macro that will be called whenever a sound should be played.

It creates a single instance of a Howler object, with the sources passed to it from the calling passage, and once it loads one of the files, plays it immediately.

To actually use all this code, we need to make sure the following condition is met: the “loadJS” macro, with the argument to load the Howler.js library, MUST be called before the “playAudio” macro.

:: Start
Your story will display this passage first. Edit it by double clicking it.
<<display "loadAudio">>
:: loadAudio
<<loadJS "howler.min.js">>
view raw twineAudio.js hosted with ❤ by GitHub

So, one method might be to create a passage with a single line that calls the “loadJS” macro. Then, within the Start passage, have the “display” macro call this other passage.

:: PlayAudio
<<playAudio "sound.mp3" "sound.ogg" "sound.wav" "sound.m4a">>
view raw twineAudio.js hosted with ❤ by GitHub

Finally, to play a sound file in another passage, we simply call the “playAudio” macro with the parameters of the file and its associated different formats. Howler.js will load the first supported type and play the file.


  • I show one of the most basic ways to play audio files here. Consider, if you want more options, investigating the Howler.js site or even the code itself on GitHub. There are ways to have Howler objects chained together or play as a result of other actions.
  • If you have many short sounds, consider making an audio sprite instead. Howler.js has the ability to play from and to a preset place within an audio file. This can be used to group sounds together as one download instead of even more files.
  • Allow the user to have some control over the volume. Because it is bad form to blast full-volume sounds at an unaware user, either let them know your project has sounds of some kind or, with advanced JavaScript usage, allow them to raise or lower the volume to their own liking.

2 thoughts on “Using HTML5 Audio in Twine

  1. bawpie

    Thanks for this. Although I haven’t used it for audio, your instructions for adding custom javascript are the best I’ve found, so thanks muchly!

  2. Thanks for this, I was trying desperately to figure out how to load custom javascript, and this post has described it perfectly. I would prefer to change my javascript into a custom Twine macro just for better integration, but as a complete javascript novice I have no idea where to start.

    I’ve seen plenty of examples and references to people using jquery and javascript in their Twine projects, but it doesn’t exactly seem to be well documented!

Comments are closed.