Now Playing API
Overview
The Now Playing API provides access to LedFx’s centralized media playback state. It exposes the current track metadata, artwork reference, extracted gradient information, and service configuration.
The Now Playing Service is provider-neutral — it receives normalized metadata from any configured source (currently Sendspin) and presents a single unified view. Effects and other consumers remain unaware of which provider supplied the data.
Base URL: http://<host>:<port>/api/now-playing
Key Concepts
Metadata: Track title, artist, album, duration, and playback position from the active provider.
Artwork: Album art stored as a managed asset at
assets/now_playing/now_playing.{ext}, with automatic gradient extraction.Gradients: LED-optimized color gradients extracted from artwork in three variants (
led_safe,led_punchy,led_max). These can be automatically applied to target virtuals.Configuration: Controls gradient application, track text display, and album art display behavior.
Data Model
Now Playing State
The GET response combines the current playback state with the service configuration.
{
"active_source_id": "sendspin",
"metadata": { ... },
"artwork": { ... },
"selected_gradient_variant": "led_punchy",
"current_gradient": "linear-gradient(90deg, rgb(180,40,20) 0%, rgb(220,160,50) 50%, rgb(40,80,160) 100%)",
"updated_at": 1716000000.123,
"config": { ... }
}
Top-level Fields:
Field |
Type |
Description |
|---|---|---|
|
string | null |
Provider currently supplying metadata (e.g. |
|
object | null |
Current track metadata. Null when no track is playing. |
|
object | null |
Current artwork reference with gradient data. Null when no artwork is available. |
|
string |
Active gradient variant: |
|
string | null |
Resolved CSS gradient string for the selected variant. Null when no artwork/gradients available. |
|
float | null |
Unix timestamp of the last state update. |
|
object |
Current service configuration (see Configuration section). |
Metadata Object
{
"source_id": "sendspin",
"title": "Midnight City",
"artist": "M83",
"album": "Hurry Up, We're Dreaming",
"duration": 244.0,
"position": 42.5,
"track_id": "spotify:track:6GyFP1nfCDB8lbD2bG0Hq9",
"artwork_url": "https://i.scdn.co/image/ab67616d0000b273...",
"artwork_hash": "a1b2c3d4e5f6g7h8",
"updated_at": 1716000000.123
}
Field |
Type |
Description |
|---|---|---|
|
string |
Provider that supplied this metadata. |
|
string | null |
Track title. |
|
string | null |
Track artist. |
|
string | null |
Album name. |
|
float | null |
Track duration in seconds. |
|
float | null |
Current playback position in seconds. |
|
string | null |
Provider-specific track identifier. |
|
string | null |
Original artwork URL from the provider. |
|
string | null |
Hash for artwork change detection. |
|
float | null |
Unix timestamp when this metadata was last updated. |
Artwork Object
{
"source_id": "sendspin",
"url": "https://i.scdn.co/image/ab67616d0000b273...",
"cache_key": "C:/Users/user/.ledfx/assets/now_playing/now_playing.jpg",
"content_type": "image/jpeg",
"hash": "a1b2c3d4e5f6g7h8",
"width": 640,
"height": 640,
"gradients": {
"led_safe": {
"gradient": "linear-gradient(90deg, rgb(180,40,20) 0%, ...)"
},
"led_punchy": {
"gradient": "linear-gradient(90deg, rgb(220,50,10) 0%, ...)"
},
"led_max": {
"gradient": "linear-gradient(90deg, rgb(255,0,0) 0%, ...)"
},
"metadata": {
"pattern": "interleaved",
"has_dominant_background": true
}
}
}
Field |
Type |
Description |
|---|---|---|
|
string |
Provider that supplied this artwork. |
|
string | null |
Original artwork URL (null for byte-based artwork). |
|
string | null |
Absolute path to the stored artwork file. |
|
string | null |
MIME type of the artwork image. |
|
string | null |
Content hash for change detection (SHA-256 prefix). |
|
int | null |
Image width in pixels. |
|
int | null |
Image height in pixels. |
|
object | null |
Extracted gradient variants and extraction metadata. |
Gradient Variants
Three LED-optimized gradient variants are extracted from each artwork image:
Variant |
Description |
|---|---|
|
Closest to the source image colors. Conservative but accurate. |
|
Enhanced saturation and contrast. Best default for physical LEDs. |
|
Maximum saturation and vibrancy. High-impact but may be too aggressive. |
Each variant contains a gradient field with a CSS linear-gradient(...) string that can be used directly by LedFx effects.
Configuration Object
{
"gradient": {
"enabled": true,
"variant": "led_punchy",
"virtual_ids": []
},
"track_text": {
"enabled": false,
"duration": 8,
"virtual_ids": [],
"preset": ""
},
"album_art": {
"enabled": false,
"duration": 10,
"virtual_ids": []
}
}
Gradient Configuration
Field |
Type |
Default |
Description |
|---|---|---|---|
|
bool |
|
Whether to apply extracted gradients to target virtuals on track change. |
|
string |
|
Which gradient variant to use. One of: |
|
array of strings |
|
Virtual IDs to target for gradient application. Empty array means all virtuals. |
Track Text Configuration
Field |
Type |
Default |
Description |
|---|---|---|---|
|
bool |
|
Whether to switch the target virtual to the text effect on each track change. |
|
int |
|
Duration in seconds before restoring the previous effect (0–60). A value of |
|
array of strings |
|
Target matrix virtual IDs for text display. |
|
string |
|
Preset name to apply when switching to the text effect. Empty string uses the effect’s default configuration. Can be a built-in LedFx preset or a user-defined preset for the texter effect |
Album Art Configuration
Field |
Type |
Default |
Description |
|---|---|---|---|
|
bool |
|
Whether to switch the target virtual to the image effect on each artwork change. |
|
int |
|
Duration in seconds before restoring the previous effect (0–60). A value of |
|
array of strings |
|
Target matrix virtual IDs for art display. |
Endpoints
Get Now Playing State
Returns the current playback state, artwork, gradients, and configuration.
GET /api/now-playing
Success Response (bare response — no status wrapper):
When a track is playing:
{
"active_source_id": "sendspin",
"metadata": {
"source_id": "sendspin",
"title": "Midnight City",
"artist": "M83",
"album": "Hurry Up, We're Dreaming",
"duration": 244.0,
"position": 42.5,
"track_id": null,
"artwork_url": "https://example.com/art.jpg",
"artwork_hash": "a1b2c3d4",
"updated_at": 1716000000.123
},
"artwork": {
"source_id": "sendspin",
"url": "https://example.com/art.jpg",
"cache_key": "/home/user/.ledfx/assets/now_playing/now_playing.jpg",
"content_type": "image/jpeg",
"hash": "a1b2c3d4",
"width": 640,
"height": 640,
"gradients": {
"led_safe": {
"gradient": "linear-gradient(90deg, rgb(180,40,20) 0%, rgb(200,150,40) 50%, rgb(40,80,160) 100%)"
},
"led_punchy": {
"gradient": "linear-gradient(90deg, rgb(220,50,10) 0%, rgb(240,180,20) 50%, rgb(20,60,200) 100%)"
},
"led_max": {
"gradient": "linear-gradient(90deg, rgb(255,0,0) 0%, rgb(255,200,0) 50%, rgb(0,40,255) 100%)"
},
"metadata": {
"pattern": "interleaved",
"has_dominant_background": true
}
}
},
"selected_gradient_variant": "led_punchy",
"current_gradient": "linear-gradient(90deg, rgb(220,50,10) 0%, rgb(240,180,20) 50%, rgb(20,60,200) 100%)",
"updated_at": 1716000000.123,
"config": {
"gradient": {
"enabled": true,
"variant": "led_punchy",
"virtual_ids": []
},
"track_text": {
"enabled": false,
"duration": 8,
"virtual_ids": [],
"preset":""
},
"album_art": {
"enabled": false,
"duration": 10,
"virtual_ids": []
}
}
}
When no track is playing (idle state):
{
"active_source_id": null,
"metadata": null,
"artwork": null,
"selected_gradient_variant": "led_punchy",
"current_gradient": null,
"updated_at": null,
"config": {
"gradient": {
"enabled": true,
"variant": "led_punchy",
"virtual_ids": []
},
"track_text": {
"enabled": false,
"duration": 8,
"virtual_ids": [],
"preset":""
},
"album_art": {
"enabled": false,
"duration": 10,
"virtual_ids": []
}
}
}
Update Configuration
Updates the Now Playing configuration. Accepts a partial or full configuration object; unspecified sections retain their current values.
PUT /api/now-playing
Request Body:
Partial update (only gradient section):
{
"gradient": {
"enabled": false,
"variant": "led_max"
}
}
Full update (all sections):
{
"gradient": {
"enabled": true,
"variant": "led_punchy",
"virtual_ids": ["wled-living-room", "wled-bedroom"]
},
"track_text": {
"enabled": true,
"duration": 5,
"virtual_ids": ["matrix-panel"],
"preset":""
},
"album_art": {
"enabled": false,
"duration": 10,
"virtual_ids": []
}
}
Success Response:
{
"status": "success",
"payload": {
"type": "success",
"reason": "Now Playing configuration updated.",
"data": {
"gradient": {
"enabled": true,
"variant": "led_punchy",
"virtual_ids": ["wled-living-room", "wled-bedroom"]
},
"track_text": {
"enabled": true,
"duration": 5,
"virtual_ids": ["matrix-panel"],
"preset":""
},
"album_art": {
"enabled": false,
"duration": 10,
"virtual_ids": []
}
}
}
}
Validation Error (invalid variant):
{
"status": "failed",
"payload": {
"type": "error",
"reason": "value is not allowed for dictionary value @ data['gradient']['variant']"
}
}
Validation Error (duration out of range):
{
"status": "failed",
"payload": {
"type": "error",
"reason": "value must be at most 60 for dictionary value @ data['track_text']['duration']"
}
}
Invalid JSON:
{
"status": "failed",
"payload": {
"type": "error",
"reason": "JSON Decode Error"
}
}
Non-object body:
{
"status": "failed",
"payload": {
"type": "error",
"reason": "Request body must be a JSON object."
}
}
Configuration Behavior
Gradient Application
When gradient.enabled is true and a new artwork gradient is extracted, the service automatically applies the selected gradient variant to the target virtuals. This uses the same code path as the global effects API (PUT /api/effects with apply_global action):
Resolves the CSS gradient string into color stops
Samples color groups from the gradient
Updates each active effect’s gradient and color keys
Persists the updated effect configurations
If gradient.virtual_ids is empty, all virtuals with active effects are updated. If specific IDs are provided, only those virtuals are targeted.
Partial Updates
The PUT endpoint supports section-level partial updates:
{"gradient": {"enabled": false}}
This disables gradient application without affecting track_text or album_art configuration. Top-level sections omitted from the request are preserved as-is. Within each section, the update is also a partial-update merge: fields not included in the request retain their current values rather than being reset to schema defaults. For example, sending {"track_text": {"enabled": true}} merges with the existing track_text config, leaving duration, virtual_ids, and preset unchanged.
Variant Switching
When the gradient variant is changed (e.g. from led_punchy to led_max), the service immediately re-resolves the gradient from the cached artwork metadata — no re-download or re-extraction needed. If gradient application is enabled, the new variant is applied to target virtuals.
Events
The Now Playing Service fires the following LedFx events that frontends and other systems can subscribe to:
Event |
Fired When |
|---|---|
|
Track identity changes (title, artist, album, or track_id). |
|
Any metadata update (including position-only updates). |
|
New artwork is downloaded and stored. |
|
The resolved gradient string changes (new artwork or variant switch). |
|
The active provider is cleared (e.g. disconnected). |
Provider Integration
The Now Playing Service currently supports Sendspin as a metadata provider. When connected to a Sendspin server, track metadata and artwork URLs are automatically forwarded to the Now Playing Service.
Sendspin provides:
Track title, artist, album
Artwork URL
Track duration and playback progress
Track ID
Provider integration is automatic — no additional configuration is required beyond connecting to a Sendspin server via the Sendspin Servers API.
Usage Examples
Poll Current State
curl http://localhost:8888/api/now-playing
Enable Gradient Application to Specific Virtuals
curl -X PUT http://localhost:8888/api/now-playing \
-H "Content-Type: application/json" \
-d '{
"gradient": {
"enabled": true,
"variant": "led_punchy",
"virtual_ids": ["wled-living-room", "wled-bedroom"]
}
}'
Disable Gradient Application
curl -X PUT http://localhost:8888/api/now-playing \
-H "Content-Type: application/json" \
-d '{"gradient": {"enabled": false}}'
Switch to Maximum Saturation Variant
curl -X PUT http://localhost:8888/api/now-playing \
-H "Content-Type: application/json" \
-d '{"gradient": {"variant": "led_max"}}'
Enable Track Text Display
curl -X PUT http://localhost:8888/api/now-playing \
-H "Content-Type: application/json" \
-d '{
"track_text": {
"enabled": true,
"duration": 5,
"virtual_ids": ["matrix-panel"]
"preset":""
}
}'