// DOCUMENTATION

Ship a player
in 30 seconds.

Create a player in the dashboard, copy one <script> tag, paste it anywhere. No build step, no framework, no uploads — stream straight from your own audio URLs. Here's everything the embed can do.

// QUICK_START

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.

01

CREATE A PLAYER

Open the dashboard, hit New Player, name it, and paste your first track's audio and cover URLs.

02

COPY THE SNIPPET

Each player shows a ready-to-paste <script> tag with its data-player ID already filled in.

03

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_SNIPPET

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

AttributeRequiredWhat 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.

// SKINS

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 + playlist

MINI

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 list

COVER

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 valuePlaylistLayoutUse it for
classicYesCover + meta + transport + scrollable listAlbums, mixtapes, full catalogues
miniNoSingle compact rowSidebars, footers, blog posts
coverNoLarge centered art over controlsSingle 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).

// AUTO_THEME

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 --accent CSS 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.

// PLAYLISTS

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 pos field 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 /play beacon.
🎛️

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.

OPEN THE DASHBOARD →

// API_REFERENCE

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.

RouteAuthNotes
GET /api/player/:idPublicRead player config + tracks (used by the embed).
POST /api/player/:id/playPublicPlay-count beacon. Fire-and-forget.
AUTH Owner routesBearerCreate / 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.

// TROUBLESHOOTING

FAQ & FIXES

Check three things, in order: (1) the 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.
Auto-theme reads the cover pixels on a canvas, which needs CORS. If your cover host doesn't send 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.
Usually one of: (1) Autoplay policy — browsers block sound until the user interacts, so an 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.
Yes — paste the snippet once per player, each with its own 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.)
No. Resona never stores audio — you paste external URLs and the player streams from them directly. Host your files wherever you like (CDN, object storage, your own server); just make sure they're served over HTTPS with CORS/range support for the smoothest playback.

READY? PASTE AND IT PLAYS.

Create your first player free — one player, unlimited URL-streamed tracks, cover-driven theming, no card.