018
AI ready Refreshed

Podcast Template

£29 View live demo or the whole Vault, £79.20

Includes the original v1 design as a separate download.

A five-page website template for podcasts, radio shows, DJ mix series and interview programmes, with a player strip that keeps playing while visitors move between pages. Gigantic compressed type, real room photography, hairline episode index rows, neon for everything live and cobalt for editorial blocks; Back Room Tapes, a music-and-making show recorded behind a Sheffield record shop, is the worked example.

ScrollTriggerDraggable Lenis advanced
14 more details
page-transitionsoft-navigationdirectional-transitiondraggablescrubmask-revealscroll-revealstaggermarqueehover-effectfilterkeyboard-navigationfocus-managementprogressive-enhancement

Updated

About this template

A five-page site for a show that lives on audio: home, an episode index, a fully worked episode page, about and listen. It is built like a station rather than a blog. A black bar and a player strip sit at the top of every page, gigantic compressed capitals run against a monospace voice, photographs bleed to the edges, neon marks everything live and cobalt carries the editorial blocks.

Read the full template overview

The player strip is the point. It sits outside the part of the page that changes, so an episode keeps playing while a listener browses the index, reads the about page or follows a link from a clip. Every play control on the site, from index rows to transcript timestamps, drives that one player.

The episode page carries chapters, show notes and a full transcript whose timestamps seek the audio, with a neon highlight that follows the paragraph being spoken. Pages change with a spool transition: the page winds up into the player strip like tape onto a reel and the next one plays back out of it.

The worked example is Back Room Tapes, a fortnightly interview show recorded behind a record shop on Division Street, Sheffield. Swap the copy, cover photographs and audio files and the same pages suit any interview podcast, community radio station, mix series or talks archive.

Podcast Template - website template preview

What's included

14 items
  • Five complete pages: home, episode index, worked episode page, about and listen
  • Persistent player strip under the navigation that keeps playing across every page change
  • Drag-to-scrub waveform drawn from per-file peak arrays, with chapter markers, keyboard seeking and slider semantics
  • One audio element drives everything: hero play button, index rows, clips, chapters and transcript timestamps
  • Clips row that jumps the player to the exact second a quote was said
  • Episode page with chapters, show notes and a transcript that highlights and follows the paragraph being spoken, plus a Back to now control
  • Spool page transition that winds each page into the player strip and plays the next one out; Back runs it as a rewind
  • Episode index of hairline rows with a cover that follows the pointer and a filter by trade
  • Now-playing ticker over the home page photograph, read from the player
  • Host switcher on the about page with each host's intro cued in the player
  • Copy-to-clipboard RSS field, newsletter and guest pitch forms as static front ends
  • Works without JavaScript: native audio controls, plain links and readable pages
  • Lenis smooth scrolling, created once alongside the page router and switchable off
  • Eight-token palette with measured contrast pairs: neon for live, cobalt for editorial

Perfect for

5 use cases
  • Interview podcasts that want a home of their own with an episode index and transcripts
  • Community and online radio stations with a player that stays on while listeners browse
  • DJ mix series, record labels and venue session archives
  • Talks, lecture and sermon archives where chapters and transcripts matter
  • A front end to hand to a developer wiring a real RSS feed or CMS behind it

How it works

4 sections

A player that survives navigation

Each page is a complete HTML file. A small router fetches the next page and swaps only the page container, so the black bar and the player strip, which sit outside it, are never replaced and the audio never stops. Without JavaScript or over file:// the links load pages normally and the strip is a native audio control.

Everything plays through one element

Episodes are described once in a JSON block at the foot of each page. Any element with a data-cue attribute plays that episode, and adding data-at jumps to a second, which is how index rows, clips, chapters and transcript timestamps all address the same player. Every readout is painted from the audio element's own events, so the strip, the episode page and the index cannot disagree.

The waveform and the transcript

The waveform is drawn from a short array of peaks per audio file, scrubbed by dragging, clicking or the keyboard, and marked with chapters. On the episode page each transcript paragraph carries its start time; the paragraph being spoken gets a neon block, and the page follows it unless the listener has scrolled away.

The spool transition

On a click the current page is held where it stands and wound up into the player strip while the next page loads underneath, with the strip's reels spinning. The new page then plays back out of the strip. Only transform and opacity are animated on a single viewport-sized layer, Back runs it as a rewind, and reduced motion gets a short crossfade.

Plugins ScrollTrigger, Draggable
Difficulty Advanced
Smooth scroll Lenis integration
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this template's demo (5 pages, lowest score shown), from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Questions about this template

What kinds of show does this template suit besides a music podcast?

Any audio programme with episodes: interview podcasts, community or online radio, DJ mix series, a record label's session archive, or a talks and lecture series. The structure is an index, an episode page with chapters and transcript, an about page and a listen page; replace the copy, cover photographs and audio and the design carries over.

Does the audio really keep playing when visitors change page?

Yes, when the site is served over http or https. The player strip sits outside the part of each page that is swapped, so navigation never touches the audio element. Opened straight from disk the links still work as ordinary page loads, which restart the page and its audio, so test it on any local web server.

How do I add a new episode?

Add an entry to the episodes JSON block at the foot of each page with its title, guest, audio file and length in seconds, then add a row to the index with a matching data-cue. For a full episode page, duplicate episode.html, change its chapters, notes and transcript times, and link the row title to it.

Where do the waveforms come from, and do they read my audio live?

They are short arrays of peak values per audio file, kept in one object at the top of the script. The README shows an ffmpeg command and a few lines of Node that produce the array from your own episode. Nothing is analysed in the browser, which keeps the player light and the file small.

Are the newsletter and guest pitch forms connected to anything?

No. Both are static front ends that check the fields and show a confirmation in place without sending anything. The newsletter form collects an email address; the pitch form collects a name, email, who the guest is, what they make and a note. Point them at your mailing list provider, Formspree, Netlify Forms or your own inbox.

Can I connect it to my podcast host's RSS feed?

Each RSS item maps onto two places. Its enclosure URL, title, author and duration become one entry in the episodes JSON block at the foot of every page (src, title, guest, seconds), keyed by episode number. Its pubDate and itunes:duration fill one index row in episodes.html, with a data-cue matching that key. A script that writes those two from your feed is all a developer needs to add.

Paid template

Purchase to unlock the code.

Buying Podcast Template opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£29 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £79.20, one payment.

Documentation

Quick Start

  1. Upload the whole folder to any static host (Netlify, Vercel, Cloudflare Pages, GitHub Pages or your own server). There is no build step.
  2. Open index.html over http(s)://. Opened straight from disk (file://), the pages still work, but as ordinary page loads: the transition and the uninterrupted player need a web server, because the router fetches the next page. Use a server that supports byte-range requests (every real host does) or audio cannot seek.
  3. Replace the audio in assets/audio/, edit the episodes block and the copy in each .html file, swap the photographs in assets/img/, and repaint the tokens at the top of assets/style.css.

Files:

File What it is
index.html, episodes.html, episode.html, about.html, listen.html The five pages. Each is complete on its own.
assets/style.css All styles. Tokens and the measured contrast pairs are in the first :root block.
assets/script.js The player, page features and the page transition. script.min.js is the minified copy.
assets/page-router.js The shared soft-navigation router. Do not edit it; the transition is written in script.js.
assets/audio/ Three placeholder episodes. Replace them with yours.
assets/img/ Baked WebP photographs. img-manifest.json records where each came from.

Using It With Your Own Design

  • Colours. There are eight tokens: grey ground, paper, black slab, ink, two mutes and two accents. --neon marks everything live: the on-air tab, play buttons, the ticker, the played part of each waveform, now-playing marks and the transcript follow block. --cobalt carries editorial blocks: number tiles, badges, season heads, selected tabs and whole bands, always with paper text on it. Neon is only ever a block with ink on it. It is never text on the grey, and it is never used on the black slabs; controls there use ground or paper instead.
  • Type. Sofia Sans Extra Condensed Black is the display face, used at one enormous size per page. Reddit Mono at two weights does everything else. Swap the files in assets/fonts/ (see Fonts), then --font-display / --font-mono. A different display face needs the --display size checked, because the words are set to fill the width.
  • The bar and the player strip are repeated at the top of every page, outside [data-page]. Change them in all five files, and keep them identical.
  • Section by section. Each section is a plain block in <main>. Delete any of them and the rest keeps working. Every feature in script.js returns early when its markup is missing.

Pages and Configuration

Home (index.html)

  • The hero stacks the show's name in two display lines around a full-bleed photograph of the room. The ticker band runs across the photograph's lower edge. data-ticker holds the extra items, separated by |, and data-latest names the episode it announces while nothing is playing. While audio plays, the first item becomes "Now playing" from the player.
  • Clips are list items with a button.clip-cue carrying data-cue (episode) and data-at (second). Pressing one cues the player there and plays.
  • Start here and Latest are cards and index rows. Every play button is data-cue="<id>".

Episodes (episodes.html)

  • Rows are grouped by section.season, each with a sticky season head.
  • Each row's data-topics lists its trades, separated by spaces. The chips in [data-filter] each carry a data-topic that matches one of them. Rows with data-cover show that image beside the pointer on desktop.

Episode (episode.html)

  • [data-episode-player="118"] is a second view of the same player: a large waveform, chapters and the current time. It mirrors the strip, and pressing or scrubbing it loads that episode if another one is playing.
  • Chapters are list items with data-t (seconds). Each one is a link with data-cue and data-at.
  • The transcript is a list of .tx-p blocks with data-t. The paragraph being spoken gets the neon block, and the page follows it unless the listener has scrolled in the last few seconds. In that case a Back to now button appears instead.
  • Duplicate this page for each episode you want to publish in full, and link the row title to it.

About (about.html)

  • [data-hosts] holds two tab panels and two portraits (data-host-photo). Without JavaScript the panels simply stack. Each panel's button cues that host's intro in the player.

Listen (listen.html)

  • App rows are ordinary links. [data-feed] is a read-only field with a copy button.
  • The newsletter and guest pitch forms (form[data-demo-form]) are static front ends. They check the fields and show a confirmation in place without sending anything. Connect them to your mailing list provider, Formspree, Netlify Forms or your own inbox, and remove data-demo-form.

The Player

Episodes are described once, in the <script type="application/json" data-episodes> block at the foot of every page. Keep it identical in all five files:

Code snippet omitted: it ships with the download.

seconds is the file's real length. It is used to draw chapters before the file has loaded. chapters is optional.

Anything with data-cue="<id>" plays that episode through the strip: a second press pauses it. Add data-at="<seconds>" to jump to a moment and play from there. That one convention drives the hero button, rows, clips, chapters and transcript timestamps. Put hidden on cue buttons in the markup. The script reveals them, so there is no dead button without JavaScript.

The strip itself is section[data-deck]. Its data-episode is the episode loaded on arrival. Without JavaScript it is a native <audio controls> element playing that episode.

Audio

assets/audio/ ships three generated placeholder files: six to seven minutes each of quiet tone and noise, made with ffmpeg at 40 kbps, mono. They exist so the player, chapters and transcript have something real to play. They are not speech, so the transcript timings are illustrative. Replace them with your episodes and change the src and seconds values in the episodes block. The episode lengths printed in the rows are part of the fictional content, so they will not match the placeholders.

Waveforms

Peak arrays live in the PEAKS object at the top of assets/script.js, keyed by audio file name. One number is one bar, from 0 to 1. A file with no entry gets a flat bar, so a missing array costs you only the drawing.

To make the array from your own episode, use ffmpeg and Node:

Code snippet omitted: it ships with the download.

Code snippet omitted: it ships with the download.

Page Transitions

assets/page-router.js owns the plumbing: link interception, prefetch on hover, fetching, history, scroll restoration, focus and interruption. script.js supplies the transition through the router's leave, enter and reset hooks.

  • Spool. On the click, the page winds up into the player strip like tape onto a reel. It squeezes upward into a line, speed lines cross it and the strip's reels spin. The next page is fetched at the same time and then plays back out of the strip. Back spins the reels the other way and runs the lines right to left, as a rewind. Only transform and opacity are animated, on one layer the size of the window, so it holds its frame rate on large screens.
  • To keep the router and the uninterrupted player but drop the animation, remove the leave and enter hooks from the PageRouter.create call. Pages then swap with a short crossfade.
  • Page features are init functions called from the router's init hook. A script pasted into one page's HTML will not run after a soft navigation, so add it to initPage in script.js instead.

Palette

Token Value Role
--ground #E2E2DF Page ground
--paper #F4F4F1 Player strip, transcript, clips, fields
--slab #1A1A19 Bar, footer, black bands
--ink #161615 Text, hairlines, play buttons
--mute #5C5C58 Secondary text on ground and paper
--mute-dark #A8A8A2 Secondary text on black
--neon #D4FF3A Anything live, always a block with ink on it (15.7:1)
--cobalt #2F5BFF Editorial blocks, with paper text on it (4.7:1)

The measured contrast pairs are listed in the :root comment in style.css.

Accessibility and Fallbacks

  • The waveform is a focusable role="slider" with a spoken value. Space or Enter plays and pauses, arrows step 5 seconds, Page keys step 30, and Home and End jump to either end. On touch, a horizontal drag scrubs and a vertical one scrolls the page.
  • After each page change, focus moves to the new page's heading and the title is announced. Mouse navigation shows no focus ring.
  • Reduced motion: pages crossfade, the ticker and the on-air pulse stand still, entrances are skipped and smooth scrolling is off.
  • Without JavaScript, the strip is a native audio control for the latest episode and the episode page has its own. Chapters and timestamps are links into the audio file. Every page is readable and every link works.
  • If the GSAP CDN is blocked, the player and every cue still work. You lose only drag-to-scrub, the transition and the animation.

Dependencies

  • GSAP 3.12+ with ScrollTrigger and Draggable (the demo pins 3.15.0 from jsDelivr).
  • Lenis 1.1+ for smooth scrolling (the demo pins 1.3.17 from unpkg). It is optional: add data-smooth="off" to <html>, or remove the script tag.
  • Sofia Sans Extra Condensed and Reddit Mono, self-hosted (see Fonts).

Fonts

Both families are self-hosted in assets/fonts/, so no page makes a render-blocking third-party request. The files are the Latin subsets Google Fonts serves, declared by the @font-face rules at the top of assets/style.css with the same weights and unicode-range Google uses; Reddit Mono is one variable file covering both weights. Each page preloads both, since the display capitals and the mono text are both on the first screen. Licences: Reddit Mono (Copyright 2020-2023, Reddit, Inc., https://github.com/reddit/redditsans) and Sofia Sans Extra Condensed (Copyright 2019 The Sofia Sans Project Authors, https://github.com/lettersoup/Sofia-Sans) are under the SIL Open Font License 1.1, full text in assets/fonts/OFL.txt.

To swap a face, put your .woff2 in assets/fonts/, point its @font-face rules and the --font-* token at it, and change the preloads in each page's <head>. To use Google Fonts instead, delete the @font-face rules and the preloads and add the Google Fonts <link> to every page.

Images and Credits

Every photograph in assets/img/ is a baked WebP derivative: cropped, resized and graded for this template. assets/img-manifest.json records the source of each one. To replace an image, overwrite the file at the same size, or change its source in the manifest and regenerate.

  • The hosts, the room, the booth and the episode 118 guest (booth, hosts, room, dev, hannah, cover-118) are AI-generated illustrations of the fictional show. They do not depict real people. Masters are in assets/img-src/gen/.
  • Episode covers and detail shots are from Pexels, under the Pexels licence: crates and faders (Pexels 6865917, 8132724, 6176716, 15303870), a sign painter (11734209), soldering (38264269), a club crowd (5192289), a DJ (27595429), a reel-to-reel machine (10985335, 8382082) and a turntable (164872), a print studio (4483218), a maker's bench (37358114) and letterpress type (1440501).
  • Header and footer copy, names, episodes, quotes and press lines are fictional.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.