THREE STEPS TO LIVE
You'll have a working player on your page before your coffee's cold. No account migration, no SDK install — just the dashboard and a snippet.
CREATE A PLAYER
Open the dashboard, hit New Player, name it, and paste your first track's audio and cover URLs.
COPY THE SNIPPET
Each player shows a ready-to-paste <script> tag with its data-player ID already filled in.
PASTE IT IN
Drop the tag into your HTML where you want the player to appear. It renders in place on load — that's it.
The script renders the player exactly where you paste it. Put the tag inside the container that should hold the player — no target div or extra markup required.
THE EMBED SNIPPET
One tag does all of it. Copy this from the dashboard with your real ID swapped in:
embed.html<!-- drop where you want the player --> <script src="https://cil101101.com/shop/resona/embed/p.js" data-player="PLAYER_ID"></script>
ATTRIBUTES
| Attribute | Required | What it does |
|---|---|---|
data-player |
Yes | The player's unique ID. The script fetches that player's config and tracks from the API and renders it in place. Copy it straight from the dashboard. |
data-api |
Optional | Override the API base URL (default https://resona.a0916897655.workers.dev). Point it at a self-hosted or staging backend, e.g. data-api="https://api.staging.example.com". |
With data-api set, the loader resolves the player from {data-api}/api/player/{id} instead of the default host. Everything else — skin, accent, tracks — still comes from that player's saved config.
You can drop the same snippet on as many pages as you like. Editing the player in the dashboard updates every embed at once — no re-paste, no cache-bust.
THREE SKINS, ONE PLAYER
Pick a skin per player in the dashboard. Every skin shares the same engine — auto-theme, persistent playback, synced lyrics — they just differ in footprint.
CLASSIC
The full experience: cover, title/artist, transport, progress bar, and the scrollable playlist. Best when you want listeners to browse the whole queue.
full + playlistMINI
A compact single-row bar — cover, now-playing, play/pause and progress. No playlist. Perfect for sidebars, footers, or a "now playing" strip.
compact · no listCOVER
Large, centered album art with controls beneath — the artwork is the hero. Made for release pages, single drops, and link-in-bio embeds.
large art| Skin value | Playlist | Layout | Use it for |
|---|---|---|---|
classic | Yes | Cover + meta + transport + scrollable list | Albums, mixtapes, full catalogues |
mini | No | Single compact row | Sidebars, footers, blog posts |
cover | No | Large centered art over controls | Single releases, link-in-bio |
The skin is stored on the player, so the API returns it as the skin field — the loader reads it and renders the matching layout. Custom skins and overrides are a Pro feature (see below).
THE COVER DRIVES THE COLOR
Resona's signature trick: on each track change the player samples the dominant color from that track's cover art on a canvas, boosts its saturation, and hot-swaps it in as the live accent. Progress bar, play button glow, controls and ambient tint all shift to match the artwork — so every track ships its own mood, automatically.
HOW IT WORKS
- The cover loads with
crossorigin="anonymous"so it can be read on a canvas. - Mid-luminance pixels are averaged (pure black and blown-out white are ignored) to find the real dominant tone.
- That color is pushed onto the
--accentCSS variable, and everything tinted by it transitions smoothly. - If the canvas is CORS-tainted (cover host sends no CORS headers), it silently falls back to the player's saved
accent.
OVERRIDE IT
Want your brand color locked in regardless of artwork? Set a fixed accent on the player in the dashboard. When an accent is set, the API returns it as accent and the player uses it as-is — auto-extraction is skipped, so the look stays consistent across every track.
Leave the accent blank to let each cover paint the player. Set it to a hex like #06B6D4 to pin your brand. Fixed accents and custom skins are included with CC All-Access.
MANAGING PLAYLISTS
Everything about a player — its tracks, order, skin, accent, autoplay, and lyrics — lives in the dashboard. You never touch the embed again after pasting it; edits propagate to every page running that player.
IN THE DASHBOARD YOU CAN
- Add tracks by pasting an audio URL plus a cover URL — no uploads, no storage limits.
- Reorder the queue by drag — the
posfield sets play order in the API response. - Attach synced lyrics (an LRC string per track) for line-by-line highlight and auto-scroll (Pro).
- Set the skin and accent, toggle
autoplay, and remove Resona branding (Pro). - Watch play analytics — per-player play counts roll up from the
/playbeacon.
Free covers one player with unlimited URL-streamed tracks. Unlimited players, custom skins, fixed accents, synced LRC lyrics, advanced analytics + API access, and branding removal are all in CC All-Access (£6.99/mo · £59/yr) — one subscription, every CC platform.
PUBLIC API
The embed loader talks to a small public API. You can call it directly too — for a custom front-end, server-side render, or your own analytics. The base URL is https://resona.a0916897655.workers.dev (override per-embed with data-api).
GET /api/player/:id
Returns a player's public config and ordered tracks. No auth — this is what the embed fetches on load.
GET /api/player/abc123 → 200{ "id": "abc123", "name": "Summer Singles", "skin": "cover", // classic | mini | cover "accent": null, // hex to pin, or null = auto-theme "autoplay": false, "tracks": [ { "pos": 1, "title": "Midnight Drive", "artist": "Kō Aoki", "src_url": "https://cdn.example.com/midnight.mp3", "cover_url": "https://cdn.example.com/midnight.jpg", "lrc": "[00:12.40]Headlights on the coast road\n..." } ] }
POST /api/player/:id/play
A fire-and-forget play-count beacon — the player pings it when a track starts. No auth, no body required; send the track position if you want per-track counts.
POST /api/player/abc123/play// request body (optional) { "pos": 1 } // → 204 No Content
Call it yourself with a one-liner:
beacon.jsfetch("https://resona.a0916897655.workers.dev/api/player/abc123/play", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ pos: 1 }) });
OWNER ROUTES
Creating players, editing tracks, reading analytics and every other write operation lives behind owner routes. These require a Firebase Bearer token and are managed for you inside the dashboard — there's no public write API to wire up by hand.
| Route | Auth | Notes |
|---|---|---|
GET /api/player/:id | Public | Read player config + tracks (used by the embed). |
POST /api/player/:id/play | Public | Play-count beacon. Fire-and-forget. |
| AUTH Owner routes | Bearer | Create / update / delete players + tracks, analytics. Authorization: Bearer <firebase-token> — handled in the dashboard. |
Never ship a Firebase token in client-side embed code. The public GET and play routes are all the browser needs — owner routes are for server-to-server or the dashboard only.
FAQ & FIXES
data-player ID matches a player in your dashboard exactly — IDs are case-sensitive; (2) the <script> tag is actually in the page where you want the player to render, not stripped by a CMS sanitizer; (3) open the browser console — a 404 on /api/player/:id means a wrong ID, and a network/CSP error means resona.a0916897655.workers.dev is blocked. If your site has a strict Content-Security-Policy, allow script-src https://resona.a0916897655.workers.dev and connect-src https://resona.a0916897655.workers.dev.
Access-Control-Allow-Origin, the canvas becomes "tainted" and the browser blocks the read — the player silently falls back to the saved accent (or the cyan default). Fix it by serving covers from a host that returns CORS headers (most CDNs and object stores do), or just set a fixed accent on the player and skip extraction entirely. Note: a fixed accent always wins — clear it if you want covers to drive the color.
autoplay player may load muted/paused; a single click on play starts it. (2) The audio URL itself — open src_url directly in a tab; if it 403s, needs a login, or isn't a browser-playable format (stick to MP3/AAC/OGG), the player can't help. (3) Mixed content — an http:// audio URL on an https:// page is blocked; serve audio over HTTPS. (4) Range requests — seeking needs the host to support HTTP range; without it audio plays but the scrubber may misbehave.
data-player ID, in the spot you want it. They render independently. (The free plan covers one player total across your account; CC All-Access removes the limit.)
READY? PASTE AND IT PLAYS.
Create your first player free — one player, unlimited URL-streamed tracks, cover-driven theming, no card.