When I was at UC Riverside, I’d drive between the Bay Area and Southern California pretty much every quarter. Sometimes I’d come back during the quarter too, usually for holidays. Those drives got tedious. There’s only so much music I can listen to before it starts feeling like the same songs and beats over and over.
I wanted something that would keep me interested for longer. Something with a story I could follow instead of just more background noise.
I’d been reading manga since high school, so light novels felt like the next step. They had the kinds of stories I liked, and a lot of them were really long. Once I realized I could listen to them as audiobooks, they were pretty much exactly what I was looking for.
It started as a way to make the drive less boring, but eventually it became a hobby. I’d listen while walking to class, doing dishes, or doing anything routine that didn’t need too much attention.
Not coding, though. I can’t follow a story and think through code at the same time. I don’t have that kind of multitasking ability.
Eventually, I wanted my own setup
I used ElevenLabs and Speechify for a while. I liked the voices, and they had a lot of the features I wanted. They also helped me figure out what I cared about in an audiobook app.
But in my own use, I’d sometimes run into delays, pauses, or other interruptions when generating or playing audio depended on the cloud. They weren’t huge problems individually, but they got annoying when I was in the middle of a story.
Eventually, I thought, why don’t I just make my own?
I was already a software engineer, and I’d been experimenting with AI coding tools since around when ChatGPT came out. I’d tried tools like Cline and Cursor, and later Claude Code and Codex. This seemed like a good project to work on because I knew exactly what bothered me, and I actually wanted to use the result.
That turned into a few related projects. Epub Listener handles turning a book into audio. Hark is the player. Kestrel is the speech-generation work. They handle different parts of the same thing: getting a book into a form I can listen to, then making the listening experience work the way I want.
I also spent time working on an open-source audio model on my older M2 MacBook with 16 GB of RAM. The goal was to make generation faster while keeping the voice good enough for long listening sessions. There are quality tradeoffs, so I wouldn’t describe it as getting all that speed for free. But I wanted to see how far I could get with the computer I already had.
Keep the audio on my device
For the player, I took a lot of inspiration from ElevenLabs. The biggest thing I wanted was simple: if I already have an audiobook downloaded on my device, just play that file.
I didn’t want to upload it to a cloud database first. Some of these books are hundreds of hours long. I’m using free hosted storage, and storing all that audio there wasn’t going to make sense.
Keeping the audio locally also means playback doesn’t depend on fetching it from a server. Generating the audio is a separate step. Once I have the file, I can listen to it directly.
That doesn’t mean every part of the app has to be offline. Things like syncing progress are different from storing the entire audiobook. I just didn’t need the audio itself going back and forth.
Let me undo losing my place
The other feature I really wanted was playback history.
Imagine a 300-hour book squeezed onto a tiny seek bar. You accidentally touch the wrong spot, and now you’re somewhere completely different. Maybe you’ve jumped sixty hours away from where you were listening.
Then you have to figure out where you were before you touched it. Dragging the same little slider around and guessing isn’t a great solution.
So I added a history of playback actions with saved positions I could return to. If I accidentally jumped somewhere, I wanted to go back to where I was, not spend the next few minutes trying to find it again.
It’s a pretty small feature to explain, but it solves something that genuinely bothered me. The longer the book, the more useful it becomes.
That’s basically how I’ve kept building the app. I use it, something annoys me, and I change it. I’m not trying to guess what someone might want from an audiobook player. Most of the time, I’m fixing something I just ran into myself.
A project I actually use
Along the way, this also became a way to improve how I work with AI coding tools.
Having a project I understood and used regularly made that easier. I could try different ways of describing a change, working through an implementation, and checking whether the result actually did what I wanted.
And I had a reason to care beyond getting the tool to say it was finished. I was going to use the app again. If a change didn’t work, I’d run into it while listening.
That gave me somewhere to refine those workflows before bringing them into my work as a software engineer.
I use the setup every day now, and I’m still changing things. Sometimes it’s the player, sometimes it’s the audio generation, and sometimes it’s how I work with the coding tools.
Originally, I just wanted the drive to Riverside to be less boring. Now I have my own audiobook setup, and I’m still listening to light novels while doing the dishes.