Audio embed player
Our embed player is the best way to add narrations and other audio to your web pages.
Quick start
To embed a TYPE III AUDIO narration, add the embed code with no custom parameters.
When your web page loads, the embed player will:
- Query the TYPE III AUDIO narration database for the current page URL.
- If a narration is found, load that narration.
- If a narration is not found, create a new narration.
It's that easy.
<script
type="module"
crossorigin
src="https://embed.type3.audio/player.js"
></script>
<type-3-player></type-3-player>
Here's how the player looks by default:
Features
Optional behaviours you can switch on with attributes.
Listen to this page
Instead of showing the player by default, you can show a smaller "Listen to this page" button.
When the user clicks the button, playback will begin and the full player will show.
Once the audio duration is known, the button label automatically includes the listening time rounded to the nearest minute, for example Listen to this page (12 min).
<type-3-player
listen-to-this-page="true"
>
</type-3-player>
Floating player
With sticky="true", the player floats at the bottom of the screen once playback starts and the inline player scrolls out of view. With sticky="always", the inline player is disabled and the floating player is always shown.
While the floating player is visible, .t3a-sticky-player-visible is added to the <body> element.
Header play buttons
With header-play-buttons="true", the player adds a "Play" button to the left of any heading whose text exactly matches a chapter title from your audio.
You'll need to style the button yourself:
/*
Styles for the TYPE III AUDIO player "heading play button" feature.
*/
/* Heading play button should not be shown on small screens */
.t3a-heading-play-button {
display: none;
}
/* Set minimum width at which heading play button should be shown */
@media screen and (min-width: 850px) {
.t3a-heading-play-button {
display: block;
position: absolute;
top: 0;
left: 0;
margin-left: -34px;
margin-right: 10px;
border-radius: 9999px;
border: none;
width: 1.5rem;
height: 1.5rem;
outline: none;
cursor: pointer;
transform: translate(0, 0);
z-index: 10;
/* Colour of the play button */
background-color: #ddd;
/* Colour of the play button icon. */
color: #fff;
}
.t3a-heading-play-button:hover {
/* Colour of the play on hover */
background-color: #333;
}
.t3a-heading-play-button:focus {
outline: none;
}
.t3a-heading-play-icon {
display: flex;
justify-content: center;
align-items: center;
margin-left: 2px;
}
/* Refine this to match the dimensions of your heading typeface */
h1 .t3a-heading-play-button { margin-top: 9px }
h2 .t3a-heading-play-button { margin-top: 9px }
h3 .t3a-heading-play-button { margin-top: 3px }
}
To show the buttons only after playback has started:
.t3a-heading-play-button {
display: none;
}
.t3a-playback-started .t3a-heading-play-button {
display: block;
}
Link to timestamp
If link-to-timestamp="true", the player scans the page for strings like [HH:MM:SS], [H:MM:SS], and [MM:SS] and turns them into links that seek the player to that timestamp and start playback.
To restrict the search, set link-to-timestamp-selector to any valid CSS selector.
If a page has multiple <type-3-player> elements, set link-to-timestamp="false" on the ones you don't want to seek.
Embed an MP3
To embed a specific MP3 with your own metadata — for example, audio that isn't in the TYPE III AUDIO narration database — set mp3-url and the related attributes directly.
<script
type="module"
crossorigin
src="https://embed.type3.audio/player.js"
></script>
<type-3-player
mp3-url="https://dl.type3.audio/episodes/radio-bostrom/11335090-base-camp-for-mount-ethics-2022.mp3"
author="Nick Bostrom"
title="Base Camp for Mount Ethics"
>
</type-3-player>
Styling
You can style the player via native attributes, or target its stable CSS class hooks for finer control.
Colours
<script
type="module"
crossorigin
src="https://embed.type3.audio/player.js"
></script>
<type-3-player
background-color="#f1f1f1"
primary-color="#333"
accent-color="#2ebdd1"
></type-3-player>
Borders and hover
<type-3-player
border-color="#e5e5e5"
border-color-dark="#333"
border-width="1px"
hover-transition="0.15s ease"
play-button-color-hover="#2ebdd1"
></type-3-player>
-dark variants for prefers-color-scheme: dark are listed in the colours and hover animation reference tables.
Typography
Use primary-font-family and secondary-font-family to set the typefaces used inside the player. The primary font applies to the selected chapter heading in the chapters modal. The secondary font applies to the chapter list and the "Listen to this page" label.
<type-3-player
primary-font-family="Inter, system-ui, sans-serif"
primary-font-weight="600"
secondary-font-family="Inter, system-ui, sans-serif"
secondary-font-weight="400"
></type-3-player>
Custom CSS hooks
The player exposes a stable set of t3a-* class names on its meaningful elements. Target them from custom-css or your host page's stylesheet when the attributes above don't cover what you need.
Player root and state
| Class | Element |
|---|---|
| .t3a-player | every player root (inline, sticky, listen-to-this-page) |
| .t3a-player--inline | inline player root |
| .t3a-player--sticky | floating / sticky player root |
| .t3a-player--listen-to-this-page | "Listen to this page" button root |
| .t3a-player--playing | added to the player root while audio is playing |
| .t3a-player--paused | added to the player root while audio is paused |
Body classes
The player adds the following classes to the host page's <body> element:
| Class | Added when |
|---|---|
| .t3a-sticky-player-visible | the floating / sticky player is visible |
| .t3a-playback-started | playback has started at least once on the current page |
| .t3a--ai-narration | the loaded narration is an AI narration |
| .t3a--human-narration | the loaded narration is a human narration or upload |
| .t3a--hardcoded-mp3-url | mp3-url was set on the player (rather than resolved from a narration) |
Track info, transport, and slider
| Class | Element |
|---|---|
| .t3a-title | track title (uses primary font) |
| .t3a-author | author name (uses secondary font) |
| .t3a-listen-to-this-page-text | "Listen to this page" label (uses secondary font) |
| .t3a-transport | row containing skip-back, play/pause, skip-forward |
| .t3a-button--play | play / pause button |
| .t3a-button--play-state | play / pause button while it will start playback when clicked |
| .t3a-button--pause-state | play / pause button while it will pause playback when clicked |
| .t3a-button--paused | play / pause button while the player is paused |
| .t3a-button--playing | play / pause button while the player is playing |
| .t3a-button--skip-back | skip-back button |
| .t3a-button--skip-forward | skip-forward button |
| .t3a-skip-increment | "15" / "30" label inside a skip button |
| .t3a-skip-increment--back | label inside the skip-back button |
| .t3a-skip-increment--forward | label inside the skip-forward button |
| .t3a-slider | seek-bar wrapper |
| .t3a-slider-track | seek-bar input element |
| .t3a-slider-fill | filled portion of the seek bar |
| .t3a-duration | elapsed / total duration counter |
Action buttons
| Class | Element |
|---|---|
| .t3a-actions | row of action buttons |
| .t3a-action-button | every action button |
| .t3a-action-button--chapters | "Chapters" button |
| .t3a-action-button--speed | "Speed" button |
| .t3a-action-button--subscribe | "Subscribe" button |
| .t3a-action-button--feedback | "Feedback" button |
| .t3a-action-button--close | "Close" button |
| .t3a-action-button--add-to-feed | "Add to feed" button |
Modals
| Class | Element |
|---|---|
| .t3a-modal | every modal root |
| .t3a-modal-body | inner modal content area |
| .t3a-modal-close | modal close button |
| .t3a-modal--chapters | chapters modal |
| .t3a-modal--speed | speed modal |
| .t3a-modal--subscribe | subscribe modal |
| .t3a-modal--error | error modal |
| .t3a-chapter-heading | "Chapter N of N" label |
| .t3a-chapter-heading-title | currently-selected chapter title (uses primary font) |
| .t3a-chapter-list | scrollable list of chapters |
| .t3a-chapter-item | a single chapter row |
| .t3a-chapter-item--active | the active chapter row |
| .t3a-chapter-time | timestamp inside a chapter row |
| .t3a-chapter-title | chapter title inside a chapter row (uses secondary font) |
| .t3a-speed-slider | speed slider input |
| .t3a-speed-bound | min / max bound label on the speed slider |
| .t3a-speed-bound--min | min bound label |
| .t3a-speed-bound--max | max bound label |
| .t3a-speed-stepper | row of −, value, + speed controls |
| .t3a-speed-stepper-button | a − or + speed button |
| .t3a-speed-stepper-button--minus | − speed button |
| .t3a-speed-stepper-button--plus | + speed button |
| .t3a-speed-value | numeric speed value |
| .t3a-subscribe-grid | grid of subscribe-platform tiles |
| .t3a-subscribe-item | a single subscribe-platform tile |
| .t3a-subscribe-item-icon | icon inside a subscribe-platform tile |
| .t3a-subscribe-item-label | label below a subscribe-platform tile |
| .t3a-error-message | error modal body |
Integrations
Analytics events
Use the analytics attribute to send events to your preferred analytics service (defaults to google):
google:window.gtag("event", event.type, event)segment:window.analytics.track(event.type, event)custom:window.t3aAnalytics(event.type, event)none: no events sent
Events are only sent if the relevant window.x function is defined — if a visitor's privacy settings block your analytics service, no events fire.
| Event type | Event attributes | Fired |
|---|---|---|
| play | currentPlaybackPosition, playbackSpeed | when player starts playing |
| continued-listening | currentPlaybackPosition, playbackSpeed | when player plays without interruption for 30 sec |
| completed-episode | currentPlaybackPosition, playbackSpeed | when player progress reaches <95% for the first time and its playing |
| reached-minute | minute, playbackSpeed | when player reaches minute 5, 10, 15 (etc) of the audio |
| reached-percentage | percentage, playbackSpeed | when player reaches 25, 50 and 75% of the audio |
| subscribe | platform | when subscribe button is clicked |
| player-loaded | mp3Url | when player successfully loads an MP3 |
| error | errorMessage, userMessage, mp3Url, networkState | when an error occurs |
All analytics events also include the following attributes:
- episodeType: the type of episode (
ai_narration,human_narrationorhardcoded_mp3_url) - episodeTitle: title of the episode, if known
- episodeAuthor: author of the narrated article, if known
- episodeId: episode ID (if episode was loaded from the TYPE III episode database)
- url: the URL where the player was loaded, excluding query params and hash values
- origin: the domain name where the player was loaded
WordPress plugin
Install our WordPress plugin to enable the [type_3_player] shortcode. Pass any of the attributes as shortcode arguments:
[type_3_player listen-to-this-page="true"]
How to add shortcodes in the Block Editor:
Attribute reference
Player features
| Attribute | Type | |
|---|---|---|
| analytics | "none", "google", "segment", "custom" | sends analytics events to selected provider (defaults to "google") |
| narration-url | string | if you want to load a narration for URL X, on a page with URL Y, set "narration-url" to URL X. (Legacy alias: episode-url.) |
| (soon) library-url | string | link for the "View in library" button |
| listen-to-this-page | boolean | show a "listen to this page" button instead of the full player |
| listen-to-this-page-text | string | set the "listen to this page" button text. defaults to "Listen to this page". The listening time is added automatically when duration is known. |
| link-to-timestamp | boolean | is link to timestamp feature active. |
| link-to-timestamp-selector | css selector string | link to timestamp feature will look for links in the selected elements (default "body *") |
| mp3-url | string | url to mp3 audio file |
| show-skip-buttons | boolean | show the skip back and skip forward buttons. defaults to true; set to false to hide them. Legacy alias: skip-buttons. |
| play-icon-svg | SVG string | custom SVG markup for the play icon. |
| pause-icon-svg | SVG string | custom SVG markup for the pause icon. |
| sticky | "true", "always", "false" | true = enable floating player when inline player is not in viewport; always = disable inline player and always show the player in floating mode (defaults to "false") |
Subscribe buttons
| Attribute | Type | |
|---|---|---|
| subscribe-url--apple | string | apple podcast url |
| subscribe-url--spotify | string | spotify url |
| subscribe-url--youtube | string | youtube url |
| subscribe-url--podcast-addict | string | podcast addict podcast url |
| subscribe-url--rss | string | rss feed url |
Colours
| Attribute | Type | |
|---|---|---|
| background-color | css color string | background color (default #FEFCE8) |
| primary-color | css color string | text color, fill slider color, controls icon color (default black) |
| background-color-dark | css color string | (prefers-color-scheme: dark) background color |
| primary-color-dark | css color string | (prefers-color-scheme: dark) text color, fill slider color, controls icon color |
| accent-color-dark | css color string | (prefers-color-scheme: dark) the play button color |
| play-button-color | css color string | play / pause button background color |
| play-button-color-dark | css color string | (prefers-color-scheme: dark) play / pause button background color |
| play-button-icon-color | css color string | play / pause icon color |
| play-button-icon-color-dark | css color string | (prefers-color-scheme: dark) play / pause icon color |
| play-button-icon-color-hover | css color string | play / pause icon color for button :hover state |
| play-button-icon-color-hover-dark | css color string | (prefers-color-scheme: dark) play / pause icon color for button :hover state |
| play-button-color-hover | css color string | play / pause button background color for :hover state (default: primary-color) |
| play-button-color-hover-dark | css color string | (prefers-color-scheme: dark) play / pause button background color for :hover state |
| border-color | css color string | border colour around the player container |
| border-color-dark | css color string | (prefers-color-scheme: dark) border colour around the player container |
| border-width | css length string | border width around the player container (e.g. "1px"). default: no border |
| modal-background-color | css color string | background colour for the chapters / speed / subscribe modals (default: primary-color) |
| modal-background-color-dark | css color string | (prefers-color-scheme: dark) modal background colour |
| modal-primary-color | css color string | text / accent colour on top of the modal background (default: background-color) |
| modal-primary-color-dark | css color string | (prefers-color-scheme: dark) modal text / accent colour |
| data-skeleton-bg-color | css color string | skeleton loader background color |
| data-skeleton-text-color | css color string | skeleton loader text color |
Hover animation
| Attribute | Type | |
|---|---|---|
| hover-transition | css time + timing function | when set, play/pause and skip buttons fade between base and hover colours. Value is a CSS transition timing (e.g. "0.15s ease" or "150ms"). |
Typography and custom CSS
| Attribute | Type | |
|---|---|---|
| primary-font-family | css font-family string | font for the track title and chapter heading title (default: bundled UntitledSans) |
| primary-font-weight | css font-weight | weight for primary-font slots (default: "500") |
| secondary-font-family | css font-family string | font for the author name, chapter list, and "Listen to this page" label |
| secondary-font-weight | css font-weight | weight for secondary-font slots (default: "500") |
| custom-css | css string | any CSS you put here will be added within a <style> tag. |
Privacy note
The player also sends the analytics events above to TYPE III AUDIO's internal analytics service. The mechanism is GDPR-compliant: no personally identifiable information is collected, and visitor consent or cookie notices are not required. We use the data to improve the player and report usage to clients.
To opt out, email us.

