Hoot
A cross-platform desktop music player with offline playback and a custom theme system.
- Role
- Design Engineer, independent project
- Period
- Independent project
- Status
- Shipped
Overview
A downloadable desktop music player built with React and Electron — offline playback, playlist management, and a custom theme system, packaged as a standalone app rather than a web page.
The problem
Most web-based music players assume a live connection and a browser tab that stays open. A desktop-native player needed to own its own window, work fully offline, and feel like an application rather than a website.
Design process
Built the interaction model around a persistent playback state that survives navigation between screens — the player bar never unmounts, matching how a native media app behaves rather than how a typical React route transition does.
Architecture
Electron shell around a React renderer, with playback state and file-system access isolated behind a main-process boundary so the UI layer stays focused on interaction, not file I/O.
Engineering decisions
Electron over a PWA
True offline file access and a persistent background player were simpler to guarantee with a native shell than to reproduce reliably through service-worker caching.
Trade-offs
Distribution weight
Shipping an Electron bundle costs a larger download than a web page — accepted in exchange for the offline guarantees a web build could not make.
Challenges
A theme system that reaches native chrome
Making custom theming feel consistent across both the in-app UI and the parts of the window Electron itself controls.
Outcome
A shipped, downloadable release with offline playback, playlist management and custom theming.
Technology
Images
Motion
Transport controls and playlist transitions use short, springy feedback — the kind of motion that makes a native-feeling player feel responsive, not decorative animation layered on top.
Lessons learned
- Persistent playback state belongs above the router, not inside a page — a media player is closer to an OS-level widget than a route.