code:
# `Save` API {#save-api}

Note:
There are several configuration settings for saves that it would be wise for you to familiarize yourself with.

Warning:
In-browser saves—i.e., autosave and slot saves—are largely incompatible with private browsing modes, which cause all in-browser storage mechanisms to either persist only for the lifetime of the browsing session or fail outright.

## Save Objects {#save-api-save-objects}

Note:
Adding additional properties directly to save objects is not recommended.  Instead, use the metadata property.

Save objects have some of the following properties:

* **`id`:** (*string*) The story's save ID.
* **`state`:** (*object*) The marshaled story history (see below for details).
* **`title`:** (*string*) The title of the save.
* **`date`:** (*integer*) The date when the save was created (in milliseconds elapsed since epoch).
* **`metadata`:** (optional, *any*) Save metadata (end-user specified; must be JSON-serializable).
* **`version`:** (optional, *any*) Save version (end-user specified via [`Config.saves.version`](#config-api-property-saves-version)).

The **`state`** object has the following properties:

* **`history`:** (*Array<object>*) The array of moment objects (see below for details).
* **`index`:** (*integer*) The index of the active moment.
* **`expired`:** (optional, *Array<string>*) The array of expired moment passage titles, exists only if any moments have expired.
* **`seed`:** (optional, *string*) The seed of SugarCube's seedable PRNG, exists only if enabled.

Each **`moment`** object has the following properties:

* **`title`:** (*string*) The title of the associated passage.
* **`variables`:** (*object*) The current variable store object, which contains sigil-less name ⇒ value pairs—e.g., `$foo` exists as `foo`.
* **`pull`:** (optional, *integer*) The current pull count of SugarCube's seedable PRNG, exists only if enabled.

## General {#save-api-general}

### `Save.clear()` {#save-api-method-clear}

Deletes all slot saves and the autosave, if it's enabled.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Save.clear()
```

### `Save.get()` {#save-api-method-get}

Returns the saves object.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Save.get()
```

### `Save.ok()` → *boolean* {#save-api-method-ok}

Returns whether both the slot saves and autosave are available and ready.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (Save.ok()) {
	/* Code to manipulate saves. */
}
```

## Slots {#save-api-slots}

### `Save.slots.length` → *integer* {#save-api-getter-slots-length}

Returns the total number of available slots.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
Save.slots.length
```

### `Save.slots.count()` → *integer* {#save-api-method-slots-count}

Returns the total number of filled slots.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Save.slots.count()
```

### `Save.slots.delete(slot)` {#save-api-method-slots-delete}

Deletes a save from the given slot.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`slot`:** (*integer*) Save slot index (0-based).

#### Examples:

```
Save.slots.delete(5)  → Deletes the sixth slot save
```

### `Save.slots.get(slot)` → *object* {#save-api-method-slots-get}

Returns a save object from the given slot or `null`, if there was no save in the given slot.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`slot`:** (*integer*) Save slot index (0-based).

#### Examples:

```
Save.slots.get(5)  → Returns the sixth slot save
```

### `Save.slots.has(slot)` → *boolean* {#save-api-method-slots-has}

Returns whether the given slot is filled.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`slot`:** (*integer*) Save slot index (0-based).

#### Examples:

```
if (Save.slots.has(5)) {
	/* Code to manipulate the sixth slot save. */
}
```

### `Save.slots.isEmpty()` → *boolean* {#save-api-method-slots-isempty}

Returns whether there are any filled slots.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Save.slots.isEmpty()  → Effectively returns: Save.slots.count() === 0
```

### `Save.slots.load(slot)` {#save-api-method-slots-load}

Loads a save from the given slot.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`slot`:** (*integer*) Save slot index (0-based).

#### Examples:

```
Save.slots.load(5)  → Load the sixth slot save
```

### `Save.slots.ok()` → *boolean* {#save-api-method-slots-ok}

Returns whether the slot saves are available and ready.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (Save.slots.ok()) {
	/* Code to manipulate slot saves. */
}
```

### `Save.slots.save(slot [, title [, metadata]])` {#save-api-method-slots-save}

Saves to the given slot.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`slot`:** (*integer*) Save slot index (0-based).
* **`title`:** (optional, *string*) The title of the save.  If omitted or `null`, defaults to the passage's description.
* **`metadata`:** (optional, *any*) The data to be stored in the save object's `metadata` property.  Must be JSON-serializable.

#### Examples:

```
→ Save to the sixth slot save with the default title and no metadata
Save.slots.save(5)

→ Save to the sixth slot save with the title "Midgar" and no metadata
Save.slots.save(5, "Midgar")

→ Save to the sixth slot save with the default title and metadata someMetadata
Save.slots.save(5, null, someMetadata)

→ Save to the sixth slot save with the title "Midgar" and metadata someMetadata
Save.slots.save(5, "Midgar", someMetadata)
```

## Autosave {#save-api-autosave}

### `Save.autosave.delete()` {#save-api-method-autosave-delete}

Deletes the autosave.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Save.autosave.delete()  → Deletes the autosave
```

### `Save.autosave.get()` → *object* {#save-api-method-autosave-get}

Returns the save object from the autosave or `null`, if there was no autosave.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Save.autosave.get()  → Returns the autosave
```

### `Save.autosave.has()` → *boolean* {#save-api-method-autosave-has}

Returns whether the autosave is filled.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (Save.autosave.has()) {
	/* Code to manipulate the autosave. */
}
```

### `Save.autosave.load()` {#save-api-method-autosave-load}

Loads the autosave.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Save.autosave.load()  → Load the autosave
```

### `Save.autosave.ok()` → *boolean* {#save-api-method-autosave-ok}

Returns whether the autosave is available and ready.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (Save.autosave.ok()) {
	/* Code to manipulate the autosave. */
}
```

### `Save.autosave.save([title [, metadata]])` {#save-api-method-autosave-save}

Saves to the autosave.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`title`:** (optional, *string*) The title of the save.  If omitted or `null`, defaults to the passage's description.
* **`metadata`:** (optional, *any*) The data to be stored in the save object's `metadata` property.  Must be JSON-serializable.

#### Examples:

```
→ Save to the autosave with the default title and no metadata
Save.autosave.save()

→ Save to the autosave with the title "Midgar" and no metadata
Save.autosave.save("Midgar")

→ Save to the autosave with the default title and metadata someMetadata
Save.autosave.save(null, someMetadata)

→ Save to the autosave with the title "Midgar" and metadata someMetadata
Save.autosave.save("Midgar", someMetadata)
```

## Disk {#save-api-disk}

### `Save.export([filename [, metadata]])` {#save-api-method-export}

Saves to disk.

#### History:

* `v2.0.0`: Introduced.
* `v2.8.0`: Added `metadata` parameter.

#### Parameters:

* **`filename`:** (optional, *string*) The base filename of the save, which gets slugified to remove most symbols.  Appended to this is a datestamp (format: `YYYMMDD-hhmmss`) and the file extension `.save`—e.g., `"The Scooby Chronicles"` would result in the full filename: `the-scooby-chronicles-{datestamp}.save`.  If omitted or `null`, defaults to the story's title.
* **`metadata`:** (optional, *any*) The data to be stored in the save object's `metadata` property.  Must be JSON-serializable.

#### Examples:

```
→ Export a save with the default filename and no metadata
Save.export()

→ Export a save with the filename "the-7th-fantasy-{datestamp}.save" and no metadata
Save.export("The 7th Fantasy")

→ Export a save with the default filename and metadata someMetadata
Save.export(null, someMetadata)

→ Export a save with the filename "the-7th-fantasy-{datestamp}.save" and metadata someMetadata
Save.export("The 7th Fantasy", someMetadata)
```

### `Save.import(event)` {#save-api-method-import}

Loads a save from disk.

**NOTE:** You do not call this manually, it *must* be called by the `change` event handler of an `<input type="file">` element.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`event`:** (*event object*) The event object that was passed to the `change` event handler of the associated `<input type="file">` element.

#### Examples:

##### Basic usage

```
// Assuming you're creating a file input something like the following
var input  = document.createElement('input');
input.type = 'file';
input.id   = 'saves-import-file';
input.name = 'saves-import-file';

// Set up Save.import() as the event handler for when a file has been chosen
jQuery(input).on('change', Save.import);
```

In case you needed to do more than simply load the save, you may do something like the following:

```
// Assuming you're creating a file input something like the following
var input  = document.createElement('input');
input.type = 'file';
input.id   = 'saves-import-file';
input.name = 'saves-import-file';

// Set up a custom event handler for when a file has been chosen, which will call Save.import()
jQuery(input).on('change', function (ev) {
	// You must pass in the event when calling Save.import() manually
	Save.import(ev);

	// Put anything else you needed to do here
});
```

##### As a self-contained link/button using macros

```
<<link "Load From Disk">>
	<<script>>
	jQuery(document.createElement('input'))
		.prop('type', 'file')
		.on('change', Save.import)
		.trigger('click');
	<</script>>
<</link>>
```

## Serialization {#save-api-serialization}

### `Save.serialize([metadata])` → *string* | *null* {#save-api-method-serialize}

Returns a save as a serialized string, or `null` if saving is not allowed within the current context.

#### History:

* `v2.21.0`: Introduced.

#### Parameters:

* **`metadata`:** (optional, *any*) The data to be stored as metadata.  Must be JSON-serializable.

#### Examples:

```
→ Serialize a save with no metadata
const myGameState = Save.serialize();
if (myGameState === null) {
	/* Failure.  You've disallowed saving. */
}

→ Serialize a save with metadata someMetadata
const myGameState = Save.serialize(someMetadata);
if (myGameState === null) {
	/* Failure.  You've disallowed saving. */
}
```

### `Save.deserialize(saveStr)` → *any* | *null* {#save-api-method-deserialize}

Deserializes the given save string, created via [`Save.serialize()`](#save-api-method-serialize), and loads the save.  Returns the bundled metadata, if any, or `null` if the given save could not be deserialized and loaded.

#### History:

* `v2.21.0`: Introduced.

#### Parameters:

* **`saveStr`:** (*string*) The serialized save string.

#### Examples:

```
→ Deserialize a save with no metadata
const loadResult = Save.deserialize(myGameState);
if (loadResult === null) {
	/* Failure.  An error was displayed to the player. */
}

→ Deserialize a save with metadata
const loadResult = Save.deserialize(myGameState);
if (loadResult !== null) {
	/* Success.  Do something with loadResult, which contains the metadata. */
}
else {
	/* Failure.  An error was displayed to the player. */
}
```

## Events {#save-api-events}

### `Save.onLoad.add(handler)` {#save-api-method-onload-add}

Performs any required processing before the save data is loaded—e.g., upgrading out-of-date save data.  The handler is passed one parameter, the save object to be processed.  If it encounters an unrecoverable problem during its processing, it may throw an exception containing an error message; the message will be displayed to the player and loading of the save will be terminated.

#### History:

* `v2.36.0`: Introduced.

#### Parameters:

* **`handler`:** (*function*) The handler function to be executed upon the loading of a save.

#### Handler parameters:

* **`save`:** (*object*) The [save object](#save-api-save-objects) to be processed.

#### Examples:

```js
Save.onLoad.add(function (save) {
	/* code to process the save object before it's loaded */
});
```

### `Save.onLoad.clear()` {#save-api-method-onload-clear}

Deletes all currently registered on-load handlers.

#### History:

* `v2.36.0`: Introduced.

#### Parameters: *none*

#### Examples:

```js
Save.onLoad.clear();
```

### `Save.onLoad.delete(handler)` → *boolean* {#save-api-method-onload-delete}

Deletes the specified on-load handler, returning `true` if the handler existed or `false` if not.

#### History:

* `v2.36.0`: Introduced.

#### Parameters:

* **`handler`:** (*function*) The handler function to be deleted.

#### Examples:

```js
// Given:
// 	let myOnLoadHandler = function (save) {
// 		/* code to process the save object before it's loaded */
// 	};
// 	Save.onLoad.add(myOnLoadHandler);

Save.onLoad.delete(myOnLoadHandler);
```

### `Save.onLoad.size` → *integer* {#save-api-getter-onload-size}

Returns the number of currently registered on-load handlers.

#### History:

* `v2.36.0`: Introduced.

#### Parameters: *none*

#### Examples:

```js
console.log('There are %d onLoad handlers registered.', Save.onLoad.size);
```

### `Save.onSave.add(handler)` {#save-api-method-onsave-add}

Performs any required processing before the save data is saved.  The handlers is passed two parameters, the save object to be processed and save operation details object.

#### History:

* `v2.36.0`: Introduced.

#### Parameters:

* **`handler`:** (*function*) The handler function to be executed upon the saving of a save.

#### Handler parameters:

* **`save`:** (*object*) The [save object](#save-api-save-objects) to be processed.
* **`details`:** (*object*) The save operation details object.

#### Save operation details object:

A save operation details object will have the following properties:

* **`type`:** (*string*) A string representing what caused the save operation.  Possible values are: `'autosave'`, `'disk'`, `'serialize'`, `'slot'`.

#### Examples:

##### Using only the save object parameter

```js
Save.onSave.add(function (save) {
	/* code to process the save object before it's saved */
});
```

##### Using both the save object and operation details parameters

```js
Save.onSave.add(function (save, details) {
	switch (details.type) {
		case 'autosave': {
			/* code to process the save object from autosaves before it's saved */
			break;
		}

		case 'disk': {
			/* code to process the save object from disk saves before it's saved */
			break;
		}

		case 'serialize': {
			/* code to process the save object from serialize saves before it's saved */
			break;
		}

		default: { /* slot */
			/* code to process the save object from slot saves before it's saved */
			break;
		}
	}
});
```

### `Save.onSave.clear()` {#save-api-method-onsave-clear}

Deletes all currently registered on-save handlers.

#### History:

* `v2.36.0`: Introduced.

#### Parameters: *none*

#### Examples:

```js
Save.onSave.clear();
```

### `Save.onSave.delete(handler)` → *boolean* {#save-api-method-onsave-delete}

Deletes the specified on-save handler, returning `true` if the handler existed or `false` if not.

#### History:

* `v2.36.0`: Introduced.

#### Parameters:

* **`handler`:** (*function*) The handler function to be deleted.

#### Examples:

```js
// Given:
// 	let myOnSaveHandler = function (save, details) {
// 		/* code to process the save object before it's saved */
// 	};
// 	Save.onSave.add(myOnSaveHandler);

Save.onSave.delete(myOnSaveHandler);
```

### `Save.onSave.size` → *integer* {#save-api-getter-onsave-size}

Returns the number of currently registered on-save handlers.

#### History:

* `v2.36.0`: Introduced.

#### Parameters: *none*

#### Examples:

```js
console.log('There are %d onSave handlers registered.', Save.onSave.size);
```


code:
# `Setting` API {#setting-api}

Manages the Settings dialog and [`settings` object](#setting-api-object-settings).

Warning:
Setting API method calls must be placed within your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) or settings will not function correctly.

### `Setting.addHeader(name [, desc])` {#setting-api-method-addheader}

Adds a header to the Settings dialog.

#### History:

* `v2.7.1`: Introduced.

#### Parameters:

* **`name`:** (*string*) Name of the header.
* **`desc`:** (optional, *string*) Description explaining the header in greater detail.

#### Examples:

```
// Setting up a basic header
Setting.addHeader("Content Settings");

// Setting up a header w/ a description
Setting.addHeader("Content Settings", "Settings controlling what content is made available in the game.");
```

### `Setting.addToggle(name, definition)` {#setting-api-method-addtoggle}

Adds the named property to the `settings` object and a toggle control for it to the Settings dialog.

#### History:

* `v2.0.0`: Introduced.
* `v2.26.0`: Added `desc` property to definition object.

#### Parameters:

* **`name`:** (*string*) Name of the `settings` property to add, which the control will manage.
* **`definition`:** (*object*) Definition of the control.

#### Definition object:

A toggle definition object should have some of the following properties:

* **`label`:** (*string*) Label to use for the control.
* **`desc`:** (optional, *string*) Description explaining the control in greater detail.
* **`default`:** (optional, *boolean*) The default value for the setting and default state of the control.  Leaving it undefined means to use `false` as the default.
* **`onInit`:** (optional, *function*) The function to call during initialization.
* **`onChange`:** (optional, *function*) The function to call when the control's state is changed.

#### Examples:

##### Basic toggle setting

```
// Setting up a basic toggle control for the settings property 'mature'
Setting.addToggle("mature", {
	label : "Content for mature audiences?"
}); // default value not defined, so false is used
```

##### Toggle that adds/removes a CSS class

```
// Setting up a toggle control for the settings property 'widescreen' w/ callbacks
var settingWidescreenHandler = function () {
	if (settings.widescreen) { // is true
		$("html").addClass("widescreen");
	}
	else { // is false
		$("html").removeClass("widescreen");
	}
};
Setting.addToggle("widescreen", {
	label    : "Allow the story to use the full width of your browser window?",
	default  : false,
	onInit   : settingWidescreenHandler,
	onChange : settingWidescreenHandler
});
```

And the requisite CSS style rule:

```
html.widescreen #passages {
	max-width: none;
}
```

### `Setting.addList(name, definition)` {#setting-api-method-addlist}

Adds the named property to the `settings` object and a list control for it to the Settings dialog.

#### History:

* `v2.0.0`: Introduced.
* `v2.26.0`: Added `desc` property to definition object.

#### Parameters:

* **`name`:** (*string*) Name of the `settings` property to add, which the control will manage.
* **`definition`:** (*object*) Definition of the control.

#### Definition object:

A list definition object should have some of the following properties:

* **`label`:** (*string*) Label to use for the control.
* **`list`:** (*Array<string>*) The array of items.
* **`desc`:** (optional, *string*) Description explaining the control in greater detail.
* **`default`:** (optional, *[as **`list`** array]*) The default value for the setting and default state of the control.  It should have the same value as one of the members of the **`list`** array.  Leaving it undefined means to use the first array member as the default.
* **`onInit`:** (optional, *function*) The function to call during initialization.
* **`onChange`:** (optional, *function*) The function to call when the control's state is changed.

#### Examples:

```
// Setting up a basic list control for the settings property 'difficulty'
Setting.addList("difficulty", {
	label   : "Choose a difficulty level.",
	list    : ["Easy", "Normal", "Hard", "INSANE"],
	default : "Normal"
});

// Setting up a list control for the settings property 'theme' w/ callbacks
var settingThemeNames = ["(none)", "Bright Lights", "Charcoal", "Midnight", "Tinsel City"];
var settingThemeHandler = function () {
	// cache the jQuery-wrapped <html> element
	var $html = $("html");

	// remove any existing theme class
	$html.removeClass("theme-bright-lights theme-charcoal theme-midnight theme-tinsel-city");

	// switch on the theme name to add the requested theme class
	switch (settings.theme) {
	case "Bright Lights":
		$html.addClass("theme-bright-lights");
		break;
	case "Charcoal":
		$html.addClass("theme-charcoal");
		break;
	case "Midnight":
		$html.addClass("theme-midnight");
		break;
	case "Tinsel City":
		$html.addClass("theme-tinsel-city");
		break;
	}
};
Setting.addList("theme", {
	label    : "Choose a theme.",
	list     : settingThemeNames,
	onInit   : settingThemeHandler,
	onChange : settingThemeHandler
}); // default value not defined, so the first array member "(none)" is used
```

### `Setting.addRange(name, definition)` {#setting-api-method-addrange}

Adds the named property to the `settings` object and a range control for it to the Settings dialog.

#### History:

* `v2.26.0`: Introduced.

#### Parameters:

* **`name`:** (*string*) Name of the `settings` property to add, which the control will manage.
* **`definition`:** (*object*) Definition of the control.

#### Definition object:

A range definition object should have some of the following properties:

* **`label`:** (*string*) Label to use for the control.
* **`min`:** (*number*) The minimum value.
* **`max`:** (*number*) The maximum value.
* **`step`:** (*number*) Limits the increments to which the value may be set.  It must be evenly divisible into the full range—i.e., `max - min`.
* **`desc`:** (optional, *string*) Description explaining the control in greater detail.
* **`default`:** (optional, *number*) The default value for the setting and default state of the control.  Leaving it undefined means to use the value of `max` as the default.
* **`onInit`:** (optional, *function*) The function to call during initialization.
* **`onChange`:** (optional, *function*) The function to call when the control's state is changed.

#### Examples:

```
// Setting up a volume control for the settings property 'masterVolume' w/ callback
Setting.addRange("masterVolume", {
	label    : "Master volume.",
	min      : 0,
	max      : 10,
	step     : 1,
	onChange : function () {
		SimpleAudio.volume(settings.masterVolume / 10);
	}
}); // default value not defined, so max value (10) is used
```

### `Setting.load()` {#setting-api-method-load}

Loads the settings from storage.

Note:
The API automatically calls this method at startup, so you should never need to call this method manually.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Setting.load();
```

### `Setting.reset([name])` {#setting-api-method-reset}

Resets the setting with the given name to its default value.  If no name is given, resets all settings.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`name`:** (optional, *string*) Name of the `settings` object property to reset.

#### Examples:

```
// Reset the setting 'difficulty'
Setting.reset("difficulty");

// Reset all settings
Setting.reset();
```

### `Setting.save()` {#setting-api-method-save}

Saves the settings to storage.

Note:
The controls of the Settings dialog automatically call this method when settings are changed, so you should normally never need to call this method manually.  Only when manually modifying the values of settings object properties, outside of the controls, would you need to call this method.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
Setting.save();
```

### `settings` object {#setting-api-object-settings}

A prototype-less generic object whose properties and values are defined by the [`Setting.addToggle()`](#setting-api-method-addtoggle), [`Setting.addList()`](#setting-api-method-addlist), and [`Setting.addRange()`](#setting-api-method-addrange) methods.

Normally, the values of its properties are automatically managed by their associated Settings dialog control.  If necessary, however, you may manually change their values—n.b. you'll need to call the [`Setting.save()`](#setting-api-method-save) after having done so.

#### History:

* `v2.0.0`: Introduced.


code:
# `AudioList` API {#audiolist-api}

Audio lists (playlists) are useful for playing tracks in a sequence—i.e., one after another.

See Also:
SimpleAudio API, AudioTrack API, and AudioRunner API.

### `<AudioList>.duration()` → *number* {#audiolist-api-prototype-method-duration}

Returns the playlist's total playtime in seconds, `Infinity` if it contains any streams, or `NaN` if no metadata exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var listLength = aList.duration();
```

### `<AudioList>.fade(duration , toVol [, fromVol])` → *`Promise` object* {#audiolist-api-prototype-method-fade}

Starts playback of the playlist and fades the currently playing track between the specified starting and destination volume levels over the specified number of seconds.

Note:
The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

#### History:

* `v2.28.0`: Introduced.
* `v2.29.0`: Updated to return a `Promise`.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the currently playing track should be faded.
* **`toVol`:** (*number*) The destination volume level.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the currently playing track's current volume level.

#### Examples:

```
// Fade the playlist from volume 0 to 1 over 6 seconds.
aList.fade(6, 1, 0);
```

### `<AudioList>.fadeIn(duration [, fromVol])` → *`Promise` object* {#audiolist-api-prototype-method-fadein}

Starts playback of the playlist and fades the currently playing track from the specified volume level to `1` (loudest) over the specified number of seconds.

#### History:

* `v2.28.0`: Introduced.
* `v2.29.0`: Updated to return a `Promise`.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the currently playing track should be faded.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the currently playing track's current volume level.

#### Examples:

```
// Fade the playlist in from volume 0 over 5 seconds.
aList.fadeIn(5, 0);
```

### `<AudioList>.fadeOut(duration [, fromVol])` → *`Promise` object* {#audiolist-api-prototype-method-fadeout}

Starts playback of the playlist and fades the currently playing track from the specified volume level to `0` (silent) over the specified number of seconds.

Note:
The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

#### History:

* `v2.28.0`: Introduced.
* `v2.29.0`: Updated to return a `Promise`.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the currently playing track should be faded.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the currently playing track's current volume level.

#### Examples:

```
// Fade the playlist out from volume 1 over 8 seconds.
aList.fadeOut(8, 1);
```

### `<AudioList>.fadeStop()` {#audiolist-api-prototype-method-fadestop}

Interrupts an in-progress fade of the currently playing track, or does nothing if no fade is progressing.

Note:
This does not alter the volume level.

#### History:

* `v2.29.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aList.fadeStop();
```

### `<AudioList>.isEnded()` → *boolean* {#audiolist-api-prototype-method-isended}

Returns whether playback of the playlist has ended.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aList.isEnded()) {
	/* do something */
}
```

### `<AudioList>.isFading()` → *boolean* {#audiolist-api-prototype-method-isfading}

Returns whether a fade is in-progress on the currently playing track.

#### History:

* `v2.29.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aList.isFading()) {
	/* do something */
}
```

### `<AudioList>.isPaused()` → *boolean* {#audiolist-api-prototype-method-ispaused}

Returns whether playback of the playlist has been paused.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aList.isPaused()) {
	/* do something */
}
```

### `<AudioList>.isPlaying()` → *boolean* {#audiolist-api-prototype-method-isplaying}

Returns whether the playlist is playing.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aList.isPlaying()) {
	/* do something */
}
```

### `<AudioList>.isStopped()` → *boolean* {#audiolist-api-prototype-method-isstopped}

Returns whether playback of the playlist has been stopped.

#### History:

* `v2.29.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aList.isStopped()) {
	/* do something */
}
```

### `<AudioList>.load()` {#audiolist-api-prototype-method-load}

Pauses playback of the playlist and, if they're not already in the process of loading, forces its tracks to drop any existing data and begin loading.

Warning:
This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aList.load();
```

### `<AudioList>.loop([state])` → **get:** *boolean* | **set:** *`AudioList` object* {#audiolist-api-prototype-method-loop}

Gets or sets the playlist's repeating playback state (default: `false`).  When used to set the loop state, returns a reference to the current `AudioList` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (optional, *boolean*) The loop state.

#### Examples:

```
// Get the playlist's current loop state.
var isLooped = aList.loop();

// Loop the playlist.
aList.loop(true);

// Unloop the playlist.
aList.loop(false);
```

### `<AudioList>.mute([state])` → **get:** *boolean* | **set:** *`AudioList` object* {#audiolist-api-prototype-method-mute}

Gets or sets the playlist's volume mute state (default: `false`).  When used to set the mute state, returns a reference to the current `AudioList` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (optional, *boolean*) The mute state.

#### Examples:

```
// Get the playlist's current volume mute state.
var isMuted = aList.mute();

// Mute the playlist's volume.
aList.mute(true);

// Unmute the playlist's volume.
aList.mute(false);
```

### `<AudioList>.pause()` {#audiolist-api-prototype-method-pause}

Pauses playback of the playlist.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aList.pause();
```

### `<AudioList>.play()` → *`Promise` object* {#audiolist-api-prototype-method-play}

Begins playback of the playlist.

#### History:

* `v2.28.0`: Introduced.
* `v2.29.0`: Updated to return a `Promise`.

#### Parameters: *none*

#### Examples:

##### Basic usage

```
aList.play();
```

##### Using the returned `Promise`

```
aList.play()
	.then(function () {
		console.log('The playlist is playing.');
	})
	.catch(function (problem) {
		console.warn('There was a problem with playback: ' + problem);
	});
```

### `<AudioList>.playWhenAllowed()` {#audiolist-api-prototype-method-playwhenallowed}

Begins playback of the playlist or, failing that, sets the playlist to begin playback as soon as the player has interacted with the document.

#### History:

* `v2.29.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aList.playWhenAllowed();
```

### `<AudioList>.remaining()` → *number* {#audiolist-api-prototype-method-remaining}

Returns how much remains of the playlist's total playtime in seconds, `Infinity` if it contains any streams, or `NaN` if no metadata exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var listRemains = aList.remaining();
```

### `<AudioList>.shuffle([state])` → **get:** *boolean* | **set:** *`AudioList` object* {#audiolist-api-prototype-method-shuffle}

Gets or sets the playlist's randomly shuffled playback state (default: `false`).  When used to set the shuffle state, returns a reference to the current `AudioList` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (optional, *boolean*) The shuffle state.

#### Examples:

```
// Get the playlist's current shuffle state.
var isShuffled = aList.shuffle();

// Enable shuffling of the playlist.
aList.shuffle(true);

// Disable shuffling of the playlist.
aList.shuffle(false);
```

### `<AudioList>.skip()` {#audiolist-api-prototype-method-skip}

Skips ahead to the next track in the playlist, if any.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTrack.skip();
```

### `<AudioList>.stop()` {#audiolist-api-prototype-method-stop}

Stops playback of the playlist.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTrack.stop();
```

### `<AudioList>.time()` → *number* {#audiolist-api-prototype-method-time}

Returns the playlist's current time in seconds, or `NaN` if no metadata exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var listTime = aList.time();
```

### `<AudioList>.unload()` {#audiolist-api-prototype-method-unload}

Stops playback of the playlist and forces its tracks to drop any existing data.

Note:
Once unloaded, playback cannot occur until the track's data is loaded again.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aList.unload();
```

### `<AudioList>.volume([level])` → **get:** *number* | **set:** *`AudioList` object* {#audiolist-api-prototype-method-volume}

Gets or sets the playlist's volume level (default: `1`).  When used to set the volume, returns a reference to the current `AudioList` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`level`:** (optional, *number*) The volume level to set.  Valid values are floating-point numbers in the range `0` (silent) to `1` (loudest)—e.g., `0` is 0%, `0.5` is 50%, `1` is 100%.

#### Examples:

```
// Get the playlist's current volume level.
var trackVolume = aList.volume();

// Set the playlist's volume level to 75%.
aList.volume(0.75);
```


code:
# `AudioRunner` API {#audiorunner-api}

Audio runners are useful for performing actions on multiple tracks at once.

See Also:
SimpleAudio API, AudioTrack API, and AudioList API.

### `<AudioRunner>.fade(duration , toVol [, fromVol])` {#audiorunner-api-prototype-method-fade}

Starts playback of the selected tracks and fades them between the specified starting and destination volume levels over the specified number of seconds.

Note:
The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the tracks should be faded.
* **`toVol`:** (*number*) The destination volume level.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the tracks' current volume level.

#### Examples:

```
// Fade the selected tracks from volume 0 to 1 over 6 seconds.
someTracks.fade(6, 1, 0);
```

### `<AudioRunner>.fadeIn(duration [, fromVol])` {#audiorunner-api-prototype-method-fadein}

Starts playback of the selected tracks and fades them from the specified volume level to `1` (loudest) over the specified number of seconds.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the tracks should be faded.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the tracks' current volume level.

#### Examples:

```
// Fade the selected tracks in from volume 0 over 5 seconds.
someTracks.fadeIn(5, 0);
```

### `<AudioRunner>.fadeOut(duration [, fromVol])` {#audiorunner-api-prototype-method-fadeout}

Starts playback of the selected tracks and fades them from the specified volume level to `0` (silent) over the specified number of seconds.

Note:
The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the tracks should be faded.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the tracks' current volume level.

#### Examples:

```
// Fade the selected tracks out from volume 1 over 8 seconds.
someTracks.fadeOut(8, 1);
```

### `<AudioRunner>.fadeStop()` {#audiorunner-api-prototype-method-fadestop}

Interrupts an in-progress fade of the selected tracks, or does nothing if no fade is progressing.

Note:
This does not alter the volume level.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTracks.fadeStop();
```

### `<AudioRunner>.load()` {#audiorunner-api-prototype-method-load}

Pauses playback of the selected tracks and, if they're not already in the process of loading, forces them to drop any existing data and begin loading.

Warning:
This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTracks.load();
```

### `<AudioRunner>.loop(state)` → *`AudioRunner` object* {#audiorunner-api-prototype-method-loop}

Sets the selected tracks' repeating playback state (default: `false`).  Returns a reference to the current `AudioRunner` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (*boolean*) The loop state.

#### Examples:

```
// Loop the selected tracks.
someTracks.loop(true);

// Unloop the selected tracks.
someTracks.loop(false);
```

### `<AudioRunner>.mute(state)` → *`AudioRunner` object* {#audiorunner-api-prototype-method-mute}

Sets the selected tracks' volume mute state (default: `false`).  Returns a reference to the current `AudioRunner` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (*boolean*) The mute state.

#### Examples:

```
// Mute the selected tracks' volume.
someTracks.mute(true);

// Unmute the selected tracks' volume.
someTracks.mute(false);
```

### `<AudioRunner>.off(...args)` → *`AudioRunner` object* {#audiorunner-api-prototype-method-off}

Removes event handlers from the selected tracks.  Returns a reference to the current `AudioRunner` instance for chaining.

Note:
Shorthand for jQuery's .off() method applied to each of the audio elements.

Warning:
The SimpleAudio APIs use events internally for various pieces of functionality.  To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers.  It is further strongly suggested that you provide that same custom user namespace when removing them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

See:
<jQuery>.off() in the jQuery API docs for more information.

#### Examples:

```
// Remove any handlers for the ended event.
someTracks.off('ended.myEvents');
```

### `<AudioRunner>.on(...args)` → *`AudioRunner` object* {#audiorunner-api-prototype-method-on}

Attaches event handlers to the selected tracks.  Returns a reference to the current `AudioRunner` instance for chaining.

Note:
Shorthand for jQuery's .on() method applied to each of the audio elements.

Warning:
The SimpleAudio APIs use events internally for various pieces of functionality.  To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers.  It is further strongly suggested that you provide that same custom user namespace when removing them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

See:
<jQuery>.on() in the jQuery API docs for more information.

#### Examples:

```
// Add a handler for the ended event.
someTracks.on('ended.myEvents', function () {
	/* do something */
});
```

### `<AudioRunner>.one(...args)` → *`AudioRunner` object* {#audiorunner-api-prototype-method-one}

Attaches single-use event handlers to the selected tracks.  Returns a reference to the current `AudioRunner` instance for chaining.

Note:
Shorthand for jQuery's .one() method applied to each of the audio elements.

Warning:
The SimpleAudio APIs use events internally for various pieces of functionality.  To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers.  It is further strongly suggested that you provide that same custom user namespace when removing them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

See:
<jQuery>.one() in the jQuery API docs for more information.

#### Examples:

```
// Add a single-use handler for the ended event.
someTracks.one('ended.myEvents', function () {
	/* do something */
});
```

### `<AudioRunner>.pause()` {#audiorunner-api-prototype-method-pause}

Pauses playback of the selected tracks.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTracks.pause();
```

### `<AudioRunner>.play()` {#audiorunner-api-prototype-method-play}

Begins playback of the selected tracks.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTracks.play();
```

### `<AudioRunner>.playWhenAllowed()` {#audiorunner-api-prototype-method-playwhenallowed}

Begins playback of the selected tracks or, failing that, sets the tracks to begin playback as soon as the player has interacted with the document.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTracks.playWhenAllowed();
```

### `<AudioRunner>.stop()` {#audiorunner-api-prototype-method-stop}

Stops playback of the selected tracks.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTracks.stop();
```

### `<AudioRunner>.time(seconds)` → *`AudioRunner` object* {#audiorunner-api-prototype-method-time}

Sets the selected tracks' current time in seconds.  Returns a reference to the current `AudioRunner` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`seconds`:** (*number*) The time to set.  Valid values are floating-point numbers in the range `0` (start) to the maximum duration—e.g., `60` is `60` is sixty seconds in, `90.5` is ninety-point-five seconds in.

#### Examples:

```
// Set the selected tracks' current time to 30 seconds from their beginning.
someTracks.time(30);
```

### `<AudioRunner>.unload()` {#audiorunner-api-prototype-method-unload}

Stops playback of the selected tracks and forces them to drop any existing data.

Note:
Once unloaded, playback cannot occur until the selected tracks' data is loaded again.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTracks.unload();
```

### `<AudioRunner>.volume(level)` → *`AudioRunner` object* {#audiorunner-api-prototype-method-volume}

Sets the selected tracks' volume level (default: `1`).  Returns a reference to the current `AudioRunner` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`level`:** (*number*) The volume level to set.  Valid values are floating-point numbers in the range `0` (silent) to `1` (loudest)—e.g., `0` is 0%, `0.5` is 50%, `1` is 100%.

#### Examples:

```
// Set the selected tracks' volume level to 75%.
someTracks.volume(0.75);
```


code:
# `AudioTrack` API {#audiotrack-api}

Audio tracks encapsulate and provide a consistent interface to an audio resource.

See Also:
SimpleAudio API, AudioRunner API, and AudioList API.

### `<AudioTrack>.clone()` → *`AudioTrack` object* {#audiotrack-api-prototype-method-clone}

Returns a new independent copy of the track.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var trackCopy = aTrack.clone();
```

### `<AudioTrack>.duration()` → *number* {#audiotrack-api-prototype-method-duration}

Returns the track's total playtime in seconds, `Infinity` for a stream, or `NaN` if no metadata exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var trackLength = aTrack.duration();
```

### `<AudioTrack>.fade(duration , toVol [, fromVol])` → *`Promise` object* {#audiotrack-api-prototype-method-fade}

Starts playback of the track and fades it between the specified starting and destination volume levels over the specified number of seconds.

Note:
The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the track should be faded.
* **`toVol`:** (*number*) The destination volume level.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the track's current volume level.

#### Examples:

```
// Fade the track from volume 0 to 1 over 6 seconds.
aTrack.fade(6, 1, 0);
```

### `<AudioTrack>.fadeIn(duration [, fromVol])` → *`Promise` object* {#audiotrack-api-prototype-method-fadein}

Starts playback of the track and fades it from the specified volume level to `1` (loudest) over the specified number of seconds.

#### History:

* `v2.28.0`: Introduced.
* `v2.29.0`: Updated to return a `Promise`.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the track should be faded.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the track's current volume level.

#### Examples:

```
// Fade the track in from volume 0 over 5 seconds.
aTrack.fadeIn(5, 0);
```

### `<AudioTrack>.fadeOut(duration [, fromVol])` → *`Promise` object* {#audiotrack-api-prototype-method-fadeout}

Starts playback of the track and fades it from the specified volume level to `0` (silent) over the specified number of seconds.

Note:
The Config.audio.pauseOnFadeToZero setting (default: true) determines whether the audio subsystem automatically pauses tracks that have been faded to 0 volume (silent).

#### History:

* `v2.28.0`: Introduced.
* `v2.29.0`: Updated to return a `Promise`.

#### Parameters:

* **`duration`:** (*number*) The number of seconds over which the track should be faded.
* **`fromVol`:** (optional, *number*) The starting volume level.  If omitted, defaults to the track's current volume level.

#### Examples:

```
// Fade the track out from volume 1 over 8 seconds.
aTrack.fadeOut(8, 1);
```

### `<AudioTrack>.fadeStop()` {#audiotrack-api-prototype-method-fadestop}

Interrupts an in-progress fade of the track, or does nothing if no fade is progressing.

Note:
This does not alter the volume level.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aTrack.fadeStop();
```

### `<AudioTrack>.hasData()` → *boolean* {#audiotrack-api-prototype-method-hasdata}

Returns whether enough data has been loaded to play the track through to the end without interruption.

Note:
This is an estimate calculated by the browser based upon the currently downloaded data and the download rate.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.hasData()) {
	/* do something */
}
```

### `<AudioTrack>.hasMetadata()` → *boolean* {#audiotrack-api-prototype-method-hasmetadata}

Returns whether, at least, the track's metadata has been loaded.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.hasMetadata()) {
	/* do something */
}
```

### `<AudioTrack>.hasNoData()` → *boolean* {#audiotrack-api-prototype-method-hasnodata}

Returns whether none of the track's data has been loaded.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.hasNoData()) {
	/* do something */
}
```

### `<AudioTrack>.hasSomeData()` → *boolean* {#audiotrack-api-prototype-method-hassomedata}

Returns whether, at least, some of the track's data has been loaded.

Tip:
The <AudioTrack>.hasData() method is generally more useful.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.hasSomeData()) {
	/* do something */
}
```

### `<AudioTrack>.hasSource()` → *boolean* {#audiotrack-api-prototype-method-hassource}

Returns whether any valid sources were registered.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.hasSource()) {
	/* do something */
}
```

### `<AudioTrack>.isEnded()` → *boolean* {#audiotrack-api-prototype-method-isended}

Returns whether playback of the track has ended.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isEnded()) {
	/* do something */
}
```

### `<AudioTrack>.isFading()` → *boolean* {#audiotrack-api-prototype-method-isfading}

Returns whether a fade is in-progress on the track.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isFading()) {
	/* do something */
}
```

### `<AudioTrack>.isFailed()` → *boolean* {#audiotrack-api-prototype-method-isfailed}

Returns whether an error has occurred.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isFailed()) {
	/* do something */
}
```

### `<AudioTrack>.isLoading()` → *boolean* {#audiotrack-api-prototype-method-isloading}

Returns whether the track is loading data.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isLoading()) {
	/* do something */
}
```

### `<AudioTrack>.isPaused()` → *boolean* {#audiotrack-api-prototype-method-ispaused}

Returns whether playback of the track has been paused.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isPaused()) {
	/* do something */
}
```

### `<AudioTrack>.isPlaying()` → *boolean* {#audiotrack-api-prototype-method-isplaying}

Returns whether the track is playing.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isPlaying()) {
	/* do something */
}
```

### `<AudioTrack>.isSeeking()` → *boolean* {#audiotrack-api-prototype-method-isseeking}

Returns whether the track is seeking.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isSeeking()) {
	/* do something */
}
```

### `<AudioTrack>.isStopped()` → *boolean* {#audiotrack-api-prototype-method-isstopped}

Returns whether playback of the track has been stopped.

#### History:

* `v2.29.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isStopped()) {
	/* do something */
}
```

### `<AudioTrack>.isUnavailable()` → *boolean* {#audiotrack-api-prototype-method-isunavailable}

Returns whether the track is currently unavailable for playback.  Possible reasons include: no valid sources are registered, no sources are currently loaded, an error has occurred.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isUnavailable()) {
	/* do something */
}
```

### `<AudioTrack>.isUnloaded()` → *boolean* {#audiotrack-api-prototype-method-isunloaded}

Returns whether the track's sources are currently unloaded.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (aTrack.isUnloaded()) {
	/* do something */
}
```

### `<AudioTrack>.load()` {#audiotrack-api-prototype-method-load}

Pauses playback of the track and, if it's not already in the process of loading, forces it to drop any existing data and begin loading.

Warning:
This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aTrack.load();
```

### `<AudioTrack>.loop([state])` → **get:** *boolean* | **set:** *`AudioTrack` object* {#audiotrack-api-prototype-method-loop}

Gets or sets the track's repeating playback state (default: `false`).  When used to set the loop state, returns a reference to the current `AudioTrack` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (optional, *boolean*) The loop state.

#### Examples:

```
// Get the track's current loop state.
var isLooped = aTrack.loop();

// Loop the track.
aTrack.loop(true);

// Unloop the track.
aTrack.loop(false);
```

### `<AudioTrack>.mute([state])` → **get:** *boolean* | **set:** *`AudioTrack` object* {#audiotrack-api-prototype-method-mute}

Gets or sets the track's volume mute state (default: `false`).  When used to set the mute state, returns a reference to the current `AudioTrack` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (optional, *boolean*) The mute state.

#### Examples:

```
// Get the track's current volume mute state.
var isMuted = aTrack.mute();

// Mute the track's volume.
aTrack.mute(true);

// Unmute the track's volume.
aTrack.mute(false);
```

### `<AudioTrack>.off(...args)` → *`AudioTrack` object* {#audiotrack-api-prototype-method-off}

Removes event handlers from the track.  Returns a reference to the current `AudioTrack` instance for chaining.

Note:
Shorthand for jQuery's .off() method applied to the audio element.

Warning:
The SimpleAudio APIs use events internally for various pieces of functionality.  To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers.  It is further strongly suggested that you provide that same custom user namespace when removing them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

See:
<jQuery>.off() in the jQuery API docs for more information.

#### Examples:

```
// Remove any handlers for the ended event.
aTrack.off('ended.myEvents');
```

### `<AudioTrack>.on(...args)` → *`AudioTrack` object* {#audiotrack-api-prototype-method-on}

Attaches event handlers to the track.  Returns a reference to the current `AudioTrack` instance for chaining.

Note:
Shorthand for jQuery's .on() method applied to the audio element.

Warning:
The SimpleAudio APIs use events internally for various pieces of functionality.  To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers.  It is further strongly suggested that you provide that same custom user namespace when removing them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

See:
<jQuery>.on() in the jQuery API docs for more information.

#### Examples:

```
// Add a handler for the ended event.
aTrack.on('ended.myEvents', function () {
	/* do something */
});
```

### `<AudioTrack>.one(...args)` → *`AudioTrack` object* {#audiotrack-api-prototype-method-one}

Attaches single-use event handlers to the track.  Returns a reference to the current `AudioTrack` instance for chaining.

Note:
Shorthand for jQuery's .one() method applied to the audio element.

Warning:
The SimpleAudio APIs use events internally for various pieces of functionality.  To prevent conflicts, it is strongly suggested that you specify a custom user namespace—e.g., .myEvents—when attaching your own handlers.  It is further strongly suggested that you provide that same custom user namespace when removing them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

See:
<jQuery>.one() in the jQuery API docs for more information.

#### Examples:

```
// Add a single-use handler for the ended event.
aTrack.one('ended.myEvents', function () {
	/* do something */
});
```

### `<AudioTrack>.pause()` {#audiotrack-api-prototype-method-pause}

Pauses playback of the track.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aTrack.pause();
```

### `<AudioTrack>.play()` → *`Promise` object* {#audiotrack-api-prototype-method-play}

Begins playback of the track.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

##### Basic usage

```
aTrack.play();
```

##### Using the returned `Promise`

```
aTrack.play()
	.then(function () {
		console.log('The track is playing.');
	})
	.catch(function (problem) {
		console.warn('There was a problem with playback: ' + problem);
	});
```

### `<AudioTrack>.playWhenAllowed()` {#audiotrack-api-prototype-method-playwhenallowed}

Begins playback of the track or, failing that, sets the track to begin playback as soon as the player has interacted with the document.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aTrack.playWhenAllowed();
```

### `<AudioTrack>.remaining()` → *number* {#audiotrack-api-prototype-method-remaining}

Returns how much remains of the track's total playtime in seconds, `Infinity` for a stream, or `NaN` if no metadata exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
var trackRemains = aTrack.remaining();
```

### `<AudioTrack>.stop()` {#audiotrack-api-prototype-method-stop}

Stops playback of the track.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
someTrack.stop();
```

### `<AudioTrack>.time([seconds])` → **get:** *number* | **set:** *`AudioTrack` object* {#audiotrack-api-prototype-method-time}

Gets or sets the track's current time in seconds.  When used to set a value, returns a reference to the current `AudioTrack` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`seconds`:** (optional, *number*) The time to set.  Valid values are floating-point numbers in the range `0` (start) to the maximum duration—e.g., `60` is `60` is sixty seconds in, `90.5` is ninety-point-five seconds in.

#### Examples:

```
// Get the track's current time.
var trackTime = aTrack.time();

// Set the track's current time to 30 seconds from its beginning.
aTrack.time(30);

// Set the track's current time to 30 seconds from its end.
aTrack.time(aTrack.duration() - 30);
```

### `<AudioTrack>.unload()` {#audiotrack-api-prototype-method-unload}

Stops playback of the track and forces it to drop any existing data.

Note:
Once unloaded, playback cannot occur until the track's data is loaded again.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
aTrack.unload();
```

### `<AudioTrack>.volume([level])` → **get:** *number* | **set:** *`AudioTrack` object* {#audiotrack-api-prototype-method-volume}

Gets or sets the track's volume level (default: `1`).  When used to set the volume, returns a reference to the current `AudioTrack` instance for chaining.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`level`:** (optional, *number*) The volume level to set.  Valid values are floating-point numbers in the range `0` (silent) to `1` (loudest)—e.g., `0` is 0%, `0.5` is 50%, `1` is 100%.

#### Examples:

```
// Get the track's current volume level.
var trackVolume = aTrack.volume();

// Set the track's volume level to 75%.
aTrack.volume(0.75);
```


code:
# `SimpleAudio` API {#simpleaudio-api}

The core audio subsystem and backend for the [audio macros](#macros-audio).

See Also:
AudioTrack API, AudioRunner API, and AudioList API.

#### Audio limitations {#simpleaudio-api-limitations}

The audio subsystem is based upon the HTML Media Elements APIs and comes with some built-in limitations:

1. True gapless transitions between tracks is not supported.
2. In mobile browsers, playback volume is controlled by the device hardware.  Thus, all volume adjustments are ignored by the device, though muting should work normally.
3. In mobile browsers and, more recently, most desktop browsers, playback must be initiated by the player—generally via click/touch.  In these cases, audio will not automatically play on the starting passage, nor is it likely to play if initiated from within asynchronous code—e.g., via `<<timed>>`—though this ultimately depends on various factors.  A simple solution for the former is to use some kind of click/touch-through screen—e.g., a splash screen, which the player goes through to the real starting passage.  The latter is harder to resolve, so is best avoided.
4. The load and playback states of tracks are not currently recorded within the active play session or saves.  Thus, if you need either to be recoverable, then you'll have to handle that yourself.

## General {#simpleaudio-api-general}

### `SimpleAudio.load()` {#simpleaudio-api-method-load}

Pauses playback of *all* currently registered tracks and, if they're not already in the process of loading, force them to drop any existing data and begin loading.

Warning:
This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
SimpleAudio.load();
```

### `SimpleAudio.loadWithScreen()` {#simpleaudio-api-method-loadwithscreen}

Displays the loading screen until *all* currently registered audio tracks have either loaded to a playable state or aborted loading due to errors.  The loading process is as described in [`SimpleAudio.load()`](#simpleaudio-api-method-load).

Warning:
This should not be done lightly if your audio sources are on the network, as it forces players to begin downloading them.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
SimpleAudio.loadWithScreen();
```

### `SimpleAudio.mute([state])` → **get:** *boolean* | **set:** *undefined* {#simpleaudio-api-method-mute}

Gets or sets the mute state for the master volume (default: `false`).

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (optional, *boolean*) The mute state.

#### Examples:

```
// Get the current master volume mute state.
var isMuted = SimpleAudio.mute();

// Mute the master volume.
SimpleAudio.mute(true);

// Unmute the master volume.
SimpleAudio.mute(false);
```

### `SimpleAudio.muteOnHidden([state])` → **get:** *boolean* | **set:** *undefined* {#simpleaudio-api-method-muteonhidden}

Gets or sets the mute-on-hidden state for the master volume (default: `false`).  The mute-on-hidden state controls whether the master volume is automatically muted/unmuted when the story's browser tab loses/gains visibility.  Loss of visibility is defined as when the browser window is either switched to another tab or minimized.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`state`:** (optional, *boolean*) The mute-on-hidden state.

#### Examples:

```
// Get the current master volume mute-on-hidden state.
var isMuteOnHidden = SimpleAudio.muteOnHidden();

// Enable automatic muting of the master volume when visibility is lost.
SimpleAudio.muteOnHidden(true);

// Disable automatic muting of the master volume when visibility is lost.
SimpleAudio.muteOnHidden(false);
```

### `SimpleAudio.select(selector)` → *`AudioRunner` object* {#simpleaudio-api-method-select}

Returns an [`AudioRunner` instance](#audiorunner-api) for the tracks matching the given selector.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`selector`:** (*string*) The list of audio track(s) and/or group ID(s), separated by spaces.  There are several predefined group IDs (`:all`, `:looped`, `:muted`, `:paused`, `:playing`).  The `:not()` group modifier syntax (`groupId:not(selector)`) allows a group to have some of its tracks excluded from selection.

#### Examples:

##### Basic usage

```
SimpleAudio.select(":ui")  → Returns the AudioRunner instance for the tracks matching ":ui"
```

##### Typical usage

```
// Return the AudioTrack instance matching "swamped" and do something with it
SimpleAudio.select("swamped").volume(1).play();

// Start playback of paused audio tracks
SimpleAudio.select(":paused").play();

// Pause playback of playing audio tracks
SimpleAudio.select(":playing").pause();

// Stop playback of playing audio tracks
SimpleAudio.select(":playing").stop();

// Stop playback of all audio tracks (not uniquely part of a playlist)
SimpleAudio.select(":all").stop();

// Stop playback of playing audio tracks except those in the ":ui" group
SimpleAudio.select(":playing:not(:ui)").stop();

// Change the volume of all audio tracks except those in the ":ui" group
// to 40%, without changing the current playback state
SimpleAudio.select(":all:not(:ui)").volume(0.40);
```

### `SimpleAudio.stop()` {#simpleaudio-api-method-stop}

Stops playback of *all* currently registered tracks.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
SimpleAudio.stop();
```

### `SimpleAudio.unload()` {#simpleaudio-api-method-unload}

Stops playback of *all* currently registered tracks and force them to drop any existing data.

Note:
Once a track has been unloaded, playback cannot occur until it is reloaded.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
SimpleAudio.unload();
```

### `SimpleAudio.volume([level])` → **get:** *number* | **set:** *undefined* {#simpleaudio-api-method-volume}

Gets or sets the master volume level (default: `1`).

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`level`:** (optional, *number*) The volume level to set.  Valid values are floating-point numbers in the range `0` (silent) to `1` (loudest)—e.g., `0` is 0%, `0.5` is 50%, `1` is 100%.

#### Examples:

```
// Get the current master volume level.
var currentMasterVolume = SimpleAudio.volume();

// Set the master volume level to 75%.
SimpleAudio.volume(0.75);
```

## Tracks {#simpleaudio-api-tracks}

### `SimpleAudio.tracks.add(trackId, sources…)` {#simpleaudio-api-method-tracks-add}

Adds an audio track with the given track ID.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`trackId`:** (*string*) The ID of the track, which will be used to reference it.
* **`sources`:** (*string*… | *Array<string>*) The audio sources for the track, which may be a list of sources or an array.  Only one is required, though supplying additional sources in differing formats is recommended, as no single format is supported by all browsers.  A source must be either a URL (absolute or relative) to an audio resource, the name of an audio passage, or a data URI.  In rare cases where the audio format cannot be automatically detected from the source (URLs are parsed for a file extension, data URIs are parsed for the media type), a format specifier may be prepended to the front of each source to manually specify the format (syntax: `formatId|`, where `formatId` is the audio format—generally, whatever the file extension would normally be; e.g., `mp3`, `mp4`, `ogg`, `weba`, `wav`).

#### Examples:

```
// Cache a track with the ID "boom" and one source via relative URL
SimpleAudio.tracks.add("boom", "media/audio/explosion.mp3");

// Cache a track with the ID "boom" and one source via audio passage
SimpleAudio.tracks.add("boom", "explosion");

// Cache a track with the ID "bgm_space" and two sources via relative URLs
SimpleAudio.tracks.add("bgm_space", "media/audio/space_quest.mp3", "media/audio/space_quest.ogg");

// Cache a track with the ID "what" and one source via URL with a format specifier
SimpleAudio.tracks.add("what", "mp3|http://an-audio-service.com/a-user/a-track-id");
```

### `SimpleAudio.tracks.clear()` {#simpleaudio-api-method-tracks-clear}

Deletes all audio tracks.

Note:
Cannot delete tracks solely under the control of a playlist.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
SimpleAudio.tracks.clear();
```

### `SimpleAudio.tracks.delete(trackId)` {#simpleaudio-api-method-tracks-delete}

Deletes the audio track with the given track ID.

Note:
Cannot delete tracks solely under the control of a playlist.

Warning:
Does not currently remove the track from either groups or playlists.  Thus, any groups or playlists containing the deleted track should be rebuilt.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`trackId`:** (*string*) The ID of the track.

#### Examples:

```
SimpleAudio.tracks.delete("bgm_space");
```

### `SimpleAudio.tracks.get(trackId)` → *`AudioTrack` object* | *null* {#simpleaudio-api-method-tracks-get}

Returns the [`AudioTrack` instance](#audiotrack-api) with the given track ID, or `null` on failure.

Note:
To affect multiple tracks and/or groups at once, see the SimpleAudio.select() method.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`trackId`:** (*string*) The ID of the track.

#### Examples:

##### Basic usage

```
SimpleAudio.tracks.get("swamped")  → Returns the AudioTrack instance matching "swamped"
```

##### Typical usage

```
// Return the AudioTrack instance matching "swamped" and do something with it
SimpleAudio.tracks.get("swamped").volume(1).play();
```

### `SimpleAudio.tracks.has(trackId)` → *boolean* {#simpleaudio-api-method-tracks-has}

Returns whether an audio track with the given track ID exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`trackId`:** (*string*) The ID of the track.

#### Examples:

```
if (SimpleAudio.tracks.has("bgm_space")) {
	// Track "bgm_space" exists.
}
```

## Groups {#simpleaudio-api-groups}

### `SimpleAudio.groups.add(groupId, trackIds…)` {#simpleaudio-api-method-groups-add}

Adds an audio group with the given group ID.  Groups are useful for applying actions to multiple tracks simultaneously and/or excluding the included tracks from a larger set when applying actions.

Note:
If you want to play tracks in a sequence, then you want a playlist instead.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`groupId`:** (*string*) The ID of the group, which will be used to reference it and *must* begin with a colon.  **NOTE:** There are several predefined group IDs (`:all`, `:looped`, `:muted`, `:paused`, `:playing`) and the `:not` group modifier that cannot be reused/overwritten.
* **`trackIds`:** (*string*… | *Array<string>*) The IDs of the tracks to make part of the group, which may be a list of track IDs or an array.

#### Examples:

```
// Set up a group ":ui" with the tracks: "ui_beep", "ui_boop", and "ui_swish"
SimpleAudio.groups.add(":ui", "ui_beep", "ui_boop", "ui_swish");
```

### `SimpleAudio.groups.clear()` {#simpleaudio-api-method-groups-clear}

Deletes all audio groups.

Note:
Only deletes the groups themselves, does not affect their component tracks.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
SimpleAudio.groups.clear();
```

### `SimpleAudio.groups.delete(groupId)` {#simpleaudio-api-method-groups-delete}

Deletes the audio group with the given group ID.

Note:
Only deletes the group itself, does not affect its component tracks.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`groupId`:** (*string*) The ID of the group.

#### Examples:

```
SimpleAudio.groups.delete(":ui");
```

### `SimpleAudio.groups.get(groupId)` → *Array<string>* | *null* {#simpleaudio-api-method-groups-get}

Returns the array of track IDs with the given group ID, or `null` on failure.

Note:
To actually affect multiple tracks and/or groups, see the SimpleAudio.select() method.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`groupId`:** (*string*) The ID of the group.

#### Examples:

```
SimpleAudio.groups.get(":ui")  → Returns the array of track IDs matching ":ui"
```

### `SimpleAudio.groups.has(groupId)` → *boolean* {#simpleaudio-api-method-groups-has}

Returns whether an audio group with the given group ID exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`groupId`:** (*string*) The ID of the group.

#### Examples:

```
if (SimpleAudio.groups.has(":ui")) {
	// Group ":ui" exists.
}
```

## Lists {#simpleaudio-api-lists}

### `SimpleAudio.lists.add(listId, sources…)` {#simpleaudio-api-method-lists-add}

Adds a playlist with the given list ID.  Playlists are useful for playing tracks in a sequence—i.e., one after another.

Note:
If you simply want to apply actions to multiple tracks simultaneously, then you want a group instead.

#### History:

* `v2.28.0`: Introduced.
* `v2.29.0`: Changed descriptor object `copy` property to `own`.

#### Parameters:

* **`listId`:** (*string*) The ID of the list, which will be used to reference it.
* **`sources`:** (*string* | *object* | *Array<string | object>*) The track IDs or descriptors of the tracks to make part of the list, which may be specified as a list or an array.

#### Descriptor objects:

Track descriptor objects come in two forms and should have some of the noted properties:

* **Existing track form: `{ id, [own], [volume] }`**
  * **`id`:** (*string*) The ID of an existing track.
  * **`own`:** (optional, *boolean*) When `true`, signifies that the playlist should create its own independent copy of the track, rather than simply referencing the existing instance.  Owned copies are solely under the control of their playlist and cannot be selected with either the [`SimpleAudio.tracks.get()` method](#simpleaudio-api-method-tracks-get) or the [`SimpleAudio.select()` method](#simpleaudio-api-method-select).
  * **`volume`:** (optional, *number*) The base volume level of the track within the playlist.  If omitted, defaults to the track's current volume.  Valid values are floating-point numbers in the range `0` (silent) to `1` (loudest)—e.g., `0` is 0%, `0.5` is 50%, `1` is 100%.
* **New track form: `{ sources, [volume] }`**
  * **`sources`:** (*Array<string>*) The audio sources for the track.  Only one is required, though supplying additional sources in differing formats is recommended, as no single format is supported by all browsers.  A source must be either a URL (absolute or relative) to an audio resource, the name of an audio passage, or a data URI.  In rare cases where the audio format cannot be automatically detected from the source (URLs are parsed for a file extension, data URIs are parsed for the media type), a format specifier may be prepended to the front of each source to manually specify the format (syntax: `formatId|`, where `formatId` is the audio format—generally, whatever the file extension would normally be; e.g., `mp3`, `mp4`, `ogg`, `weba`, `wav`).
  * **`volume`:** (optional, *number*) The base volume level of the track within the playlist.  If omitted, defaults to `1` (loudest).  Valid values are floating-point numbers in the range `0` (silent) to `1` (loudest)—e.g., `0` is 0%, `0.5` is 50%, `1` is 100%.

#### Examples:

##### Basic usage with track IDs

```
// Add existing tracks at their current volumes
SimpleAudio.lists.add("bgm_lacuna", "swamped", "heavens_a_lie", "closer", "to_the_edge");
```

##### Using a mix of track IDs and descriptors

```
SimpleAudio.lists.add("bgm_lacuna",
	// Add existing track "swamped" at its current volume
	"swamped",

	// Add existing track "heavens_a_lie" at 50% volume
	{
		id     : "heavens_a_lie",
		volume : 0.5
	},

	// Add an owned copy of existing track "closer" at its current volume
	{
		id  : "closer",
		own : true
	},

	// Add an owned copy of existing track "to_the_edge" at 100% volume
	{
		id     : "to_the_edge",
		own    : true,
		volume : 1
	}
);
```

##### Using descriptors with sources

```
SimpleAudio.lists.add("bgm_lacuna",
	// Add a track from the given sources at the default volume (100%)
	{
		sources : ["media/audio/Swamped.mp3"]
	}

	// Add a track from the given sources at 50% volume
	{
		sources : ["media/audio/Heaven's_A_Lie.mp3"],
		volume  : 0.5
	},

	// Add a track from the given sources at the default volume (100%)
	{
		sources : ["media/audio/Closer.mp3"]
	},

	// Add a track from the given sources at 100% volume
	{
		sources : ["media/audio/To_The_Edge.mp3"],
		volume  : 1
	}
);
```

### `SimpleAudio.lists.clear()` {#simpleaudio-api-method-lists-clear}

Deletes all playlists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
SimpleAudio.lists.clear();
```

### `SimpleAudio.lists.delete(listId)` {#simpleaudio-api-method-lists-delete}

Deletes the playlist with the given list ID.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`listId`:** (*string*) The ID of the playlist.

#### Examples:

```
SimpleAudio.lists.delete("bgm_lacuna");
```

### `SimpleAudio.lists.get(listId)` → *`AudioList` object* | *null* {#simpleaudio-api-method-lists-get}

Returns the [`AudioList` instance](#audiolist-api) with the given list ID, or `null` on failure.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`listId`:** (*string*) The ID of the playlist.

#### Examples:

##### Basic usage

```
SimpleAudio.lists.get("bgm_lacuna")  → Returns the AudioList instance matching "bgm_lacuna"
```

##### Typical usage

```
// Return the AudioList instance matching "bgm_lacuna" and do something with it
SimpleAudio.lists.get("bgm_lacuna").volume(1).loop(true).play();
```

### `SimpleAudio.lists.has(listId)` → *boolean* {#simpleaudio-api-method-lists-has}

Returns whether a playlist with the given list ID exists.

#### History:

* `v2.28.0`: Introduced.

#### Parameters:

* **`listId`:** (*string*) The ID of the playlist.

#### Examples:

```
if (SimpleAudio.lists.has("bgm_lacuna")) {
	// Playlist "bgm_lacuna" exists.
}
```


code:
# `State` API {#state-api}

The story history contains moments (states) created during play.  Since it is possible to navigate the history—i.e., move backward and forward though the moments within the history—it may contain both past moments—i.e., moments that have been played—and future moments—i.e., moments that had been played, but have been rewound/undone, yet are still available to be restored.

In addition to the history, there is also the active moment—i.e., present—and expired moments—i.e., moments that had been played, but have expired from the history, thus cannot be navigated to.

API members dealing with the history work upon either the active moment—i.e., present—or one of the history subsets: the full in-play history—i.e., past + future—the past in-play subset—i.e., past only—or the extended past subset—i.e., expired + past.  These instances will be noted.

### `State.active` → *object* {#state-api-getter-active}

Returns the active (present) moment.

Note:
Using State.active directly is generally unnecessary as there exist a number of shortcut properties, State.passage and State.variables, and story functions, passage() and variables(), which grant access to its normal properties.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
State.active.title      → The title of the present moment
State.active.variables  → The variables of the present moment
````

### `State.bottom` → *object* {#state-api-getter-bottom}

Returns the bottommost (least recent) moment from the full in-play history (past + future).

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
State.bottom.title      → The title of the least recent moment within the full in-play history
State.bottom.variables  → The variables of the least recent moment within the full in-play history
```

### `State.current` → *object* {#state-api-getter-current}

Returns the current moment from the full in-play history (past + future), which is the pre-play version of the active moment.

Warning:
State.current is not a synonym for State.active.  You will, very likely, never need to use State.current directly within your code.

#### History:

* `v2.8.0`: Introduced.

#### Examples:

```
State.current.title      → The title of the current moment within the full in-play history
State.current.variables  → The variables of the current moment within the full in-play history
```

### `State.length` → *integer* {#state-api-getter-length}

Returns the number of moments within the past in-play history (past only).

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
if (State.length === 0) {
	/* No moments within the past in-play history. Egad! */
}
```

### `State.passage` → *string* {#state-api-getter-passage}

Returns the title of the passage associated with the active (present) moment.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
State.passage  → The passage title of the present moment
```

### `State.size` → *integer* {#state-api-getter-size}

Returns the number of moments within the full in-play history (past + future).

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (State.size === 0) {
	/* No moments within the full in-play history. Egad! */
}
```

### `State.temporary` → *object* {#state-api-getter-temporary}

Returns the current temporary variables.

#### History:

* `v2.13.0`: Introduced.

#### Examples:

```
State.temporary  → The current temporary variables
```

### `State.top` → *object* {#state-api-getter-top}

Returns the topmost (most recent) moment from the full in-play history (past + future).

Warning:
State.top is not a synonym for State.active.  You will, very likely, never need to use State.top directly within your code.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
State.top.title      → The title of the most recent moment within the full in-play history
State.top.variables  → The variables of the most recent moment within the full in-play history
```

### `State.turns` → *integer* {#state-api-getter-turns}

Returns the total number (count) of played moments within the extended past history (expired + past).

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
if (State.turns === 1) {
	/* Initial turn.  The starting passage is displayed. */
}
```

### `State.variables` → *object* {#state-api-getter-variables}

Returns the variables from the active (present) moment.

#### History:

* `v2.0.0`: Introduced.

#### Examples:

```
State.variables  → The variables of the present moment
```

### `State.getVar(varName)` → *any* {#state-api-method-getvar}

Returns the value of the story or temporary variable by the given name.

#### History:

* `v2.22.0`: Introduced.

#### Parameters:

* **`varName`:** (*string*) The name of the story or temporary variable, including its sigil—e.g., `$charName`.

#### Examples:

```
State.getVar("$charName")  → Returns the value of $charName
```

### `State.has(passageTitle)` → *boolean* {#state-api-method-has}

Returns whether any moments with the given title exist within the past in-play history (past only).

Note:
State.has() does not check expired moments.  If you need to know if the player has ever been to a particular passage, then you must use the State.hasPlayed() method or the hasVisited() story function.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`passageTitle`:** (*string*) The title of the moment whose existence will be verified.

#### Examples:

```
State.has("The Ducky")  → Returns whether a moment matching "The Ducky" exists
```

### `State.hasPlayed(passageTitle)` → *boolean* {#state-api-method-hasplayed}

Returns whether any moments with the given title exist within the extended past history (expired + past).

Note:
If you need to check for multiple passages, the hasVisited() story function will likely be more convenient to use.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`passageTitle`:** (*string*) The title of the moment whose existence will be verified.

#### Examples:

```
State.hasPlayed("The Ducky")  → Returns whether a moment matching "The Ducky" ever existed
```

### `State.index(index)` → *object* {#state-api-method-index}

Returns the moment, relative to the bottom of the past in-play history (past only), at the given index.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`index`:** (*integer*) The index of the moment to return.

#### Examples:

```
State.index(0)                 → Returns the least recent moment within the past in-play history
State.index(1)                 → Returns the second to least recent moment within the past in-play history
State.index(State.length - 1)  → Returns the most recent moment within the past in-play history
```

### `State.isEmpty()` → *boolean* {#state-api-method-isempty}

Returns whether the full in-play history (past + future) is empty.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
if (State.isEmpty()) {
	/* No moments within the full in-play history. Egad! */
}
```

### `State.peek([offset])` → *object* {#state-api-method-peek}

Returns the moment, relative to the top of the past in-play history (past only), at the, optional, offset.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`offset`:** (optional, *integer*) The offset, from the top of the past in-play history, of the moment to return.  If not given, an offset of `0` is used.

#### Examples:

```
State.peek()                  → Returns the most recent moment within the past in-play history
State.peek(0)                 → Returns the most recent moment within the past in-play history
State.peek(1)                 → Returns the second most recent moment within the past in-play history
State.peek(State.length - 1)  → Returns the least recent moment within the past in-play history
```

### `State.metadata.size` → *integer* {#state-api-method-metadata-size}

Returns the size of the story metadata store—i.e., the number of stored pairs.

#### History:

* `v2.30.0`: Introduced.

#### Examples:

```
// Determines whether the metadata store has any members.
if (State.metadata.size > 0) {
	/* store is not empty */
}
```

### `State.metadata.clear()` {#state-api-method-metadata-clear}

Empties the story metadata store.

#### History:

* `v2.29.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
// Removes all values from the metadata store.
State.metadata.clear();
```

### `State.metadata.delete(key)` {#state-api-method-metadata-delete}

Removes the specified key, and its associated value, from the story metadata store.

#### History:

* `v2.29.0`: Introduced.

#### Parameters:

* **`key`:** (*string*) The key to delete.

#### Examples:

```
// Removes 'achievements' from the metadata store.
State.metadata.delete('achievements');
```

### `State.metadata.entries()` → *Array<Array<string, any>>* {#state-api-method-metadata-entries}

Returns an array of the story metadata store's key/value pairs as `[key, value]` arrays.

#### History:

* `v2.36.0`: Introduced.

#### Parameters: *none*

#### Examples:

```js
// Iterate over the pairs with a `for` loop.
var metadata = State.metadata.entries();
for (var i = 0; i < metadata.length; ++i) {
	var key   = metadata[i][0];
	var value = metadata[i][1];

	/* do something */
}
```

```js
// Iterate over the pairs with `<Array>.forEach()`.
State.metadata.entries().forEach(function (pair) {
	var key   = pair[0];
	var value = pair[1];

	/* do something */
});
```

### `State.metadata.get(key)` → *any* {#state-api-method-metadata-get}

Returns the value associated with the specified key from the story metadata store.

#### History:

* `v2.29.0`: Introduced.

#### Parameters:

* **`key`:** (*string*) The key whose value should be returned.

#### Examples:

```
// Returns the value of 'achievements' from the metadata store.
var playerAchievements = State.metadata.get('achievements');
```

### `State.metadata.has(key)` → *boolean* {#state-api-method-metadata-has}

Returns whether the specified key exists within the story metadata store.

#### History:

* `v2.29.0`: Introduced.

#### Parameters:

* **`key`:** (*string*) The key whose existence should be tested.

#### Examples:

```
// Returns whether 'achievements' exists within the metadata store.
if (State.metadata.has('achievements')) {
	/* do something */
}
```

### `State.metadata.keys()` → *Array<string>* {#state-api-method-metadata-keys}

Returns an array of the story metadata store's keys.

#### History:

* `v2.36.0`: Introduced.

#### Parameters: *none*

#### Examples:

```js
// Iterate over the keys with a `for` loop.
var metadataKeys = State.metadata.keys();
for (var i = 0; i < metadataKeys.length; ++i) {
	var key = metadataKeys[i];

	/* do something */
}
```

```js
// Iterate over the keys with `<Array>.forEach()`.
State.metadata.keys().forEach(function (key) {
	/* do something */
});
```

### `State.metadata.set(key, value)` {#state-api-method-metadata-set}

Sets the specified key and value within the story metadata store, which causes them to persist over story and browser restarts—n.b. private browsing modes do interfere with this.  To update the value associated with a key, simply set it again.

Note:
The story metadata, like saves, is tied to the specific story it was generated with.  It is not a mechanism for moving data between stories.

Warning:
The story metadata store is not, and should not be used as, a replacement for saves.  Examples of good uses: achievement tracking, new game+ data, playthrough statistics, etc.

Warning:
This feature is largely incompatible with private browsing modes, which cause all in-browser storage mechanisms to either persist only for the lifetime of the browsing session or fail outright.

#### History:

* `v2.29.0`: Introduced.

#### Parameters:

* **`key`:** (*string*) The key that should be set.
* **`value`:** (*any*) The value to set.

#### Examples:

```
// Sets 'achievements', with the given value, in the metadata store.
State.metadata.set('achievements', { ateYellowSnow : true });

// Sets 'ngplus', with the given value, in the metadata store.
State.metadata.set('ngplus', true);
```

### `State.prng.init([seed [, useEntropy]])` {#state-api-method-prng-init}

Initializes the seedable pseudo-random number generator (PRNG) and integrates it into the story state and saves.  Once initialized, the [`State.random()`](#state-api-method-random) method and story functions, [`random()`](#functions-function-random) and [`randomFloat()`](#functions-function-randomfloat), return deterministic results from the seeded PRNG—by default, they return non-deterministic results from [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random).

Note:
State.prng.init() must be called during story initialization, within either your project's JavaScript section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) or the StoryInit special passage.  Additionally, it is strongly recommended that you do not specify any arguments to State.prng.init() and allow it to automatically seed itself.  If you should chose to use an explicit seed, however, it is strongly recommended that you also enable additional entropy, otherwise all playthroughs for all players will be exactly the same.

#### History:

* `v2.29.0`: Introduced.

#### Parameters:

* **`seed`:** (optional, *string*) The explicit seed used to initialize the pseudo-random number generator.
* **`useEntropy`:** (optional, *boolean*) Enables the use of additional entropy to pad the specified explicit seed.

#### Examples:

```
State.prng.init()                       → Automatically seed the PRNG (recommended)
State.prng.init("aVeryLongSeed")        → Seed the PRNG with "aVeryLongSeed" (not recommended)
State.prng.init("aVeryLongSeed", true)  → Seed the PRNG with "aVeryLongSeed" and pad it with extra entropy
```

### `State.prng.isEnabled()` → *boolean* {#state-api-method-prng-isenabled}

Returns whether the [seedable PRNG](#state-api-method-prng-init) has been enabled.

#### History:

* `v2.29.0`: Introduced.

#### Examples:

```
State.prng.isEnabled()  → Returns whether the seedable PRNG is enabled
```

### `State.prng.pull` → *integer* | *NaN* {#state-api-getter-prng-pull}

Returns the current pull count—i.e., how many requests have been made—from the [seedable PRNG](#state-api-method-prng-init) or, if the PRNG is not enabled, `NaN`.

Note:
The pull count is automatically included within saves and sessions, so this is not especially useful outside of debugging purposes.

#### History:

* `v2.29.0`: Introduced.

#### Examples:

```
State.prng.pull  → Returns the current PRNG pull count
```

### `State.prng.seed` → *string* | *null* {#state-api-getter-prng-seed}

Returns the seed from the [seedable PRNG](#state-api-method-prng-init) or, if the PRNG is not enabled, `null`.

Note:
The seed is automatically included within saves and sessions, so this is not especially useful outside of debugging purposes.

#### History:

* `v2.29.0`: Introduced.

#### Examples:

```
State.prng.seed  → Returns the PRNG seed
```

### `State.random()` → *number* {#state-api-method-random}

Returns a pseudo-random decimal number (floating-point) in the range `0` (inclusive) up to, but not including, `1` (exclusive).

Note:
By default, it simply returns non-deterministic results from Math.random(), however, when the seedable PRNG has been enabled, via State.prng.init(), it returns deterministic results from the seeded PRNG instead.

#### History:

* `v2.0.0`: Introduced.

#### Parameters: *none*

#### Examples:

```
State.random()  → Returns a pseudo-random floating-point number in the range [0, 1)
```

### `State.setVar(varName, value)` → *boolean* {#state-api-method-setvar}

Sets the value of the story or temporary variable by the given name.  Returns whether the operation was successful.

#### History:

* `v2.22.0`: Introduced.

#### Parameters:

* **`varName`:** (*string*) The name of the story or temporary variable, including its sigil—e.g., `$charName`.
* **`value`:** (*any*) The value to assign.

#### Examples:

```
State.setVar("$charName", "Jane Doe")  → Assigns the string "Jane Doe" to $charName
```

### `State.initPRNG([seed [, useEntropy]])` {#state-api-method-initprng}

Deprecated:
This method has been deprecated and should no longer be used.  See the State.prng.init() method for its replacement.

#### History:

* `v2.0.0`: Introduced.
* `v2.29.0`: Deprecated in favor of `State.prng.init()`.


code:
# `Story` API {#story-api}

### `Story.domId` → *string* {#story-api-getter-domid}

The DOM ID of the story, created from the slugified story title.

#### History:

* `v2.0.0`: Introduced.

### `Story.ifId` → *string* {#story-api-getter-ifid}

The IFID (Interactive Fiction IDentifier) of the story, if any.

#### History:

* `v2.5.0`: Introduced.

### `Story.title` → *string* {#story-api-getter-title}

The title of the story.

#### History:

* `v2.0.0`: Introduced.

### `Story.get(title)` → *`Passage` object* {#story-api-method-get}

Returns the `Passage` object referenced by the given title, or an empty `Passage` object on failure.

Note:
This method will not return "code" passages—i.e., script, stylesheet, and widget passages.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`title`:** (*string*) The title of the `Passage` object to return.

#### Examples:

```
Story.get("The Ducky")  → Returns the Passage object matching "The Ducky"
```

### `Story.has(title)` → *boolean* {#story-api-method-has}

Returns whether a `Passage` object referenced by the given title exists.

Note:
This method will not detect "code" passages—i.e., script, stylesheet, and widget passages.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`title`:** (*string*) The title of the `Passage` object whose existence will be verified.

#### Examples:

```
Story.has("The Ducky")  → Returns whether a Passage object matching "The Ducky" exists
```

### `Story.lookup(propertyName , searchValue [, sortProperty])` → *`Passage` object array* {#story-api-method-lookup}

Returns a new array filled with all `Passage` objects that contain the given property, whose value matches the given search value, or an empty array, if no matches are made.

Note:
This method will not return "code" passages—i.e., script, stylesheet, and widget passages.

#### History:

* `v2.0.0`: Introduced.

#### Parameters:

* **`propertyName`:** (*string*) The name of property whose value will be compared to the search value.
* **`searchValue`:** (*string* | *number*) The value to search for within the matched property.  The type of the property determines how the search occurs—non-arrays are directly compared, while arrays are searched.  If the property's value, for non-arrays, or any of its members, for arrays, match, then the `Passage` object is added to the results.
* **`sortProperty`:** (optional, *string*) The property whose value will be used to lexicographically sort the returned array.  If not given, the `Passage` object's `title` property is used.

#### Examples:

```
→ Returns all 'forest'-tagged Passage objects, sorted by their titles
Story.lookup("tags", "forest");
```

### `Story.lookupWith(predicate [, sortProperty])` → *`Passage` object array* {#story-api-method-lookupwith}

Returns a new array filled with all `Passage` objects that pass the test implemented by the given predicate function or an empty array, if no objects pass.

Note:
This method will not return "code" passages—i.e., script, stylesheet, and widget passages.

#### History:

* `v2.11.0`: Introduced.

#### Parameters:

* **`predicate`:** (*function*) The function used to test each `Passage` object, which is passed into the function as its sole parameter.  If the function returns `true`, then the `Passage` object is added to the results.
* **`sortProperty`:** (optional, *string*) The property whose value will be used to lexicographically sort the returned array.  If not given, the `Passage` object's `title` property is used.

#### Examples:

```
→ Returns all 'forest'-tagged Passage objects, sorted by their titles
Story.lookupWith(function (p) {
	return p.tags.includes("forest");
});

→ Returns all Passage objects whose titles contain whitespace, sorted by their titles
var hasWhitespaceRegExp = /\s/;
Story.lookupWith(function (p) {
	return hasWhitespaceRegExp.test(p.title);
});
```