What Changed
The ONE Platform now has native podcast support. Not an embed. Not an iframe. Not a “here’s a link to Spotify” compromise.
Real features:
- Chapter navigation with timestamps
- Progress tracking across sessions
- Timestamp sharing (link to 5:32 in the episode)
- Featured podcast placement (home + topic pages)
- Audio-first interface (no unnecessary images)
- Content collection schema validation
Technical stats:
- 1,144 additions across 15 files
- New FeaturedPodcast component (110 lines)
- Podcast content collection with full schema
- Dynamic routing for episodes
- Zero hydration mismatches
First featured episode: The Ontology-Driven Architecture (17 minutes, 7 chapters)
Why This Matters
For Creators
You’ve been forced to choose:
- Embed from Spotify/Apple (slow, breaks, ugly)
- Use a podcast host + RSS (no control, no data)
- Self-host MP3s (no chapters, no progress tracking)
All three options suck.
ONE gives you what should have been standard:
// Your content collection
---
title: "The Ontology-Driven Architecture"
audioUrl: "https://media.one.ie/one-ontology.mp3"
duration: "17.44"
chapters:
- startTime: 0
text: "Introduction: The Complexity Crisis"
- startTime: 180
text: "Reality as a Domain-Specific Language"
- startTime: 420
text: "The 6 Universal Dimensions"
---
That’s it. No RSS feed. No podcast host. No distribution platform taking a cut or owning your audience.
For Listeners
Before (embedded players):
- Slow load times (third-party embed)
- No chapter navigation
- Progress lost on page refresh
- Can’t share timestamps
- Works on mobile (maybe)
After (native player):
- Instant load (edge-cached audio)
- Click chapters to jump
- Progress saved automatically
- Share
?t=332for timestamp links - Mobile-optimized (actually tested)
For Platforms
If you’re building a creator platform, podcast hosting is table stakes. But implementing it properly is weeks of work:
- Audio player UI (chapters, progress, speed controls)
- Progress persistence (localStorage + database)
- Timestamp URL handling (
?t=...) - Schema validation (ensure valid data)
- Featured placement (home page, category pages)
- RSS feeds (for external distribution)
ONE includes all of this. Out of the box. Customizable. Production-ready.
How It Works
Content Collection Schema
// web/src/content/config.ts
const PodcastSchema = z.object({
title: z.string(),
description: z.string(),
audioUrl: z.string().url(),
duration: z.string(),
date: z.string(),
status: z.enum(['draft', 'public']).default('draft'),
category: z.string().optional(),
featured: z.boolean().default(false),
author: z.string().optional(),
slug: z.string(),
thumbnail: z.string().optional(),
episode: z.number().optional(),
season: z.number().optional(),
tags: z.array(z.string()).default([]),
chapters: z.array(z.object({
startTime: z.number(),
text: z.string()
})).optional()
});
Schema validation means:
- TypeScript types auto-generated
- Invalid data caught at build time
- IDE autocomplete for all fields
- Zero runtime errors from bad data
Featured Podcast Component
The FeaturedPodcast component does the heavy lifting:
<FeaturedPodcast
title={podcast.title}
description={podcast.description}
audioUrl={podcast.audioUrl}
duration={podcast.duration}
slug={podcast.slug}
chapters={podcast.chapters}
client:load
/>
Features include:
- Microphone icon badge (“Featured Podcast · 17.44 min”)
- Gradient title styling
- VideoPlayer integration (handles audio + video)
- Chapter list with timestamps
- “Read Full Transcript” CTA
- Responsive layout (mobile-first)
VideoPlayer Component (Audio Support)
We extended the existing VideoPlayer to handle audio:
<VideoPlayer
src={audioUrl}
title={title}
type="audio"
chapters={chapters}
videoId={slug}
enableProgressTracking={true}
enableTimestampSharing={true}
client:load
/>
The player handles:
- Audio/video format detection
- Chapter navigation UI
- Progress persistence (localStorage)
- Timestamp URL parsing (
?t=180) - Playback speed controls
- Keyboard shortcuts
- Mobile gesture support
Fixed in this release: Hydration mismatch on chapter rendering (chapters now render client-side only to avoid SSR/client HTML differences).
Dynamic Routing
/podcasts → List all podcasts
/podcasts/one-ontology → Individual podcast page with player
Both pages use Astro’s content collections:
// /podcasts/index.astro
const podcasts = await getCollection('podcasts',
({ data }) => data.status === 'public'
);
// /podcasts/[...slug].astro
const podcast = await getEntry('podcasts', params.slug);
Performance:
- Static HTML generation (build time)
- Edge caching (Cloudflare)
- Progressive enhancement (works without JS)
- Audio streams from CDN (not bundled)
Featured Placement
Podcasts can be featured on any page:
---
// home page or category page
const featuredPodcast = await getCollection('podcasts',
({ data }) => data.featured === true
).then(p => p[0]);
---
{featuredPodcast && (
<FeaturedPodcast {...featuredPodcast.data} />
)}
Currently featured:
- Home page (above “How It Works”)
- Ontology page (showcases the platform’s architecture)
What You Can Do Now
1. Add Your First Podcast
# Create podcast file
touch web/src/content/podcasts/my-podcast.md
---
title: "Your Podcast Title"
description: "Compelling description that makes people want to listen"
audioUrl: "https://your-cdn.com/audio.mp3"
duration: "15.30"
date: "2025-11-10"
status: "public"
category: "Your Category"
featured: true
author: "Your Name"
slug: "your-podcast"
episode: 1
season: 1
tags: ["tag1", "tag2"]
chapters:
- startTime: 0
text: "Introduction"
- startTime: 120
text: "Main Topic"
- startTime: 600
text: "Conclusion"
---
# Your Podcast Title
Full transcript goes here...
2. Feature It On Your Home Page
---
// src/pages/index.astro
import { getCollection } from 'astro:content';
import { FeaturedPodcast } from '@/components/FeaturedPodcast';
const podcast = (await getCollection('podcasts',
({ data }) => data.featured
))[0];
---
<Layout>
{podcast && <FeaturedPodcast {...podcast.data} />}
<!-- Rest of your home page -->
</Layout>
3. Host Audio Files Efficiently
Options:
- Cloudflare R2 (cheap object storage, fast edge delivery)
- Your own CDN (if you already have one)
- Direct file upload (for small files, not recommended)
Recommended setup:
# Upload to Cloudflare R2
wrangler r2 object put one-platform-audio/episode-1.mp3 \
--file ./episode-1.mp3
# Access via public URL
audioUrl: "https://media.one.ie/episode-1.mp3"
4. Generate Chapters Automatically
Pro tip: Use Whisper (OpenAI) to auto-generate timestamps:
# Transcribe with timestamps
whisper audio.mp3 --model base --task transcribe --output_format json
# Parse timestamps and create chapter markers
# (Add to your build script or content pipeline)
Real-World Use Cases
Creator Podcast Series
Scenario: Course creator wants to add supplemental podcast episodes to courses.
Before ONE:
- Host on Anchor/Buzzsprout ($12-29/month)
- Embed player (slow, breaks, no control)
- No chapter navigation
- Can’t track who listens
With ONE:
- Host audio files on R2 (<$1/month)
- Native player with chapters
- Progress tracking per user
- Full analytics access
Cost savings: $11-28/month. Feature gain: Everything.
Platform Documentation
Scenario: Technical platform wants to add “explainer podcasts” for complex features.
Before ONE:
- Record audio ✓
- Upload to YouTube (video required)
- Embed YouTube player (slow)
- Users get distracted by suggested videos
With ONE:
- Upload MP3 to CDN
- Add to content collection
- Feature on relevant docs page
- Clean, focused listening experience
Time to implement: 5 minutes. Distractions: Zero.
Community Audio Updates
Scenario: DAO wants to share weekly audio updates without forcing everyone to join Twitter Spaces or Discord calls.
Before ONE:
- Record Space → hope people show up live
- Upload recording → link to Spotify (friction)
- No persistent archive on platform
With ONE:
- Record once, upload MP3
- Native player on governance page
- Permanent archive with timestamps
- Share specific moments (
/podcasts/update-5?t=240)
Technical Architecture
Why Content Collections?
Content collections provide:
- Type safety (schema validation)
- Build-time errors (catch bad data early)
- Auto-generated TypeScript types
- IDE autocomplete
- Fast queries (built into Astro)
Alternative (BAD):
// Manual file reading (brittle, no validation)
const files = fs.readdirSync('podcasts/');
const podcasts = files.map(file => {
const content = fs.readFileSync(file);
return JSON.parse(content); // Hope it's valid!
});
Content collections (GOOD):
// Type-safe, validated, fast
const podcasts = await getCollection('podcasts');
// TypeScript knows exact shape of data
Why VideoPlayer for Audio?
The VideoPlayer component already had:
- Progress tracking
- Chapter navigation
- Timestamp URL handling
- Keyboard shortcuts
- Mobile optimization
Instead of building a separate AudioPlayer, we extended VideoPlayer with type="audio" mode.
Benefits:
- One component maintains both audio and video
- Shared features (chapters, progress) work identically
- Less code to maintain
- Consistent UX across media types
Hydration Strategy
<!-- FeaturedPodcast uses client:load (critical above-fold) -->
<FeaturedPodcast client:load {...podcast.data} />
<!-- VideoPlayer inside uses smart hydration -->
<VideoPlayer client:load />
Why client:load?
- Podcast is featured above the fold
- Users expect immediate interaction
- Audio player needs JavaScript (play button, progress)
Alternative considered: client:idle (load when browser idle). Rejected because podcast is primary content, not secondary.
What’s Next
Coming soon:
- ✅ RSS feed generation (auto-publish to Apple Podcasts, Spotify)
- ✅ Transcription auto-import (Whisper → chapters)
- ✅ Waveform visualization (show audio peaks)
- ✅ Playback analytics (who listens, where they drop off)
- ✅ Playlist support (group episodes by season/series)
- ✅ Comments + timestamps (discuss specific moments)
- ✅ Download button (offline listening)
Feedback wanted:
- What podcast features do you need?
- Should we add video chapters (YouTube-style)?
- RSS feed auto-generation priorities?
Join the discussion: Discord
The Bigger Picture
This isn’t just about podcasts. It’s about native content support for creators.
The pattern:
- Define schema (content collection)
- Create component (player, renderer)
- Enable dynamic routing
- Add featured placement
- Ship to production
This same pattern applies to:
- Video hosting (already supported)
- Course hosting (in progress)
- Newsletter archives (planned)
- Event recordings (planned)
- Creator AMAs (planned)
The platform becomes the distribution channel. No middlemen. No embeds. No compromises.
Try It Now
Listen to the featured podcast: → The Ontology-Driven Architecture
Add your own podcast:
- Create
web/src/content/podcasts/your-podcast.md - Add frontmatter (title, audioUrl, chapters)
- Run
bun run build - Deploy
Get help:
- Docs: Podcast Setup Guide
- Discord: #audio-features
- GitHub: Report issues
Audio-first content. Native hosting. Zero compromises.
The platform that makes it easier for AI to build than humans just made it easier for humans to create.
Related: