Appearance
Are you an LLM? You can read better optimized documentation at /docs/developers/embedded-player.md for this page in Markdown format
Embedded Player
The show.fm player is a self-contained web component you can put on any website. One script tag, one element: the player fetches everything it needs from the public API and renders a fully accessible audio player styled with the show's brand colour.
Anyone may embed any published show.fm show. No permission or account needed.
Use the script embed (recommended)
html
<script async src="https://embed.cdn.media/player/v1.js"></script>
<showfm-player
episode="3f9d2b6e-8c41-4a57-b2f0-6e5d4c3b2a19"
style="display:block;min-height:291px"
>
<a href="https://acme-radio-hour.show.fm/e/the-big-launch">
Listen to The Big Launch on show.fm
</a>
</showfm-player>The inline min-height reserves the player's exact final height before the script loads, so the embed causes no cumulative layout shift (CLS). Use 291px for the standard player and 101px for size="compact"; if your plan removes the "Powered by show.fm" footer, use 252px / 83px instead. The embed codes generated in the show.fm dashboard include the right value automatically.
Why the script embed is the recommended mode: the player renders in your page's DOM (inside an open shadow root), not in a cross-origin iframe. That's a real accessibility win: it follows your visitors' page zoom, user stylesheets, and high-contrast settings, and sits naturally in the page's focus order for keyboard and screen-reader users.
The link inside the element is progressive-enhancement fallback content: it shows until the script upgrades the element, remains for no-JS visitors, and is offered again if the episode can't be loaded. Always include one.
The script is versioned and safe to load once per page no matter how many players you place. It only ever receives backwards-compatible updates; breaking player changes would ship as a new URL (/player/v2.js). See versioning.
Older embed codes still work
Embed codes copied before the show.fm rename use the element name <podcasterplus-player> and an older script address. Both keep working: the script registers the old element name alongside <showfm-player>, and the old address still serves the same script. You do not need to update existing embeds.
Element attributes
| Attribute | Values | Default | Purpose |
|---|---|---|---|
episode | episode UUID | (none) | Play one specific episode. Takes precedence over podcast |
podcast | podcast slug or UUID | (none) | Latest-episode mode: always plays the show's newest published episode |
theme | auto | light | dark | (none: follow the show) | Pin this embed's theme. Absent, the player follows the show's theme setting (player_theme in the API payload). auto pins the embed to the visitor's prefers-color-scheme |
size | standard | compact | standard | standard is the full card; compact is a two-row inline bar |
accent | #hex colour | (none: follow the show) | Pin this embed's accent colour, overriding the show's player-colour setting |
wave | true | false | (none: follow the show) | Pin the waveform on or off. Absent, the player follows the show's waveform setting (player_waveform in the API payload). false renders a plain progress bar |
api | endpoint origin | production API | Endpoint override, for development and testing only |
heading-level | 2 to 6 | (none) | Wraps the episode title in a heading of that level. Absent, the title is a link and no heading is emitted |
transcript | on | open | reader | (none) | Adds a Transcript button to the standard player. on opens the follow-along transcript under the player and open opens it at once. reader opens it full screen instead. Offered only when the audio and transcript are on show.fm |
speaker-photos | on | off | on | In the full-screen reader, each speaker's photo from the episode's credits sits beside their lines. off shows their initials instead |
mini-player | on | off | off | When the visitor scrolls the playing player out of view, the page's mini-player takes over its audio |
mini-player-position | left | right | right | The corner the collapsed mini-player sits in |
credit | auto | on | off | auto | The "Powered by show.fm" footer. See About "Powered by show.fm" |
load | click | (none) | Shows a placeholder and requests nothing until it is pressed. See Load on click |
lang | language tag | the page's lang | The language of the player's own words. English, German and French are included |
strings | JSON object | (none) | Overrides for the player's own words, by key. Usually set as the element's strings property in JavaScript |
The same script also registers an episode list, a play button and a follow-along transcript. See Embed on Any Site for all four elements and recipes for HTML, Astro, Next.js and Laravel.
Provide either episode or podcast. Episode embeds reference the episode's UUID (find it in id from the API, or copy ready-made code from the dashboard's Share & embed card). UUIDs stay stable even if the podcaster later edits the episode's slug, so embeds pasted into third-party sites never break.
Player features
Play/pause, a waveform scrubber with an accessible seek slider (keyboard + spoken position), −15 s / +30 s skips, elapsed/total time, playback speed (0.75×–2×), mute, artwork, and a title line linking back to the episode's listen page. The full player adds download (saves the audio file, via a Content-Disposition attachment) and share (the visitor's native share sheet, with a copy-link fallback). The player integrates with the Media Session API (lock-screen metadata and controls) and uses preload="none", so no audio bytes are downloaded until the visitor presses play.
Accessibility is the point: every control is a real button with an accessible name, focus is visible, state changes are announced politely to screen readers, prefers-reduced-motion is respected, and colours are contrast-checked (see theming below).
Open the transcript full screen
With transcript="reader", the player's Transcript button opens a reading view over the whole page. The player moves to the top, and playback carries on without a break. Visitors can search, select a line to jump to it, and follow along, word by word when the transcript has word timings. Each speaker's first line shows their photo, or their initials. Escape or Close transcript goes back to the page, and playback keeps its place.
To open it from a button of your own, call openTranscript() on the player. It returns false when the player has no transcript to offer, so you can link to the transcript file instead. When the reader closes, focus goes back to your button and the player fires a showfm:transcript-close event.
html
<showfm-player id="episode-player" episode="EPISODE_UUID" transcript="reader"></showfm-player>
<button type="button" id="read-transcript">Read the transcript</button>
<script>
document.getElementById('read-transcript').addEventListener('click', async () => {
// openTranscript() arrives when the player script has loaded.
await customElements.whenDefined('showfm-player');
document.getElementById('episode-player').openTranscript();
});
</script>The full-screen reader needs the script embed: an iframe can't cover the page. A browser too old to have modal dialogs (Safari before 15.4, Firefox before 98) opens the transcript under the player instead.
Fall back to an iframe
Some platforms (hosted WordPress plans, Notion, and similar) strip <script> tags. For those, use the iframe pages; they wrap the exact same player:
html
<iframe
src="https://embed.cdn.media/ep/3f9d2b6e-8c41-4a57-b2f0-6e5d4c3b2a19"
width="100%"
height="300"
style="max-width:600px;border:0;border-radius:12px"
title="The Big Launch – audio player"
loading="lazy"
allow="autoplay; web-share; clipboard-write"
></iframe>The allow list matters: web-share and clipboard-write are what let the player's Share button open a share sheet (or copy the link) from inside a cross-origin iframe: without them the button falls back to a "Unable to share" message on most browsers.
Iframe page URLs:
| URL | Plays |
|---|---|
https://embed.cdn.media/ep/{episodeId} | One episode (by UUID) |
https://embed.cdn.media/latest/{podcastSlug} | The show's newest episode |
Both accept optional query parameters: ?theme= (auto/light/dark), ?size= (standard/compact), ?accent= (a hex colour, hash optional: accent=0e8f7e), and ?wave= (true/false). Theme, accent, and wave parameters PIN the value for that iframe; leave them off and the player follows the show's saved player settings, picking up later changes automatically. Unknown or invalid values behave as if the parameter were absent (size falls back to standard). ?radius= (whole pixels, 0 to 28) sets the card corners; an invalid value keeps the default. ?credit=on always shows "Powered by show.fm" in that iframe. credit takes auto, on or off, but off only hides the credit where the show's own Hide "Powered by show.fm" setting already does, so it works like auto. Any other credit value returns an error page. The pages carry a <noscript> native audio fallback, allow framing from any site, and set no cookies.
Iframe heights:
| Kind | URL | Height |
|---|---|---|
| Standard player | /ep/{id} | 300px |
| Compact player | /ep/{id}?size=compact | 110px |
| Episode list | /episodes/{podcast-slug-or-uuid} | 560px |
| Player with transcript | /ep/{id}?transcript=open | 640px |
Set width="100%" (with a max-width if you want). Keep the listed height so controls have enough space. The list scrolls inside its frame. All iframe controls stay in English.
Embed an episode list
- Replace
your-showbelow with your show's slug or permanent podcast UUID. - Paste the code into your website's HTML block.
- Keep
height="560"when changing the width or list options.
html
<iframe
src="https://embed.cdn.media/episodes/your-show?variant=card&layout=list&count=10"
width="100%"
height="560"
style="border:0;border-radius:12px"
title="Your show episodes"
loading="lazy"
allow="autoplay; web-share; clipboard-write"
></iframe>The header names the show and links to its listen page. Visitors without JavaScript get a list of episode links. While an episode with a transcript plays, its row offers a Transcript button, as on your own page. A slug pasted before a show rename redirects to the new slug and keeps its options; UUIDs stay stable.
| Query option | Values | Default |
|---|---|---|
variant | card, minimal | card |
layout | auto, list, grid, compact | auto |
count | Integer from 1 to 50, episodes per page | 10 |
season | Integer from 0 to 9999 | All seasons |
hide | trailer, bonus, or trailer,bonus | Nothing hidden |
descriptions | on, off | on |
radius | Integer from 0 to 28, corners in pixels | 14 |
credit | auto, on (off works like auto) | Show setting |
theme | auto, light, dark | Show setting |
accent | Three or six hex digits, optional # | Show setting |
lang | en | English |
Invalid list option values return an error page. Use variant, not the HTML style attribute, to choose Card or Minimal.
Keep the transcript open
- Use an episode with audio and a published transcript hosted by show.fm.
- Add
?transcript=opento its/ep/{id}iframe URL. - Set the iframe's height to
640.
The transcript stays open below the standard player and scrolls within its panel. For externally hosted audio or an episode without a supported transcript, use the standard 300px embed instead. The transcript URL also serves the standard player in that case, but cannot resize an iframe height already set on your website.
If a show is suspended, the frame says “This show isn't available right now.” without showing titles or artwork. A temporary loading failure keeps the same frame size and lets the player try loading in the browser.
Embed via oEmbed
show.fm is an oEmbed provider. Platforms that support oEmbed discovery (WordPress embed blocks, for example) can turn a pasted listen-page URL into a player automatically:
https://{podcastSlug}.show.fm→ latest-episode playerhttps://{podcastSlug}.show.fm/e/{episodeSlug}→ that episode
The endpoint is GET https://api.show.fm/v1/oembed?url=…&format=json, and both listen-page shapes advertise it with a discovery <link> tag. Full parameter and response reference: oEmbed in the API reference. Only JSON responses are supported (format=xml returns 501).
Theme the player
Theme attribute. With no theme attribute, the player follows the show's theme setting (set in Settings → Distribution → Embed player, delivered by the API as player_theme), so podcasters can restyle every embed at once. theme="auto" pins this embed to the visitor's prefers-color-scheme; light and dark pin it outright.
Accent colour. The accent (play button, waveform, scrubber, focus rings) comes from the show's player colour: podcasters set it once in Settings → Distribution → Embed player, it's delivered by the API as player_color in the episode payload, and every embed follows it (falling back to the show's legacy brand_color, then the show.fm purple). You can pin an individual embed to its own colour with accent="#hex"; a pinned embed keeps its colour even if the show-wide setting later changes. The "Powered by show.fm" wordmark never recolours: it stays brand purple in the light theme and switches to white in the dark theme.
Contrast safety. Whatever colour arrives (from the API or your accent override), the player nudges it toward black or white until it reaches at least 3:1 contrast against the active theme background, and it renders text and icons on the accent in black or white, whichever reads better. Text in the accent, and every other text colour, reaches 4.5:1 (WCAG AA). A pale brand yellow on a white background will be darkened until it's usable; you never have to worry about an unreadable player.
Pick a size
| Size | Layout | Script embed | Iframe height |
|---|---|---|---|
standard | Card: artwork, title + show line, waveform, full transport, download + share | size="standard" (default) | 300 |
compact | Two-row bar: artwork, title + time, full transport with inline waveform | size="compact" | 110 |
Both sizes are fluid-width, so they fill whatever container you give them.
About "Powered by show.fm"
Players show a small "Powered by show.fm" footer by default, once per page. Whether it appears is controlled by the show's owner (it's the branding.show_powered_by field in the API payload), not by the embedding site. See the help-center guide for how show owners on Creator plans and up can remove it.
The credit attribute can only add the footer: on always shows it, while off hides it only where the show's setting already does, the same as the default auto. The show.fm WordPress plugin is the one exception: it hides the footer unless the site turns it on in the plugin's settings. See the credit on WordPress.
Get embed code without writing it
Show owners can copy ready-made, correctly escaped embed code for the player, the episode list and play buttons, with a live preview, from Embeds in the dashboard. An episode's Share and embed card opens it on that episode.