Building Hydrame: smart reminders without the nag
A reminder app lives or dies by its notifications. Here's how I designed Hydrame's to nudge you gently — and adapt to your day instead of fighting it.
Most reminder apps have the same fatal flaw: the reminders. They fire on a fixed schedule, ignore what you’re doing, and slowly train you to swipe them away without reading. Once a notification becomes noise, the whole app becomes noise.
Hydrame is a water-tracking app, and water tracking is entirely a reminder problem. If the nudges are good, the app works. If they nag, you uninstall it by Thursday. So I spent most of the design time on exactly one question: how do you remind someone to drink water without becoming the thing they resent?
Fixed schedules are the enemy
The naive approach is an alarm every two hours. It’s easy to build and miserable to live with. It buzzes during meetings, while you’re asleep on a nap, and ten minutes after you just drank a glass. Each mistimed reminder costs you a little trust, and you don’t get that trust back.
The insight that shaped Hydrame: a reminder is only good if it arrives at a moment you could actually act on it. Everything else is noise wearing a helpful costume.
Adapting to the day
Instead of a fixed timer, Hydrame spreads a daily goal across your waking hours and only nudges when you’ve genuinely fallen behind and enough time has passed since the last nudge. Logging a drink pushes the next reminder out. Drink consistently and you might not hear from it at all — which is the whole point.
The core scheduling logic is deliberately small:
interface ReminderContext {
now: Date;
wakeHour: number; // e.g. 8
sleepHour: number; // e.g. 23
goalMl: number; // daily target
consumedMl: number; // logged so far today
lastNudge: Date | null;
minGapMinutes: number; // breathing room between nudges
}
function shouldNudge(ctx: ReminderContext): boolean {
const hour = ctx.now.getHours();
// Never during sleeping hours.
if (hour < ctx.wakeHour || hour >= ctx.sleepHour) return false;
// Respect a minimum gap so we never feel pushy.
if (ctx.lastNudge) {
const gapMin = (ctx.now.getTime() - ctx.lastNudge.getTime()) / 60000;
if (gapMin < ctx.minGapMinutes) return false;
}
// How much *should* you have had by now, given the day's progress?
const dayLength = ctx.sleepHour - ctx.wakeHour;
const elapsed = Math.max(0, hour - ctx.wakeHour);
const expectedMl = ctx.goalMl * (elapsed / dayLength);
// Only nudge if you're meaningfully behind the curve.
return ctx.consumedMl < expectedMl * 0.85;
}
There’s nothing clever about the math, and that’s intentional. The 0.85 threshold means “behind, but not punishingly so.” The minGapMinutes guard is the difference between a companion and a nag. Most of the work was removing reasons to fire, not adding them.
The notification copy matters as much as the timing
A perfectly timed reminder can still feel bad if the words are wrong. “You’re failing your goal!” is technically a nudge. It’s also a tiny act of hostility you’ve invited onto someone’s lock screen.
Hydrame’s reminders are short, warm, and never guilt-trippy:
- “A good moment for a glass of water.”
- “Halfway through the day — a little catch-up?”
No exclamation marks fighting for attention. No streak about to “break.” The tone is a friend mentioning it, not a coach yelling about it.
Calm by default, configurable by choice
Defaults are the most important design decision in any app, because most people never change them. So Hydrame ships quiet: a conservative number of nudges, none at night, all easy to dial down further.
If you want tighter goals or more frequent check-ins, it’s there. But you opt in to more, never out of less. That single inversion — calm as the floor, not the ceiling — is most of what makes the app feel respectful.
The goal was never to maximize notifications. It was to earn each one.
What I’d tell anyone building a reminder app
- Tie reminders to actionable moments, not the clock. A nudge you can’t act on is just noise.
- Always leave breathing room. A minimum gap between alerts is non-negotiable.
- Write the copy like a person, not a metric. Warmth costs nothing and buys a lot of trust.
- Make the calm option the default. People live in your defaults.
Hydrame is still in active development, and notifications are the part I keep refining. If you want to know the moment it lands, tell me — I’ll let you know. Quietly, of course.