Prerequisites
Before developing a plugin, make sure you have the basics ready:
- Audion installed and running (version 1.2.0 or newer).
- Basic JavaScript knowledge (ES6 syntax, async/await, DOM APIs).
- A code editor (e.g. Visual Studio Code, Zed, or Neovim).
- Git for version control and registry publishing.
Plugin Manifest: plugin.json
Every plugin begins with a plugin.json manifest file placed at the root of the plugin directory. This file declares identity, entry files, author details, and runtime permission scopes.
Required Manifest Fields
| Field | Type | Description |
|---|---|---|
name | string | Human-readable extension name (e.g. "Last.fm Scrobbler"). |
version | string | Semantic version string (e.g. "1.0.0"). |
author | string | Developer or organization name. |
type | string | Must be "js" (or "wasm" for WebAssembly extensions). |
entry | string | Relative path to entry script (e.g. "index.js"). |
permissions | string[] | Array of declared security scopes (e.g. ["player:read", "network"]). |
Optional Manifest Fields
| Field | Type | Description |
|---|---|---|
description | string | One-line description displayed on extension cards. |
category | string | integration, utility, library, or theme. |
repo | string | Public GitHub repository URL. |
license | string | SPDX open-source license identifier (e.g. "MIT"). |
min_version | string | Minimum supported Audion build (e.g. "1.2.0"). |
Sample plugin.json
{
"name": "Play Counter",
"version": "1.0.0",
"author": "Audion Contributors",
"description": "Records playback frequency and total listening time per track",
"type": "js",
"entry": "index.js",
"category": "utility",
"permissions": [
"player:read",
"storage:local"
],
"license": "MIT",
"repo": "https://github.com/dupitydumb/audion-plugins"
}Directory Structure
Audion plugins use a simple flat folder structure named with lowercase letters and dashes (kebab-case):
your-plugin-name/
├── plugin.json # Manifest file (required)
├── index.js # Entry execution script (required)
├── icon.png # Optional 64x64 icon
└── README.md # Documentation displayed on marketplaceJavaScript Lifecycle Pattern
Wrap your extension in an Immediately Invoked Function Expression (IIFE) with 'use strict' to avoid leaking variables into the global application context:
(function () {
'use strict';
const PlayCounterPlugin = {
name: 'Play Counter',
api: null,
// Called once when plugin is initialized
init(api) {
this.api = api;
console.log('[PlayCounter] Initialized successfully.');
},
// Called when user enables the extension in Settings
start() {
this.api.player.on('trackChange', (track) => {
if (!track) return;
const current = this.api.storage.get(track.id) || 0;
this.api.storage.set(track.id, current + 1);
});
},
// Called when user disables the extension
stop() {
console.log('[PlayCounter] Stopped.');
},
// Called when extension is unloaded or uninstalled
destroy() {
this.api = null;
console.log('[PlayCounter] Cleaned up.');
}
};
// Register on window object
window.AudionPlugin = PlayCounterPlugin;
})();Lifecycle Hooks
Audion executes lifecycle methods on your plugin object sequentially as states change:
Discovery
Audion scans the local plugins storage directory for valid plugin.json manifest files.
Loading
The sandboxed JavaScript runtime compiles and loads the entry script into isolated scope.
init(api)
Audion supplies the scoped PluginAPI instance to your extension. Store references and initialize internal state.
start()
Fires when the user toggles your plugin on in Settings. Subscribe to track events, scrobble hooks, or player changes.
stop()
Fires when the user toggles your plugin off. Pause any polling, background fetch routines, or visualizers.
destroy()
Fires when the extension is disabled or removed. Unbind DOM elements, clear interval timers, and flush cached storage.
destroy() is invoked. Uncleaned intervals lead to memory leaks across long listening sessions.Available Permissions
Permissions control runtime sandbox capabilities. Request only the scopes required by your extension logic:
| Scope | Description | API Methods Provided |
|---|---|---|
player:read | Access track metadata and playback progress. | getCurrentTrack(), isPlaying(), getCurrentTime() |
player:control | Play, pause, skip tracks, and adjust volume. | play(), pause(), next(), seek(seconds) |
library:read | Query stored music tracks, playlists, and albums. | getTracks(), getPlaylists() |
storage:local | Persist key-value data to dedicated plugin storage. | storage.get(k), storage.set(k, v), storage.remove(k) |
network | Make outbound HTTP/HTTPS requests to external endpoints. | Standard Web fetch() API |
ui:inject | Mount custom user interface panels into player slots. | ui.mountSlot(slotName, element) |
Storage API
Extensions with storage:local can store configuration and persistent statistics. Storage is sandboxed per plugin, ensuring no namespace collisions:
// Persist setting
this.api.storage.set('scrobbleEnabled', true);
// Retrieve setting with fallback
const isEnabled = this.api.storage.get('scrobbleEnabled') ?? false;
// Remove key
this.api.storage.remove('scrobbleEnabled');UI Injection
Extensions with ui:inject can mount visual widgets into designated application drawers or player slots:
const badge = document.createElement('div');
badge.className = 'audion-plugin-badge';
badge.textContent = 'HQ 96kHz';
// Mount to player drawer
this.api.ui.mountSlot('playerBar:meta', badge);Testing Locally
Test your extension on your local machine by copying its directory directly into your Audion user data storage:
- Windows:
%APPDATA%\Audion\plugins\your-plugin-name - macOS:
~/Library/Application Support/Audion/plugins/your-plugin-name - Linux:
~/.config/Audion/plugins/your-plugin-name
Open Audion Settings > Plugins, click Refresh, and toggle your extension on. Press Ctrl+Shift+I (or Cmd+Option+I on macOS) to open the DevTools console and inspect runtime logs.
Publishing to the Registry
The official Audion marketplace automatically indexes community extensions via GitHub repository topics. No pull requests or waiting for review:
- Create a public GitHub repository containing your
plugin.jsonmanifest at the repository root. - Open your repository Settings on GitHub and add the topic tag:
audion-plugins. - The automated indexer scans GitHub hourly for the
audion-pluginstopic and syncs valid manifests into the marketplace registry.
Deep Link Distribution
Allow users to install your extension directly from your personal website or README using the registered audion:// desktop protocol:
audion://install-plugin?url=https://github.com/your-username/your-pluginClicking the link prompts Audion to open an installation confirmation dialog, download the release ZIP, and register the sandbox automatically.
Ready to build?
Inspect existing extensions in the marketplace or clone the official template repository to start building your extension immediately.