Hamlet is a self-hosted media server for audiobooks, podcasts and ebooks. Point it at a folder of files, and it gives you a web client with chapters, playback positions, and real-time library updates, all running on your hardware, under your control.
It's in active development. The server works, the web client works, and the rough edges are mostly in the gap between "runs on my machine" and "runs on yours."
Hamlet scans your media folders, extracts metadata and cover art with FFmpeg, and serves a web client you open in any browser. No app to install on every device. Just a URL.
Point Hamlet at directories of audiobooks, podcasts and ebooks. It reads chapter markers, durations and embedded cover art from the files themselves. No manual importing, no file renaming.
A single-page web client served from the same process. Chapter-aware playback, adjustable speed, a sleep timer, an in-browser ebook reader, and your position kept per book, per show and per read, from any browser on any device on your network.
Drop a new file into a watched folder and Hamlet picks it up. The client gets a live update via server-sent events. No polling, no page refresh, no waiting for a scheduled scan.
A single Node.js process with a SQLite database. No cloud dependency, no external services, no telemetry. Everything lives on the machine you run it on, and nothing leaves it.
Search and subscribe by feed, download episodes on a schedule, and let auto-download rules prune the oldest downloaded episodes so a backlog doesn't quietly fill the disk.
EPUB and plain text open right in the browser, with a reading position tracked separately from anything you're listening to. An audiobook and its companion ebook can share one library entry.
Deliberately boring choices. One process, one database file, standard tools.
| Layer | Role | Choice |
|---|---|---|
| Server | Core | Node.js + TypeScript + Fastify. One process serves the API, the client, and cover art. |
| Database | Core | SQLite via Drizzle ORM. A single file at ./data/shelf.db. Back it up by copying it. |
| Media probing | Core | FFmpeg and ffprobe on PATH. Extracts duration, chapters, and embedded cover art from your files. |
| Live updates | Core | Server-Sent Events. No WebSocket complexity, works behind reverse proxies without configuration. |
| Podcasts & ebooks | Core | fast-xml-parser for RSS feeds and EPUB metadata, adm-zip for EPUB containers, node-cron for scheduled auto-downloads, epub.js (client-side) for in-browser reading. |
| Auth | Infra | JWT access tokens (24h), argon2 password hashing. Bearer auth on all API routes. |
| Deployment | Infra | Dockerfile included (node:20-slim + ffmpeg). Docker Compose with volume mounts for /audiobooks and /podcasts, or run bare with npm run dev. |
In order, not on a schedule.
Tap a milestone for the details.
A Docker image and a GitHub repo you can clone, point at your media, and open in a browser. The scanner, API, web client, auth, podcasts and ebook reading are all already working end-to-end. What's left is packaging, plus the kind of edge cases that only show up on someone else's machine.
docker compose up, open localhost:3000, create an account, and your audiobook, podcast and ebook library is there with chapters, covers and reading position. Playback position syncs across tabs and devices.
MOBI and AZW3 files already show up in your library and get catalogued with the rest of your ebooks; actually reading them in-browser is next, the same way Leaflet already does on Android.
Hamlet is self-hosted and in active development. Audiobooks, podcasts, and now ebooks too. Point it at your files and open your browser.
Hamlet · a Letworks project · siblings: Leaflet · Wavelet · Trust & Security · built with Node.js, TypeScript & Fastify