Xeder icon
Chrome Extension Text-to-Speech Firebase Serverless

Turn your X feed
into a podcast.

A Chrome extension that scrapes your live X timeline and reads it aloud in natural voices, so you can catch up with your hands and eyes somewhere else. This is the story of how it was built.

Role
Solo design & engineering
Type
Browser extension + backend
Pricing
$19 one-time license
Playing Feed
Som
Som
@somnivm_2m

"X is text-first. Unlike video-heavy platforms, its content can be consumed passively as audio while you work. That's the gap Xeder fills."

1.2K 3.4K 1.1M
0:04
-0:12
01 · The Problem

Text was trapped on the screen.

X is text-first. But text demands your eyes, so the only way to keep up was to stop everything and scroll.

Every other feed has an escape hatch. Podcasts play while you cook. YouTube essays run in the background. But the one platform built almost entirely on writing had no way out of the visual loop, you either looked at it, or you missed it.

That's the gap. X's content is uniquely suited to be consumed as audio, short, standalone, skimmable chunks, if only something could read it to you while your hands and eyes were doing something else.

Doomscrolling

A visual loop that locks your attention to the glass.

Video essays

"Just watch a sec" pulls focus away from your work.

Xeder, listening

Bite-sized tweets in your ears. Pause anytime, lose nothing.

02 · The Product

A player that floats over X.com.

A glassmorphism widget injected directly into the page. It reads the feed you already see, algorithmic, following, or lists, with play, pause, skip, speed control, automatic advancement, and ad filtering.

Full transport

Play, pause, skip back and forward, and adjustable playback speed.

Auto-advance

One tweet flows into the next, hands-free, as the feed keeps loading.

Ad filtering

Promoted posts and "who to follow" noise are skipped automatically.

Scroll sync

The timeline follows the audio, so the page tracks what you're hearing.

Adaptive UI

One player, three footprints

Stay detailed, go compact, or tuck it away in a corner, the feed keeps playing in every state.

Mini

tweet + controls
2m
Som@somnivm_

"X is text-first. Unlike video-heavy platforms..."

0:04-0:12
1x

Main

full context
Playing Feed
Som
Som
@somnivm_2m

"X is text-first. Unlike video-heavy platforms, its content can be consumed passively as audio while you work."

1.2K 3.4K 1.1M
0:04
-0:12

Minier

controls only
Som@somnivm_
03 · Architecture

Two halves, one stream of audio.

Xeder splits cleanly down the middle: a Chrome extension that handles the page and the DOM, and a Firebase-powered backend that handles speech, payments, licensing, and email. The extension never needs an API key for X, it reads the page the user is already on.

Chrome Extension · Client

Content Script

DOM scraper

Injected into x.com. Walks the live timeline, extracts real tweet text, filters ads and UI chrome.

Widget UI

glassmorphism overlay

The floating player. Renders the current tweet, transport controls, and the three adaptive layouts.

Audio Engine

playback

Receives one continuous MP3 stream, plays it back, and drives auto-advance through the queue.

tweet text →  ← one concatenated MP3
Firebase Backend · Cloud Functions v2 (serverless)

TTS Endpoint

Google Cloud Wavenet

Chunks text under the 5,000-byte limit, generates MP3 segments, concatenates them into one stream.

Stripe Webhook

payments

Receives checkout events, then writes a license document into Firestore for the new buyer.

onLicenseCreated

Firestore trigger

Fires when a license doc appears. Queues a transactional email instead of sending it directly.

Event-driven email pipeline
stripe
Webhook fires
firestore
licenses doc
trigger
writes to mail
extension
Trigger Email
gmail smtp
Key → user
The decisions behind the diagram
Why a Chrome extension?

Read the page, don't call the API

A web app would need Twitter's API, expensive, rate-limited, and fragile. A content script reads the live DOM directly, so it works with whatever feed the user already sees, no API logic to replicate.

Zero API cost, zero feed-logic duplication.
Why Cloud TTS?

The browser's voice cuts out

Chrome's built-in speechSynthesis silently stops after ~14 seconds, a dealbreaker for sustained playback. Google Cloud Wavenet runs server-side with far higher quality and no time limit.

Unbroken audio at ~$0.30–0.50 / user / month.
Why license keys?

No accounts, no auth tax

Instead of OAuth, sessions, and password resets, users buy through Stripe, get a key by email, and paste it into the widget. The right amount of friction for a pay-once utility.

Pay once, paste a key, done.
04 · Problems We Overcame

Where the build got hard.

Every interesting decision in the architecture above was forced by a problem that didn't show up until something broke. Here are the four that shaped the system.

01

The Chrome 14-second TTS cutout

Chrome's speechSynthesis API silently stops speaking after about 14 seconds. No error, no event, it just goes quiet. It's a known Chromium bug that has persisted for years, and for an extension whose entire purpose is sustained playback, it was a non-starter.

How it was solved

Pivoted to server-side Google Cloud TTS. The decision cascaded, it required a backend, Cloud Functions, and byte-chunking, but produced a far better product. It's also the moat: competing "read aloud" extensions all hit the same wall because they lean on the browser's built-in voice.

02

Scraping X.com's moving DOM

X is a single-page app with aggressive lazy loading, virtualized scrolling, and a structure that shifts under you. Tweets are rendered dynamically, recycled as you scroll, and interleaved with promoted posts and "who to follow" cards.

How it was solved

Careful DOM traversal and filtering rather than naive selectors, reliably identifying real tweet text, stripping timestamps, counts, and nav, dropping ads, and surviving the timeline mutating as new content streams in.

03

The first buyer never got their key

The launch email provider was Resend. After go-live, the first paying customer's license email never arrived. The webhook fired, the license was created, but the email silently failed. The cause: Resend's free tier on the default sender domain only delivers to the account's own address. Every other address is dropped with no error, no bounce, nothing in the logs.

How it was solved

Ripped out Resend for Firebase's Trigger Email extension over Gmail SMTP. The webhook no longer calls an email API directly, the onLicenseCreated trigger writes to a mail collection and the extension delivers it. Cleaner, more resilient, easier to debug.

04

Eventarc permissions & a missing link

Installing the Trigger Email extension threw an Eventarc permissions error, the service agent hadn't been granted its IAM roles yet, something Firebase doesn't warn you about. Separately, the first license emails shipped without a link to actually install the extension.

How it was solved

The permissions issue resolved itself after 2–3 minutes of propagation and a retry, the kind of thing docs never prepare you for. The email template got a styled "Install Xeder" button linking straight to the Chrome Web Store.

05 · Tech Stack

Everything under one roof.

A deliberately small surface area. As a solo build, minimizing ops mattered, the Firebase ecosystem covered functions, database, hosting, and email with near-zero infrastructure to manage.

Frontend
Chrome Extension
UI
Glassmorphism overlay
Backend
Cloud Functions v2
Database
Cloud Firestore
TTS
Google Cloud Wavenet
Payments
Stripe Checkout
Email
Trigger Email + SMTP
Hosting + SEO
Firebase Hosting
06 · The Launch

A debut on Product Hunt.

Xeder's first-ever Product Hunt launch landed it in the top of the day, a strong showing for a solo, pay-once utility.

#16
Rank of the day
114
Upvotes
103
Points
12
Comments
Xeder - Your X.com feed as a podcast | Product Hunt