Tue, 06 May 2025 00:00:00 GMT
Decentralized Browser-Native Music Player SDK Spec Version : 1.0
Audience : Senior Engineers
Author : me
CO-Author : LLM - full conversation here
Overview
This SDK enables artists to self-host music players on their own websites with full control over content, monetization, and UI. It allows users to create personal, browser-stored playlists across multiple artist sites — without any central authority or cross-site tracking.
A shared iframe (on a neutral origin) acts as a client-side data store and playback engine, communicating with host pages via postMessage
. All track storage, playlist assembly, and cross-tab coordination happen locally in the browser.
System Goals
Artist-owned hosting and access control
User-controlled browser-local playlists
No login, cloud storage, or vendor lock-in
Cross-origin, cross-tab interop
Full support for albums, not just single tracks
Support for purchasing, subscriptions, and free tracks
Optional headless or custom player UI
Secure origin-based access enforcement
Optional external backup/sync of user library
Architecture Overview
... something like this
+----------------------------+ +--------------------------+
| artist-a.com frontend | | artist-b.net frontend |
| - Loads sdk.js | | - Loads sdk.js |
| - Calls sdk.sync(album) | | - Calls sdk.sync(album) |
| | | |
| +---------------+ | | +---------------+ |
| | player iframe |<------------->| player iframe | |
| | (player-hub) | Broadcast +---------------+ |
| +---------------+ Channel ^ ^ |
| ^ ^ | | | | |
+-------------|------|-------+ +------|-----|-------------+
| | | |
| +----------------------------+ |
| | artist-a.com backend | |
| | - Serves authorized media | |
| +----------------------------+ |
| |
+-------------------------------------+
| other-media-backend.io |
| - Serves authorized media |
+-------------------------------------+
Components
1. sdk.js
(Vanilla JS SDK, framework-agnostic)
Automatically mounts the shared iframe
Provides a JS API to host pages
Handles message protocol and event subscriptions
Can be wrapped by React/Vue/Svelte adapters
SDK Public API:
playerSDK.init (options ?: {
headless ?: boolean ,
allowOrigins ?: string [],
ui ?: "compact" | "full" | "none"
})
playerSDK.sync ({
collection : string ,
title ?: string ,
cover ?: string ,
origin ?: string ,
tracks : TrackMetadata []
})
playerSDK.on (event : string , callback : (payload ) => void )
playerSDK.requestPlayback (trackId : string )
2. player.html
(Shared iframe on https://player-hub.net/player.html
)
Owns the <audio>
element
Stores all track metadata and albums in IndexedDB
Renders secure modals for track sync confirmations
Enforces track origin allowlists
Handles playback logic
Emits state changes via postMessage
and BroadcastChannel
Supports backup/restore via external adapters
Storage Schema:
type TrackMetadata = {
id : string
title : string
url : string
artist : string
cover ?: string
duration ?: number
access ?: {
type : "free" | "subscription" | "purchase"
allowedOrigins ?: string []
auth ?: {
method : "token" | "header" | "cookie"
endpoint : string
}
}
}
Security & Origin Validation
The origin of the embedding page is determined via event.origin
from the postMessage
API.
This value is never supplied by the sender, ensuring it's not spoofable.
All access enforcement uses the host portion of event.origin
, ignoring the protocol.
Message Protocol
Host → Iframe:
{ type : "sync" , collection : string , tracks : TrackMetadata [], title ?: string , cover ?: string }
{ type : "play" , trackId : string }
{ type : "pause" }
{ type : "requestPlaylist" }
{ type : "seek" , seconds : number }
Iframe → Host:
{ type : "confirmationRequired" , collection : string , tracks : TrackMetadata [] }
{ type : "syncComplete" , collection : string , acceptedIds : string [] }
{ type : "playlistUpdated" , playlist : TrackMetadata [] }
{ type : "playbackState" , playing : boolean , trackId ?: string }
{ type : "authRequired" , trackId : string , reason : string }
Access Models & Auth Handling
Free Track:
access.type = "free"
Iframe streams directly from URL
Protected Track (purchase/subscription):
access.type = "purchase" | "subscription"
Iframe checks auth.endpoint
on the track’s origin (from event.origin
)
Server may return:
A signed URL
A bearer token
Confirmation & Permissions
Tracks are never stored or played without user confirmation
Iframe displays a native modal UI (never rendered by host)
Can use window.open()
as fallback
Cross-Tab Communication
Uses BroadcastChannel('player-hub')
:
Syncs playback state
Shares playlist updates
External Backup / Sync Layer
The iframe supports registering an external sync adapter:
window .playerHub .registerSyncAdapter ({
name : "dropbox" | "solid" | "localExport" ,
export (): Promise <BackupData >,
import (data : BackupData ): Promise <void >
})
Optional: Central Player Page
URL: https://player-hub.net/library
Neutral user interface
View/edit playlist
Manual backup/restore
Future Considerations
Album schema spec
Playlist file format (playlist.v1.json
)
Public read-only playlist sharing
Token expiration + refresh
Streamlined UX for first-time confirmations
Can we do effective DRM?