
TL;DR
I built a single page ambient focus dashboard because I was tired of jumping between apps to manage tasks, timers, music, and notes. Tannify brings everything into one calm space so I can drop into deep work faster. It’s fully usable today, but still unpolished and paused while I focus on other projects.
Why I Built This
I hit a point where my workday felt scattered. I would open my task manager, then Notion, then Spotify, then a timer app, then another site to check the weather, then maybe Discord for accountability. By the time I returned to the actual task, the momentum was gone.
I wanted one place where I could breathe, plan, and focus. Something I open once and stay in. Something that doesn't feel like "tools everywhere," but a workspace with atmosphere.
And I'll be honest: I also wanted to build something fun. A dashboard with lofi music, ambient sounds, and realtime presence. A digital study room where you're not technically alone.
What I Was Trying to Solve
The main problem was context switching. Every tool solved one small thing, but none helped me stay in flow. Most existing dashboards or productivity apps were:
- too bloated
- too rigid
- or too "productivity-guru-influencer-aesthetic"
Tannify is simply a page you open when you want to focus. No onboarding, no complex structure, no hierarchy. Just tasks, focus, sound, presence.
The people who feel this are builders like me: developers, students, indie hackers, anyone who appreciates calm tools that don't try to control your workflow.
How I Actually Built It
The Journey
I started ambitious. I wanted to try Next.js Parallel Routes because I thought it would be a clean way to show dynamic widgets independently. In reality, it was the opposite. The DX was rough for this kind of dashboard, debugging was painful, and everything felt heavier than it needed.
After a few hours of fighting it, I scrapped that idea and went back to simple modular components. And everything instantly clicked. Clean, readable, predictable.
From there I built the foundation:
- task list
- pomodoro timer
- ambient mixer
- lofi player
- notes
- weather
- calendar
- real-time presence avatars
All stitched into one server rendered dashboard using Next.js 16 and React 19.
The Stack
- Next.js 16 for the main layout
- React 19 for the interactive widgets
- Supabase Auth for login
- Supabase Realtime for presence
- Zustand stores for timers, mixer levels, and settings
- shadcn/ui for the design system
- Server Components by default, client only where needed
- Single layout grid, responsive, with all widgets as independent components
Key Features I Focused on First
-
The ambient mixer Ocean waves, thunder, rain, café noise, vinyl crackle. You mix them however you want. It's probably the feature I use the most.
-
Pomodoro timer with XP Deep work sessions give XP. A gamified touch, but subtle.
-
Real-time presence I wanted a tiny "study with me" feeling. Avatars of whoever is online in the bottom right. Technically simple but emotionally powerful.
-
YouTube lofi streaming No need for a separate tab.
Screenshots

The dashboard with the ambient mixer, pomodoro timer, sticky notes, weather, calendar, and real-time presence avatars.
Interesting Technical Decisions
Supabase Realtime Presence
- One room
- Users join on page load
- AvatarGroup component listens and updates
- It works surprisingly well with minimal code
Audio Architecture
- Zustand store handles mixer volume
- Each sound is its own HTML
<audio>element - Everything runs client-side for low latency
Server vs Client Components
The dashboard page is a server component, but each widget becomes a client component only when necessary. This keeps hydration small and performance high.
Parallel Routes Attempt
This was my first big mistake. I wanted to learn Parallel Routes, but for a dynamic, widget-heavy dashboard, it introduced unnecessary fragmentation. Debugging layout shifts and rendering logic was not worth the trouble.
Switching back to components fixed everything.
Problems I Hit
-
Parallel Routes making everything harder Not a "big" disaster but frustrating. It taught me to resist the urge to over-engineer just to learn a feature.
-
Mixer performance on mobile Audio stuttering happened early. Solved by lazy-loading audio elements and using
suspendattributes. -
Supabase RLS blocking writes Classic. I forgot UPDATE policies and spent too long debugging why tasks wouldn't update.
What I Learned & Would Do Differently
- Simplicity wins. A single page with clean components beats clever routing tricks.
- Realtime presence is easier than I expected and adds surprising value.
- UI polish takes more time than core logic.
- Don't optimize for "cool tech to learn" in the early stage. Build the thing, polish later.
Next time, I'd probably:
- refine the layout system
- polish widget interactions
- extract a small design system
- introduce modular widget definitions, so users can rearrange their dashboard
Where It Stands Now
Tannify is usable but not polished. Several widgets still need real integrations:
- alarms
- real weather data
- connecting your Google account for events or tasks
- better transitions
- simplified mobile experience
It's paused while I work on higher-priority projects, but I'll return to it. I still want it to become my personal "focus hub" when I sit down to work.