Project write-upmacOS · iOS · AndroidOctober 2026

⚓︎ Anchor

I built a set of apps that see where my attention actually goes, compare it with what I planned, and step in when the two come apart.

The wallpapers. Anchor redraws the Mac's desktop every half hour, so the answer to "how is today going?" is always behind my windows. The Mac version follows the system's Light and Dark appearance, as this mockup follows yours, and every text colour passes WCAG AA. The Android app draws a portrait version for the home and lock screens, leaving the top for the clock and the bottom for the shortcuts. The iPhone, which doesn't let apps set the wallpaper, gets the same card as widgets.Example data

I'd just started working through data structures and algorithms (DSA), alongside building side projects. Within days I could see the problem: my plans were fine, but following them wasn't. I'd sit down for a DSA block and an hour later find myself refactoring a project, or on a shopping site, with no memory of deciding to switch. I wanted to fix that in the first week, not after months of it.

Time trackers didn't help, because they report the drift afterwards. I wanted something that knew my plan, watched what I was actually doing, and spoke up while it was happening. So I built Anchor. It does three jobs: it remembers my goals, it sees where my time goes, and it shows me when I'm drifting.

8days from the first commit to daily use
86commits
3apps: Mac menu bar, iPhone, Android
31tests that run before every release

How it fits together

Everything is worked out on the Mac. It reads my screen time from ActivityWatch, which records the active app and browser tab only while I'm at the keyboard. It also reads my timer, my calendar, and a health summary from my iPhone. From that it works out a single number for the day, the share of my tracked time that went to the goals I'd chosen, and draws everything else around it.

The phones never see the raw data. The Mac encrypts a small summary and uploads it, and the phones decrypt it with a key that only travels inside the QR code used to pair them. The relay in the middle can't read what it carries.

What it reads

  • ActivityWatchapps, window and tab titles
  • Sand Timerfocused minutes per project
  • Calendarthe plan, via EventKit
  • Apple Healthsleep, exercise, heart rate (opt-in)
  • Manual Check-ins"where did 13:30 go?"

Mac menu bar app

  • Sorts every minute into a goal, Building, Other or Drift
  • Estimates energy hour by hour
  • Compares the calendar with the screen, once a minute
  • Writes a daily log and an encrypted snapshot

Where it shows up

  • Wallpaperdesktop and lock screen
  • Menu bar popoverEnergy, Today, Stats
  • Nudge windowcentred, shows even in a Focus
  • Bedtime screenfull screen, during Sleep
Encrypted snapshotFirebase relay (can't read it)iPhone app and widgets·Android app, widget and wallpaper
Local first. The Mac does the work. The phones get an encrypted summary, including the next 36 hours of calendar blocks, so they can nudge at night while the Mac sleeps.

Teaching it what counts

The hardest part was deciding what a minute is. A terminal can hold DSA practice in one tab and a side project in the next. A browser can be LeetCode or Reddit. Anchor applies a short list of rules, in order, and the first one that matches wins:

  1. A window or tab whose title contains a goal's keyword counts toward that goal. Naming a terminal tab "dsa" is enough.
  2. A goal's own websites and pages count toward it, along with any apps I've added to it.
  3. A helper app, like the Claude app, counts toward whichever goal's timer ran for ten or more minutes that hour.
  4. Terminal work that isn't labelled counts as Building. It's tracked, but it doesn't count toward the day's score.
  5. Known distraction sites count as Drift. Everything else is Other.

The rules were never going to be complete, so the nudge itself became the way to correct them. When Anchor flags something I was actually using for the goal, like a trace-table tool I wrote for stepping through loops, one button adds that page to the goal. It saves the page's address, not the whole domain, so adding one tool on a site doesn't turn every page on that site into DSA time.

EnergyTodayStats
Plan and energyHighMediumLow
From Apple Health: slept 7h 10m last night · HRV 52 today (usual 48) · resting heart rate 58 (usual 60).
⚓︎ DSA until 11:30

1 of the last 2 min went to tools.example.dev. Back to DSA?

Left: the Energy tab. Today's calendar sits beside expected energy, estimated from Apple Health data that the iPhone sends each morning: last night's sleep and wake time, and today's HRV and resting heart rate against my usual. My past days' focus at each hour adds to it. The line under the title says which numbers went in, or says none have arrived yet. A DSA block that lands in a low-energy hour gets a warning, so I can move it in the morning.Example data
Right: a nudge. If the screen doesn't match the calendar block, a small window appears in the middle of the screen. The first version used notifications, and a Focus mode silenced every one of them. A window can't be silenced that way.

Designing a screen I can't argue with

The feature I changed most was the bedtime screen. My calendar has a Sleep block, and the phones and the Mac know when it's running. During that block, using a device brings up a full-screen "Time to sleep" screen.

The first version had a big "Going to bed" button. I tapped it on reflex, closed the screen, and kept scrolling. A default answer let me dismiss the screen without thinking about it. Every change after that was about making me stop and decide:

A default button lets you dismiss a screen on reflex. A slider that starts at zero makes you decide.

Try it: move the slider, then snooze.
A working copy of the bedtime screen. It follows the same rules as the apps, which are shared between the Swift and Kotlin code and covered by tests on both. One test came from a real bug: sliding right and back to zero used to turn the button back on.

Heart rate, honestly labelled

With an Apple Watch, the iPhone can send heart rate for each half hour. The Mac lines each half hour up with what I was doing, from my Manual Check-in answer, sleep, screen time or timer, and shows how my heart rate differs by activity. It also flags "raised" half hours: 20 or more beats a minute above that day's resting rate while I was sitting still. Sharing is off by default, and the screen says plainly that this is a rough signal, not a medical one.

Heart rate by activity, one week. The dot is the average and the band is the usual range. In my data, drift was the most raised activity. That's a pattern to look into, not a diagnosis.Example data

Eight days, built in public with myself

Anchor started as a Python script that drew a wallpaper. By the end of the first day it had an Android app. The Mac app replaced the scripts on day two. The rest of the week was spent living with it and fixing what annoyed me, usually the same evening.

Commits per day, with what changed. The quieter days in the middle went to Manual Check-ins and polish, and to using it and noting what felt wrong.

What I learned

Test on yourself, the same day

Most of the best changes came from a screenshot and one sentence of annoyance: "why does it say Ghostty when the page is Chrome?" Using it all day was the real test suite.

Platforms have opinions

macOS Focus hides notifications. Android 15 blocks background apps from opening screens. iOS Screen Time needs a signed developer account. Each one changed the design, not just the code.

Write the rules down as code

Once the bedtime rules lived in one small, UI-free module on each platform, a bug became one failing test, and a release script now refuses to ship if any test fails.

Keep the data at home

Health and screen data are personal. Doing the work on the Mac and relaying only an encrypted summary kept the cloud part small and simple.

What's next

The backlog holds Screen Time nudges on the iPhone, an always-on notification for phones whose lock screens don't allow custom widgets, and bringing the Mac's Apple-style design to the phone apps. The question I'll keep coming back to is the one on the wallpaper: did today go where I meant it to?

Try it early

Right now Anchor is set up for me. Next, I'm turning it into something anyone can install and set up in a few minutes. If you'd like to try it when it's ready, leave your email.

I'll email you once, when Anchor is ready to try. Nothing else, and your address isn't shared with anyone.