Developer Guide

Build Your Own Audion Plugin

Extend Audion with custom features — from playback enhancements to visual themes. JavaScript and WebAssembly supported.

Prerequisites

Before building a plugin, make sure you have the basics:

  • Audion installed and running
  • Basic JavaScript knowledge (variables, functions, objects)
  • A text editor or IDE (VS Code recommended)
  • Git for distributing your plugin (optional)
No build step required. Audion plugins are plain JavaScript — write a file, drop it in, and it works.

Plugin Manifestplugin.json

Every plugin begins with a plugin.json manifest. This file tells Audion who made the plugin, what it does, what permissions it needs, and where the code lives.

Required Fields

FieldTypeDescription
namestringDisplay name (e.g., "Play Counter")
versionstringSemantic version (e.g., "1.0.0")
authorstringYour name or organization
type"js" or "wasm"Plugin type (most should use "js")
entrystringEntry file path (e.g., "index.js")
permissionsstring[]Permissions your plugin needs (see table below)

Optional Fields

FieldTypeDescription
safe_namestringExplicit folder name (kebab-case)
descriptionstringShort description of your plugin
repostringGitHub repository URL
manifest_urlstringURL to hosted plugin.json for auto-install
iconstringPath to icon file (e.g., "icon.png")
icon_urlstringURL to hosted icon (overrides icon)
homepagestringPlugin website / documentation URL
categorystringCategory: audio | ui | lyrics | library | utility | appearance | social
tagsstring[]Searchable tags
licensestringLicense identifier (e.g., "MIT", "GPL-3.0")
min_versionstringMinimum Audion version (e.g., "1.0.0")
ui_slotsstring[]UI injection points (experimental)
cross_plugin_accessobject[]Cross-plugin communication permissions

Example Manifest

json
{
  "name": "Play Counter",
  "version": "1.0.0",
  "author": "Audion Team",
  "description": "Tracks how many times each song has been played",
  "type": "js",
  "entry": "index.js",
  "permissions": [
    "player:read",
    "storage:local",
    "ui:inject"
  ],
  "category": "library",
  "tags": [
    "stats",
    "play-count",
    "analytics"
  ],
  "license": "MIT"
}

Plugin Structure

Audion plugins follow a simple directory structure. The folder name should be lowercase with dashes (kebab-case).

plugin-examples/
└── your-plugin-name/
    ├── plugin.json       # Plugin manifest (required)
    ├── index.js          # Main plugin code — entry point
    ├── icon.png          # Plugin icon (optional)
    └── README.md         # Documentation (optional)

JavaScript Plugin Pattern

Use an Immediately Invoked Function Expression (IIFE) to avoid polluting the global namespace:

javascript
(function () {
  'use strict';

  const YourPlugin = {
    name: 'Your Plugin Name',
    someState: null,

    // Called once when plugin is loaded
    init(api) {
      this.api = api;
      console.log('[YourPlugin] Initialized!');
    },

    // Called when plugin is enabled
    start() {
      const track = this.api.player.getCurrentTrack();
      if (track) {
        console.log('Now playing:', track.title);
      }
    },

    // Called when plugin is disabled
    stop() {
      console.log('[YourPlugin] Stopped');
    },

    // Called when plugin is unloaded
    destroy() {
      // Clean up intervals, listeners, UI elements
      console.log('[YourPlugin] Cleaned up');
    }
  };

  // Register globally — required!
  window.YourPlugin = YourPlugin;
  window.AudionPlugin = YourPlugin;
})();
Important: You must register your plugin on window using both a unique name and window.AudionPlugin. Always use 'use strict'.

Lifecycle Hooks

Audion calls specific methods on your plugin object during its lifecycle:

1
Discovery

Audion scans the plugins directory for plugin.json files

2
Loading

Runtime loads the entry file

3
init(api)

Plugin receives API object. Set up internal state here.

4
start()

Called when user enables the plugin. Begin functionality.

5
stop()

Called when user disables the plugin

6
destroy()

Clean up intervals, event listeners, DOM elements

⚠ Resource Cleanup

Always clean up in destroy(). Intervals, event listeners, and DOM elements that are not removed can cause memory leaks.

Available Permissions

Permissions control what your plugin can access. Request only what you need — users review these before enabling.

PermissionDescriptionAPI Methods
player:readRead playback state and track infogetCurrentTrack(), isPlaying(), getCurrentTime(), getDuration()
player:controlControl playbackplay(), pause(), togglePlay(), next(), prev(), seek()
library:readRead music librarygetTracks(), getPlaylists()
library:writeModify library and playlistsPlanned
storage:localStore local plugin datastorage.get(), storage.set()
ui:injectInject custom DOM elementsDirect DOM manipulation
network:fetchMake HTTP requestsStandard fetch() API
system:notifyShow system notificationsBrowser Notification API
lyrics:readRead lyrics dataPlanned
lyrics:writeModify and save lyricsPlanned

Example

javascript
"permissions": ["player:read", "storage:local"]

Storage API

Plugins with the storage:local permission can persist data between app restarts.

javascript
// Save data
this.api.storage.set('playCount', { track1: 5, track2: 12 });

// Read data
const counts = this.api.storage.get('playCount');
// → { track1: 5, track2: 12 }

// Delete data
this.api.storage.remove('playCount');

Storage is namespaced per plugin — data from "Play Counter" won't conflict with "Theme Customizer".

UI Injection

Plugins with the ui:inject permission can insert custom UI elements into the app. Use it sparingly and clean up in destroy().

javascript
// Add a custom button to the player bar
const btn = document.createElement('button');
btn.textContent = 'My Plugin';
btn.onclick = () => alert('Hello from plugin!');
document.querySelector('.player-bar')?.appendChild(btn);

// Store reference for cleanup
this.myButton = btn;
javascript
// In destroy():
destroy() {
  if (this.myButton) {
    this.myButton.remove();
    this.myButton = null;
  }
}
⚠ Clean Up After Yourself

UI injection is powerful but dangerous. Always remove injected elements in destroy() and use query-selectors that are resilient to UI changes.

Testing Locally

Once your plugin folder is ready, test it in Audion:

  1. Copy your plugin folder to the plugin-examples/ directory in Audion (or the plugins directory in your Audion data folder)
  2. Open Audion and go to the Plugins page
  3. Your plugin should appear in the list. Click Enable to activate it
  4. Open the Developer Console (Ctrl+Shift+I) to see your console.log output
  5. Reload the plugin after code changes (disable → enable)
Hot Reload: You can reload a plugin without restarting Audion by toggling it off and on in the plugin manager.

Debugging Tips

  • Check the browser console for errors
  • Verify your plugin.json is valid JSON
  • Make sure window.AudionPlugin is set
  • Ensure all referenced files exist in your plugin folder

Publishing to the Registry

The Audion marketplace auto-indexes plugins via GitHub repository topics. No manual PR needed — just push code and add the topic.

Step 1: Create a Public GitHub Repository

  • Push your plugin code to a public GitHub repo
  • Ensure plugin.json and entry file are at the repository root
  • Include a README.md so users know what the plugin does

Step 2: Add the Topic

Open your repo on GitHub → Settings → scroll toTopics → add audion-plugins.

The audion-plugins registry action scans for repos with the audion-plugins topic. If your repo has valid .json manifest files (starting withplugin.json at the root), it gets indexed automatically.

Topics are discoverable. Anyone browsing GitHub can find your plugin via the audion-plugins topic too.

Validation Checklist

Before adding the topic, confirm your repo has:

  • A valid plugin.json at the root
  • All referenced files exist (entry point, icons, etc.)
  • A plugin.json with valid JSON syntax and required fields
  • A public repo (private repos won't be indexed)

Update a Published Plugin

Push changes to your repo. The action re-indexes on the next scan — no topic changes needed.

Distribution via Deep Link

Each plugin on the marketplace has an Install in Audion button that opens the app and prompts the user to install. You can link directly to your plugin from anywhere.

Deep Link Format

javascript
audion://install-plugin?url=https://github.com/your-username/your-plugin

How It Works

  1. User clicks the link — Audion opens (or comes to front)
  2. A confirmation dialog asks: "Do you want to install the plugin from repository?"
  3. Audion fetches plugin.json from the repo, downloads the entry file and icon
  4. Plugin is installed and ready to enable in the plugin manager
Once your plugin is in the registry, its marketplace page automatically gets the install button. Direct link: audion://install-plugin?url=https%3A%2F%2Fgithub.com%2Fyour-username%2Fyour-plugin

Ready to build?

Check out the audion-plugins repo for examples and the marketplace for inspiration.

Ko-fi cup icon
Enjoying Audion?Buy us a coffee