DEV Community

Ry
Ry

Posted on AI-assisted

Hydra: Drink up, Sam!

Hacktoberfest Weekend Challenge: Build for a Friend Submission 🀝

This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend

What I Built

I built this for my friend Sam, who studies until 2 am and realises at midnight that the only thing they drank all day was a soda. Every reminder app they tried said the same thing, "Time to drink water!", and they just ignored it (like everything else).

The obvious fix is another app with an account, cloud sync and a notification schedule. But a water log is a diary of someone's habits: when they wake up, when they study, when they forget to look after themselves. That is personal and doesn't need to be on a server.

Hydra is a small desktop app that runs entirely on Sam's laptop. Sam sets a daily goal, logs water with +250 / -250, and ten small hearts fill up through the day. Every hour, Sam chooses; a local open-weight model writes a reminder that sounds like a friend wrote it. It uses a tone I set, a short description of Sam, the time, and how much is left. It arrives as a Windows notification decorated with ββœΏβ€ instead of emoji. The next morning it starts again from zero.

The look is inspired by Olivia Rodrigo's "the cure": a pastel blue polka-dot background, a pink lace heart, nurse hair clips, a bandage, and coral script lettering. The progress bar is ten small hearts that fill with red as the day goes on. Sam likes that song, so I built the whole window around it.

Demo

Hydra runs locally, so there is no hosted link.




UI inspired by Olivia Rodrigo's "the cure". Not affiliated with or endorsed by her or her label.

Code

Hydra

A local hydration buddy built for a friend. An open-weight model running on Ollama writes each reminder in her voice, based on her habits and how much she has had today. No API key, no cloud, no cost per message, and her data stays on her laptop.

Setup (Windows)

  1. Install Ollama from ollama.com, then:
   ollama pull llama3.2:3b
   pip install requests
  1. Run:
   python hydra.py

Use pythonw hydra.py for no console window. Errors go to hydra.log 3. On first run, config.json is created. Edit friend_name, tone and about_friend to personalize it. See config.example.json.

How it works

  • A startup window asks for today's goal and how much you've had.
  • A window with +250 / -250 buttons logs water.
  • A background thread checks every 30 seconds and sends a Windows toast every interval_min minutes, during active_hours, until the goal is reached.
  • If Ollama is down, it falls back to…

How I Built It

The core is a small open-weight model (gemma3:4b) served locally by Ollama. Around it: a Python loop, a Tkinter canvas for the UI, and native Windows toasts. No API keys and nothing to sign up for.

1. It started as a Chrome extension, and the reminders kept failing

My first version was a Manifest V3 extension using chrome.alarms. The alarms depended on a service worker that Chrome can stop and restart, and the reminders were unreliable. I rewrote it as a plain Python app with its own loop. That fixed the trigger problem and gave me a place to put a local model.

2. A local model writes the reminder

prompt = (
    f"You write one hydration reminder for {cfg['friend_name']}.\n"
    f"About them: {cfg['about_friend']}\n"
    f"Tone: {cfg['tone']}\n"
    f"Time now: {now}. They have had {d['consumed']} ml of "
    f"{cfg['goal_ml']} ml today, {remaining} ml left.\n"
    "Rules: max 20 words, no emoji, no symbols, no quotes, no hashtags. "
    "Output only the message."
)
r = requests.post(cfg["ollama_url"], json={"model": cfg["model"], "prompt": prompt, "stream": False})
Enter fullscreen mode Exit fullscreen mode

friend_name, tone, about_friend, model and flourishes all live in config.json. Changing what the reminders sound like is a text edit, not a code change.

3. The loop

A background thread wakes every 30 seconds and sends a reminder only if all three are true:

lo <= hour < hi and d["consumed"] < cfg["goal_ml"] and time.time() >= next_due
Enter fullscreen mode Exit fullscreen mode

It must be inside active_hours, under the goal, and past the interval. Water totals are saved with a date, so a new date counts as a fresh day, and nothing needs a reset button. If Ollama isn't running, a plain "X ml to go" message goes out instead, so a reminder is never lost.

4. Taming a small model

I told the model "no emoji". It sometimes added them anyway. So I strip emoji and symbols from its output with a regex, then append a random flourish from my own list (ββœΏβ€ββœΏβ€, ✿ ❀ ✿, ...). The decoration now comes from me, not from the model's mood.

5. What broke along the way

  • Reminders unreliable: chrome.alarms ran inside an MV3 service worker that Chrome can stop. Fixed by moving to a Python loop.
  • No popups on Windows 11: plyer notifications failed silently. Fixed with a native PowerShell toast, no extra package.
  • malformed bucket chain in Tcl_DeleteHashEntry: I was deleting canvas items inside their own click handler. Fixed by running every action through root.after(20, ...).
  • Model kept adding emoji: small models sometimes ignore "no emoji". Fixed by stripping them with a regex and appending my own flourishes.
  • Greeting showed the old name: config.json overrides the new defaults. Fixed by editing the file, since defaults only apply when it's missing.

6. The UI

The theme comes from Olivia Rodrigo's "the cure" artwork. There are no UI libraries and no image files. Everything is drawn on one Tkinter canvas:

  • The lace heart is a parametric heart curve with scalloped circles along its outline.
  • The nurse clips, bandage and progress hearts are polygons.
  • The polka dots are a grid of offset ovals.
  • Text uses Segoe Script, which ships with Windows.

Why Does Open Innovation Matter?

The data never has to leave. A water log shows when someone wakes, studies and forgets to eat or drink. With a closed API, every reminder is a request to someone else's server carrying that. With a local model, Hydra works with the Wi-Fi off.

Free to run, with no secrets to leak. A reminder every hour would otherwise mean a paid API and a key to protect. Here, there is no per-message cost, and I could publish the repo as it is, because there is nothing to hide in it.

The personality is a text file. Swapping the model is one line in config.json. Making the reminders gentler, funnier or more specific to Sam means editing two sentences.

Where a closed model would have been better: the writing. A frontier model would write funnier, more varied reminders than a 4B one, and it wouldn't need an emoji filter. I accepted clumsier messages in exchange for nothing leaving the machine.

My Agent Session

Built over one weekend, a chat session with Claude helped with the debugging of the Windows notification code.

Prize Categories

Best Use of Gemma: Hydra runs gemma3:4b locally through Ollama. Gemma writes every reminder Sam receives, using a tone, a short description of Sam, the time, and how much water is left. It runs on Sam's laptop, so no API key is needed, and nothing leaves the machine.

Top comments (0)