Symlach

You should not have to pay a subscription to breathe

“Make it work, make it right, make it fast.“ ― Kent Beck

I’ve used breathing exercises for years, to fall asleep, to settle nerves before a presentation or just to feel better in the moment. It still amazes me how much a few minutes of focused breathing can improve how I feel. Recently, I’ve been struggling to switch off at night, so I went looking for an app to help me do some guided breathwork.

I downloaded a few mobile apps, but I found them to be either clunky or overly complex for my needs. There were a couple that I did like, but they either had ads or constantly prompted me to sign up to a subscription. I just wanted something simple, so I thought, why not build my own?

I wanted to keep the app simple. I only had one evening to build it. The app only needed to serve me, so it didn’t need to be as polished as I’d usually aim for.

It’s all too easy to let the excitement of watching something get built and lose sight of the original problem that needs to be solved. To stop this happening, I find it’s worth mapping out user needs before diving into the implementation, even when I’m going to be the only user. Making the desired user journey explicit by getting in front of a whiteboard, mapping out pain points and sketching what it is they’re trying to do gives me something to return to as things evolve, so I stay focused on what matters. Those sessions surface errors in thinking early, and serve as a catalyst for wider discussions around the core problem. By the end of those meetings, the solution is rarely what anyone thought it would be going in.

Normally I’m not the user and the person building the app so this would involve lots of different stakeholders, but here I’m both so standing in front of a whiteboard and switching between the roles of engineer and user felt like a weird thing to do. But why change what I know works? Despite sharing an office with my wife and getting the occasional concerning glance, I did it anyway. Putting my ego aside and letting go of the fear of looking daft has given me countless opportunities to learn things I wouldn’t otherwise throughout my career. For me it’s all about learning and finding the best solution for the user.

Anyway back to it, here’s what I came up with:

I’d normally start a new project with a walking skeleton (a habit I got into many years ago after reading Growing Object-Oriented Software Guided by Tests). A local environment with deterministic mocks, unit, integration and end-to-end tests, standardised logging, pre-commit hooks, linters, formatters, and a CI/CD pipeline. Building those foundations first has always paid off in the long run as a project evolves (it also happens to be the bit I enjoy most). Retrofitting these foundations later usually ends up feeling like an uphill battle, by that point bad habits have already crept into ways of working and the codebase. Since I only had an evening and building something high-quality wasn’t the goal, I kept this to the bare minimum.

I used Claude Code and Claude Design to build most of the app. For those who’ve been building with AI for a while, I’ll assume you know the basics that get you 90% of the way there. If you’re new to Claude, I’d recommend checking out Anthropic’s courses.

For anyone interested in the tech stack:

ToolNotes
ReactI’ve used react for 9 years on the job. It has its quirks, but I’ve yet to work anywhere I could avoid it. Why start now?
TypeScriptI’m scarred from past projects with no type safety, I’m always going to pick something that offers it where I can.
ViteIt builds fast and is simple to use, all I really need for a small project like this.
TailwindI probably could’ve used standard CSS, but I started to use Tailwind before AI started writing my code. Some habits are hard to break.
vite-plugin-pwaGives me everything I need to make the app installable on mobile.
Lucide ReactPretty standard for React icons.
ZodMaybe adding validation is overkill for this app, but I can’t shake the habit of assuming all input is invalid. Even when it’s my own.
Vitest + React Testing LibraryI’ve been using React Testing Library since 2019 and haven’t found anything better. With the right component structure it gives me the confidence I need for a project like this without having to introduce something like Playwright.
Google FontsIt’s free, and it’s cheap like the budget.
Cloudflare PagesIt’s free and straightforward deployment gives me all I need.

After a few doodles on my whiteboard and a couple of hours prompting later, I had an MVP. I spent another hour using it and making some small improvements to the user experience.

Here’s the result:

Demonstrating how to install the app on your phone for offline use.

I’m glad I went the PWA route. Releasing to app stores is a tedious process, especially for a personal app that only I plan on using. The PWA bypasses all of that, and compared to the React Native apps I’ve built in the past, there were nowhere near as many gotchas. Well worth the minor native feature tradeoffs.

Demonstrating the basic functionality. Sound cues removed.

The app lets you pick from a set of breathing routines with short explanations of what each does. A basic menu handles the rest: theme toggle, sound cues, instructions for installing as an app on your device. The routine screen keeps it minimal. An expanding circle paces you through each phase, with options to pause and stop. I originally included a breath phase counter but found it unnecessary and distracting. The visual cue using the circle was enough for my needs. After using the app during the day, I added a quick sound toggle to the routine screen that was independent of the default setting found in the main menu.

Demonstrating the ability to create custom breathing routines.

Following the same routine can get stale, and sometimes a breathing phase feels slightly off. The ability to customise phases in half-second intervals fixed that. The custom routines are stored in local storage, which is good enough for now.

Summary

In total it took around 3 hours to build the app. I deployed it to Cloudflare Pages, but later decided to take it down until I review the code more thoroughly and add some suitable guardrails around custom routine parameters. I don’t want someone creating a custom routine that causes them to hyperventilate. When I get time, I’ll put it back online if anyone is interested in using it.

In the meantime, I’ve saved myself yet another subscription. Huzzah!