1. Project Overview & Quickstart (spicetify/cli)
File: README.md
[](https://spicetify.app/)
Command-line tool to customize the official Spotify client.
Supports Windows, MacOS and Linux.
Features
- Change colors across the User Interface
- Inject CSS for advanced customization
- Inject Extensions to extend functionalities, manipulate UI and control player
- Inject Custom Apps
- Make yourself in control of the Spotify client
Links
Code Signing Policy
Free code signing provided by SignPath.io, certificate by SignPath Foundation.
File: CustomApps/lyrics-plus/README.md
Spicetify Custom App
Lyrics Plus
Show current track lyrics. Current lyrics providers:
- Internal Spotify lyrics service.
- Netease: From Chinese developers and users. Provides karaoke and synced lyrics.
- Musixmatch: A company from Italy. Provided synced lyrics.
- Genius: Provides unsynced lyrics but with description/insight from artists themselves (Disabled and cannot be used as a provider on
1.2.31and higher).
Different lyrics modes: Karaoke, Synced, Unsynced and Genius. At the moment, only Netease provides karaoke-able lyrics. Mode is automatically falled back, from Karaoke, Synced, Unsynced to Genius when lyrics are not available in that mode.
Right click or Double click at any mode tab to "lock in", so lyric mode won't auto switch. It should show a dot next to mode name when mode is locked. Right click or double click again to unlock
Lyrics in Unsynced and Genius modes can be search and jump to. Hit Ctrl + Shift + F to open search box at bottom left of screen. Hit Enter/Shift+Enter to loop over results.
Choose between different option of displaying Japanese lyrics. (Furigana, Romaji, Hiragana, Katakana)
Customise colors, change providers' priorities in config menu. Config menu locates in Profile Menu (top right button with your user name).
To install, run:
spicetify config custom_apps lyrics-plus
spicetify applyCredits
- A few parts of app code are taken from Spotify official app, including SyncedLyricsPage, CSS animation and TabBar. Please do not distribute these code else where out of Spotify/Spicetify context.
- Netease synced lyrics parser is adapted from mantou132/Spotify-Lyrics. Give it a Star if you like this app.
- The algorithm for converting Japanese lyrics is based on Hexenq's Kuroshiro.
- The algorithm for converting Chinese lyrics is based on BYVoid's OpenCC via nk2028's opencc-js.
- The algorithm for converting Korean lyrics is based on fujaru's aromanize-js
- The algorithm for detecting Simplified Chinese is adapted from nickdrewe's traditional-or-simplified.
2. Official Technical Reference & Guides (spicetify/docs)
File: README.md
Spicetify Docs
This repository holds the documentation for Spicetify, which can be found here.
Contributing
If you feel like you can contribute, please do so by opening an issue or pull request. We are always open to expand our documentation and add new features.
File: docs/development/api-wrapper/functions/add-to-queue.md
title: addToQueue
Adds a track or array of tracks to the bottom of the prioritized queue.
:::tip
This works similarly to Spicetify.Platform.PlayerAPI.addToQueue but works silently, meaning no notification will be shown.
If you want default Spotify behavior, use Spicetify.Platform.PlayerAPI.addToQueue instead.
:::
function addToQueue(uri: ContextTrack[]): Promise<void>;Parameters
| Name | Type | Description |
|---|---|---|
uri |
ContextTrack[] |
Array of tracks to add to queue. |
Example
// Add current track to queue
const currentTrack = Spicetify.Player.data.item;
await Spicetify.addToQueue([currentTrack]);
// Add a track to queue
const trackUri = "spotify:track:4iV5W9uYEdYUVa79Axb7Rh";
await Spicetify.addToQueue([ { uri: trackUri } ]);
---
## File: docs/development/api-wrapper/functions/color-extractor.md
---
title: colorExtractor
---
Extracts colors from a playlist, track, album, artist, show, etc.
```ts
function colorExtractor(uri: string): Promise<{
DARK_VIBRANT: string;
DESATURATED: string;
LIGHT_VIBRANT: string;
PROMINENT: string;
VIBRANT: string;
VIBRANT_NON_ALARMING: string;
}>;Parameters
| Parameter | Type | Description |
|---|---|---|
| uri | string |
URI of anything that has artwork (playlist, track, album, artist, show, etc.) |
Returns
| Name | Type | Description |
|---|---|---|
| DARK_VIBRANT | string |
Dark vibrant color in hex format. |
| DESATURATED | string |
Desaturated color in hex format. |
| LIGHT_VIBRANT | string |
Light vibrant color in hex format. |
| PROMINENT | string |
Prominent color in hex format. |
| VIBRANT | string |
Vibrant color in hex format. |
| VIBRANT_NON_ALARMING | string |
Vibrant non alarming color in hex format. |
Example
// Get color from current track
const currentTrack = Spicetify.Player.data.item;
const colors = await Spicetify.colorExtractor(currentTrack.uri);File: docs/development/api-wrapper/functions/get-audio-data.md
title: getAudioData
Fetch track analyzed audio data.
Under the hood, it uses the wg://audio-attributes/v1/audio-analysis/ endpoint, which is identical to Spotify Web API's Get Track's Audio Analysis. The only difference is that it doesn't require authentication.
:::caution
Beware, not all tracks have audio data.
:::
function getAudioData(uri?: string): Promise<any>;Parameters
| Parameter | Type | Description |
|---|---|---|
| uri | string | undefined |
URI of the track. If not provided, it will use the current track. |
Returns
An object containing the audio data. See the Spotify Web API reference for more details.
Example
// Get audio data from current track
const audioData = await Spicetify.getAudioData();
// Get audio data from a specific track
const audioData = await Spicetify.getAudioData("spotify:track:1qDrWA6lyx8cLECdZE7TV7");File: docs/development/api-wrapper/functions/get-font-style.md
title: getFontStyle
Spicetify provides a function that returns the CSS style for a given font variant used in the Spotify app.
:::tip
This function is used to provide backwards compatibility for older Spicetify extensions and custom apps that use main-type- classes.
Instead of using this function to get Spotify stylings, you can simply add the main-type-<variant> class to your element.
:::
function getFontStyle(font: Variant): string;Parameters
| Parameter | Type | Description |
|---|---|---|
| font | Variant |
Font variant |
Returns
string - CSS style for the given font variant.
Example
const style = getFontStyle("forte");
// Returns "viola" if given an invalid variant
// Equivalent to `getFontStyle("viola");`
const style = getFontStyle("invalid-variant");File: docs/development/api-wrapper/functions/remove-from-queue.md
title: removeFromQueue
:::tip
This works similarly to Spicetify.Platform.PlayerAPI.removeFromQueue.
:::
:::caution
If a uid is not provided, all tracks with the same uri will be removed.
:::
function removeFromQueue(uri: ContextTrack[]): Promise<void>;Parameters
| Name | Type | Description |
|---|---|---|
uri |
ContextTrack[] |
Array of tracks to remove from queue. |
Example
// Remove current track from queue
const currentTrack = Spicetify.Player.data.item;
await Spicetify.removeFromQueue([currentTrack]);
// Remove a track from queue
const trackUri = "spotify:track:4iV5W9uYEdYUVa79Axb7Rh";
await Spicetify.removeFromQueue([ { uri: trackUri } ]);File: docs/development/api-wrapper/functions/show-notification.md
title: showNotification
Show a toast notification inside Spotify.
function showNotification(text: string, isError?: boolean, msTimeout?: number): void;| Parameter | Type | Description |
|---|---|---|
| text | string |
Message to display. Can use inline HTML for styling. |
| isError | boolean |
If true, toast will be red. Defaults to false. |
| msTimeout | number |
Time in milliseconds to display the toast. Defaults to Spotify's value. |
Example
// Display a notification
Spicetify.showNotification("My Menu Item clicked!");
// Display a notification with a custom timeout
Spicetify.showNotification("My Menu Item clicked!", false, 1000);
// Display an error notification
Spicetify.showNotification("Something wrong happened", true);
// Display a bolded error notification
Spicetify.showNotification("**Something wrong happened**", true);File: docs/development/api-wrapper/classes/context-menu.md
title: ContextMenu
Create custom menu item and prepend to right click context menu.
Useful for adding custom actions to context menu (when a user right-clicks on a track, album, artist, etc.)
Classes
Item
Single context menu item.
new Spicetify.ContextMenu.Item(
name: string,
onClick: OnClickCallback,
shouldAdd?: ShouldAddCallback,
icon?: SVGIcon | string,
disabled?: boolean,
)Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
| onClick | OnClickCallback |
Callback function when the menu item is clicked. |
| shouldAdd | ShouldAddCallback |
Callback function to determine if the menu item should be added. |
| icon | SVGIcon | string |
Icon of the menu item. |
| disabled | boolean |
Whether the menu item is disabled. |
Properties
:::tip
All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information.
:::
| Name | Type | Description |
|---|---|---|
| iconList | readonly SVGIcon[] |
List of icons. |
| name | string |
Name of the menu item. |
| icon | SVGIcon | string |
Icon at the end of the menu item. |
| disabled | boolean |
Whether the menu item is disabled. |
| shouldAdd | ShouldAddCallback |
Callback function to determine if the menu item should be added. |
| onClick | OnClickCallback |
Callback function when the menu item is clicked. |
Methods
`register`
Register the menu item to context menu.
register(): void`deregister`
Remove the menu item from context menu.
deregister(): voidExample
// This function will determine if the selected item is a track
function ifItemIsTrack(uri) {
let uriObj = Spicetify.URI.fromString(uri[0]);
switch (uriObj.type) {
case Type.TRACK:
return true;
}
return false;
}
// Create a new menu item that only appears when a track is selected
const menuItem = new Spicetify.ContextMenu.Item(
"My Menu Item",
() => {
Spicetify.showNotification("My Menu Item clicked!");
},
ifItemIsTrack,
Spicetify.SVGIcons["play"],
false,
);
// Register the menu item
menuItem.register();
// Deregister the menu item
menuItem.deregister();
// Change the menu item's name
menuItem.name = "My New Menu Item";
// Change the menu item's icon
menuItem.icon = "pause"SubMenu
Create a sub menu to contain Items.
Items in subItems array shouldn't be registered.
new Spicetify.ContextMenu.SubMenu(
name: string,
subItems: Iterable,
shouldAdd?: ShouldAddCallback,
disabled?: boolean,
)Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
| subItems | Iterable |
Array of Items to be added to the sub menu. |
| shouldAdd | ShouldAddCallback |
Callback function to determine if the menu item should be added. |
| disabled | boolean |
Whether the menu item is disabled. |
Properties
:::tip
All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information.
:::
| Name | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
| disabled | boolean |
Whether the menu item is disabled. |
| shouldAdd | ShouldAddCallback |
Callback function to determine if the menu item should be added. |
Methods
`addItem`
Add an Item to the sub menu.
addItem(item: Item): void| Parameter | Type | Description |
|---|---|---|
| item | Item |
Item to be added to the sub menu. |
`removeItem`
Remove an Item from the sub menu.
removeItem(item: Item): void| Parameter | Type | Description |
|---|---|---|
| item | Item |
Item to be removed from the sub menu. |
`register`
Register the sub menu to context menu.
register(): void`deregister`
Remove the sub menu from context menu.
deregister(): voidExample
// Create a new menu item
const menuItem = new Spicetify.ContextMenu.Item(
"My Menu Item",
() => {
Spicetify.showNotification("My Menu Item clicked!");
},
() => true,
Spicetify.SVGIcons["play"],
false,
);
// Create a new sub menu
const subMenu = new Spicetify.ContextMenu.SubMenu(
"My Sub Menu",
[menuItem],
() => true,
false,
);
// Register the sub menu
subMenu.register();
// Deregister the sub menu
subMenu.deregister();
// Change the sub menu's name
subMenu.name = "My New Sub Menu";
// Add a new menu item to the sub menu
subMenu.addItem(new Spicetify.ContextMenu.Item(
"My New Menu Item",
() => {
Spicetify.showNotification("My New Menu Item clicked!");
},
() => true,
Spicetify.SVGIcons["play"],
false,
));File: docs/development/api-wrapper/classes/menu.md
title: Menu
Create and prepend custom menu items in the profile menu.
Classes
Item
Single menu item.
new Spicetify.Menu.Item(
name: string,
isEnabled: boolean,
onClick: (self: Item) => void,
icon?: SVGIcon | string,
)Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
| isEnabled | boolean |
Whether the menu item is enabled. |
| onClick | (self: Item) => void |
Callback function when the menu item is clicked. |
| icon | SVGIcon | string |
Icon of the menu item. |
Properties
:::tip
All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information.
:::
| Name | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
| isEnabled | boolean |
Whether the menu item is enabled. |
| icon | SVGIcon | string |
Icon of the menu item. |
Methods
setName
Set the label of the menu item.
setName(name: string): void| Parameter | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
setState
Set the state of the menu item. The item will have a tick icon next to it if its state is enabled.
setState(isEnabled: boolean): void| Parameter | Type | Description |
|---|---|---|
| isEnabled | boolean |
Whether the menu item is enabled. |
setIcon
Set the icon at the end of the menu item.
setIcon(icon: SVGIcon | string): void| Parameter | Type | Description |
|---|---|---|
| icon | SVGIcon | string |
Icon of the menu item. |
register
Register the menu item to the profile menu.
register(): voidderegister
Remove the menu item from the profile menu.
deregister(): voidExample
const item = new Spicetify.Menu.Item("My Item", true, () => {
console.log("My Item is clicked");
});
item.register();
// item.name = "My Item (Updated)";
item.setName("My Item (Updated)");
// item.isEnabled = false;
item.setState(false);
// item.icon = "heart";
item.setIcon("heart");SubMenu
Create a sub menu to contain Item toggles.
Items in subItems array shouldn't be registered.
new Spicetify.Menu.SubMenu(
name: string,
subItems: Item[],
)Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
| subItems | Item[] |
Array of sub menu items. |
Properties
:::tip
All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information.
:::
| Name | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
Methods
setName
Set the label of the menu item.
setName(name: string): void| Parameter | Type | Description |
|---|---|---|
| name | string |
Name of the menu item. |
addItem
Add a sub menu item.
addItem(item: Item): void| Parameter | Type | Description |
|---|---|---|
| item | Item |
Sub menu item. |
removeItem
Remove a sub menu item.
removeItem(item: Item): void| Parameter | Type | Description |
|---|---|---|
| item | Item |
Sub menu item. |
register
Register the menu item to profile menu.
register(): voidderegister
Remove the menu item from profile menu.
deregister(): voidExample
const item1 = new Spicetify.Menu.Item("My Item 1", true, () => {
console.log("My Item 1 is clicked");
});
const item2 = new Spicetify.Menu.Item("My Item 2", true, () => {
console.log("My Item 2 is clicked");
});
const subMenu = new Spicetify.Menu.SubMenu("My Sub Menu", [item1, item2]);
subMenu.register();
// subMenu.name = "My Sub Menu (Updated)";
subMenu.setName("My Sub Menu (Updated)");
// subMenu.addItem(item3);
subMenu.addItem(
new Spicetify.Menu.Item("My Item 3", true, () => {
console.log("My Item 3 is clicked");
})
);File: docs/development/api-wrapper/classes/playbar.md
title: Playbar
Create buttons in the player.
namespace Playbar {
class Button {
constructor(label: string, icon: SVGIcon | string, onClick: (self: Button) => void, disabled?: boolean, active?: boolean, registerOnCreate?: boolean);
label: string;
icon: string;
onClick: (self: Button) => void;
disabled: boolean;
active: boolean;
element: HTMLButtonElement;
tippy: any;
register: () => void;
deregister: () => void;
}
class Widget {
constructor(label: string, icon: SVGIcon | string, onClick?: (self: Widget) => void, disabled?: boolean, active?: boolean, registerOnCreate?: boolean);
label: string;
icon: string;
onClick: (self: Widget) => void;
disabled: boolean;
active: boolean;
element: HTMLButtonElement;
tippy: any;
register: () => void;
deregister: () => void;
}
};`Button`
Create buttons next to the player extra control buttons (e.g. queue, lyrics, Now Playing View, etc.).
This is useful for creating buttons whose actions have an impact on or relate to the player, and are generally dynamic/stateful, such as a button that toggles the player's loop mode.
Parameters
| Parameter | Type | Description |
|---|---|---|
| label | string |
Label of the button. |
| icon | SVGIcon | string |
Icon of the button. |
| onClick | (self: Button) => void |
Callback function when the button is clicked. |
| disabled | boolean | undefined |
Whether the button is disabled. |
| active | boolean | undefined |
Whether the button is active. |
| registerOnCreate | boolean | undefined |
Whether the button should be registered to the player on creation. |
Properties
:::tip
All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information.
:::
| Name | Type | Description |
|---|---|---|
| label | string |
Label of the button. |
| icon | string |
Icon of the button. |
| disabled | boolean |
Whether the button is disabled. |
| active | boolean |
Whether the button is active. |
| onClick | (self: Button) => void |
Callback function when the button is clicked. |
| element | HTMLButtonElement |
HTML element of the button. |
| tippy | any |
Tippy instance of the button. For more information, see Tippy.js. |
Methods
`register`
Register the button to the player.
register(): void;`deregister`
Deregister the button from the player.
deregister(): void;Example
:::caution
Tippy, onclick or any other click events will not work if disabled is set to true. You will need to manually enable the button inside your extension.
This is due to the limitations of Tippy itself and how HTML elements work.
:::
// By default, the button will be registered to the player on creation.
// You can disable this by setting registerOnCreate to false.
// Each button comes with a preconfigured Tippy instance that aims to mimic the original Spotify tooltip.
const button = new Spicetify.Playbar.Button(
"My Button",
"play",
(self) => {
// Do something when the button is clicked.
},
false, // Whether the button is disabled.
false, // Whether the button is active.
);
// You can also register the button to the player later.
button.register();
// Remove the button from the player when it is no longer needed.
button.deregister();
// If you don't want to remove the button entirely, you can also disable it.
button.disabled = true;
// Change button properties.
// Changing label will also change the tooltip content.
button.label = "Hello world!";
button.icon = "play";
// You can also set properties of the HTML element.
button.element.style.color = "red";
button.element.oncontextmenu = () => {
Spicetify.showNotification("You right-clicked me!");
};
button.element.addEventListener("click", () => {
// Do something else.
Spicetify.showNotification("You clicked me!");
});
// You can also change properties of the Tippy instance. For more information, see https://atomiks.github.io/tippyjs/v6/tippy-instance/.
button.tippy.setContent("Hello world!");
// Or if you want to use HTML.
button.tippy.setProps({
content: "**Hello world!**",
allowHTML: true,
});`Widget`
Create widgets in the player, next to track information similar to the Heart button.
This is useful for creating buttons whose actions have an impact on the state of the player and the track being played, such as a button that adds the current track to a playlist.
Parameters
| Parameter | Type | Description |
|---|---|---|
| label | string |
Label of the widget. |
| icon | SVGIcon | string |
Icon of the widget. |
| onClick | (self: Widget) => void |
Callback function when the widget is clicked. |
| disabled | boolean | undefined |
Whether the widget is disabled. |
| active | boolean | undefined |
Whether the widget is active. |
| registerOnCreate | boolean | undefined |
Whether the widget should be registered to the player on creation. |
Properties
:::tip
All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information.
:::
| Name | Type | Description |
|---|---|---|
| label | string |
Label of the widget. |
| icon | string |
Icon of the widget. |
| disabled | boolean |
Whether the widget is disabled. |
| active | boolean |
Whether the widget is active. |
| onClick | (self: Widget) => void |
Callback function when the widget is clicked. |
| element | HTMLButtonElement |
HTML element of the widget. |
| tippy | any |
Tippy instance of the widget. For more information, see Tippy.js. |
Methods
`register`
Register the widget to the player.
register(): void;`deregister`
Deregister the widget from the player.
deregister(): void;Example
:::caution
Tippy, onclick or any other click events will not work if disabled is set to true. You will need to manually enable the widget inside your extension.
This is due to the limitations of Tippy itself and how HTML elements work.
:::
// By default, the widget will be registered to the player on creation.
// You can disable this by setting registerOnCreate to false.
// Each widget comes with a preconfigured Tippy instance that aims to mimic the original Spotify tooltip.
const widget = new Spicetify.Playbar.Widget(
"My Widget",
"play",
(self) => {
// Do something when the widget is clicked.
},
false, // Whether the widget is disabled.
false, // Whether the widget is active.
);
// You can also register the widget to the player later.
widget.register();
// Remove the widget from the player when it is no longer needed.
widget.deregister();
// Change widget properties.
// Changing label will also change the tooltip content.
widget.label = "Hello world!";
widget.icon = "play";
// You can also set properties of the HTML element.
widget.element.style.color = "red";
widget.element.oncontextmenu = () => {
Spicetify.showNotification("You right-clicked me!");
};
widget.element.addEventListener("click", () => {
// Do something else.
Spicetify.showNotification("You clicked me!");
});
// You can also change properties of the Tippy instance. For more information, see https://atomiks.github.io/tippyjs/v6/tippy-instance/.
widget.tippy.setContent("Hello world!");
// Or if you want to use HTML.
widget.tippy.setProps({
content: "**Hello world!**",
allowHTML: true,
});