morph
Staying motivated to exercise is hard. Fitness apps are full of features and graphs, but often lack the social pressure that actually gets people moving.
I wanted something simpler: a daily leaderboard where you and your friends compete to see who can walk the most steps. No complex features, no endless customization - just daily competition and a bit of friendly trash talk.
the concept
Morph is a competitive step counter where the only thing that matters is today's ranking. Every morning, the counter resets, and everyone starts fresh. You can see who's in the lead in real-time, check your streak of consecutive days, and when someone's beating you, you can "throw tomatoes" at them via push notifications.
It's intentionally minimal. No weekly goals, no achievement badges, no elaborate stat tracking. Just: how many steps did you take today, and where do you rank?
gamification
The psychology is simple: public accountability and competition change behavior more than private goal-setting. When you can see your friend beating you by 2,000 steps, you're more likely to go for that extra walk.
The streak feature adds another layer - once you've logged steps for several days in a row, you don't want to break the chain. It's the Jerry Seinfeld productivity method applied to fitness.
And the tomato button? Pure fun. It sends a cheeky push notification to whoever you're competing with, keeping the competitive spirit light and playful.
technical implementation
Morph is built as a Progressive Web App using:
- Angular with standalone components
- Firebase Firestore for real-time data synchronization
- Firebase Authentication for user management
- Firebase Cloud Messaging for push notifications
- Angular Service Worker for offline support and PWA capabilities
The real-time aspect was important. When someone logs their steps, everyone else sees the leaderboard update immediately without refreshing. Firestore's snapshot listeners make this very easy.
pwa architecture
Making Morph a PWA meant users could install it on their phones and use it like a native app. The service worker caches the application shell, so it loads instantly even on slow connections.
Push notifications work even when the app isn't open, thanks to Firebase Cloud Messaging integrated with the service worker. This was necessary for the tomato-throwing feature to feel responsive and fun.
data structure
The data model is straightforward:
- Users have a name, photo, email, and optional streak data
- Metrics are daily records with date, user, total steps, and ranking
- Rankings are calculated by querying metrics for today's date, ordered by total steps
Each day gets its own set of metric documents, making it easy to query historical data and show profile statistics over time.
challenges
The main technical challenge was handling timezone differences correctly. Everyone should see "today" in their local timezone, but the database needs a consistent reference point.
I also had to improve Firestore queries to avoid excessive reads. Initially, I was recalculating rankings on every update, but that quickly became expensive. Instead, I batch-calculate rankings once per day and denormalize the data.
lessons learned
Building Morph reinforced several principles:
- Social features increase engagement - The leaderboard and tomato button created way more motivation than personal goals ever did
- Simplicity is powerful - Removing features made the app more focused and easier to use
- Real-time updates feel magical - Firestore's instant synchronization provides good UX with minimal effort
- PWAs are underrated - Getting installed on someone's home screen dramatically increases usage
reflection
While Morph started as a simple project for friends, it became a playground for learning PWA development, real-time data sync, and push notifications. The competitive aspect genuinely worked - people checked it multiple times per day during active use.
The name "morph" came from the idea of transforming through small daily habits, though honestly, it was mostly chosen because the domain was available.