Inspiration

I am very forgetful.

Not just with random little things. I forget things that are actually important to me, including things I genuinely want to do. I usually have a lot going on, and I've learned that if something isn't in front of me, there's a pretty good chance it's going to disappear from my brain.

I tried the normal solutions. Putting habits on my calendar made my calendar way too noisy. Phone alarms got annoying fast. Habit tracker apps would work for a week or two, but eventually I'd get tired of the notifications, or I'd just forget to open the app.

A few years back I saw Simone Giertz's Every Day Calendar and fell in love with it. A big brass panel, 365 buttons, you press one every day and it lights up. A beautiful object, and always there. Much later I built an app that did the same thing, one checkbox for one day, and left it running on a tablet in the kitchen. Then I actually lived with it, and it didn't work for me either. I got too far behind, a vacation came up, and it turned into a burden.

Physical habits were the worst. I love rock climbing, and when I'm in the habit of going, I'll climb two or three times a week. But if I got sick and missed a week or two, the whole routine would fall apart. Even after I got better, I sometimes wouldn't go again for months. Not because I didn't want to. I would honestly just forget that climbing had been part of my normal routine.

That's when it clicked that every day was the wrong unit. Some habits really are daily, but a lot of the ones I struggle with most only need to happen a few times a week. Checking a box on a day I did nothing felt like cheating, and skipping it felt like failing.

But the always-there part of that brass calendar was right. No buzz, no alarm, no notification I swipe away and forget about ten seconds later. Just a pleasant thing I walk past. I also didn't want to spend $300 on yet another dedicated screen to make the idea work. I had old devices lying around, and I wanted this to run on whatever people already own.

That's what became Steadily.

What it does

Steadily is a habit tracker built around two ideas: always in sight, and weekly goals. It runs on most touchscreens made in the last decade.

You set a goal per week instead of per day. "Work out 3x", "read twice", "meditate once". Any day counts, so a busy Tuesday doesn't break anything and a missed morning still leaves plenty of week.

My setup is pretty simple. I keep an old tablet on a table with Steadily open all the time. I walk past it, see the things I wanted to remember, and when I do one of them, I tap the button. There isn't an alarm telling me I need to do something at exactly 3:00 PM. The reminder happens because I naturally see the screen while going about my day. That sounds almost too simple, but that's kind of the point.

Your whole year sits on screen as a field of watercolor dots. Each week you hit a goal, another set dries green. Sometimes I look at it and think, oh nice, I'm actually doing pretty well. Other times I look at it and think, wow, that is a lot of red. Both are useful. If I'm doing well, I get a little positive reinforcement every time I see it. If I'm not, I can think about why. Maybe I need a little more effort, or maybe the goal I set was unrealistic and I should change it. I wanted that feedback to happen gradually instead of an app telling me I "failed" because I missed a day.

Steadily also has rest weeks. Sick, traveling, injured, or just need a break? Mark a habit as resting and it doesn't count against your year. That feature came directly from the climbing problem. Getting sick used to break the habit completely. I wanted a way to say, "I'm not quitting this. I'm just not doing it this week."

No daily streaks to lose. No badges. No notifications. None of the little tricks that pull you back into an app. The painting brightens in the morning and settles into darker tones at night so it belongs in the room instead of glowing at it.

Free to use. Steadily Pro ($1.99/month or $19.99/year with a 1-month free trial, or $49.99 once) adds sync across devices, past years, sharing your year as a painting, extra backgrounds, and custom background colors. Anyone can also earn a free week of Pro by watching a rewarded ad.

How I built it

I've been building and using Steadily myself for about a year. The first version was a React web app with a Node/Express backend on PostgreSQL. It worked fine on a laptop or a new tablet, but one of the main goals was to run well on old hardware, and once I tested it on the older tablets I actually wanted to use, it just wasn't fast enough. The devices I wanted to give a second life to were exactly the ones it struggled with.

So for Shipaton I rewrote the entire client in Godot 4.7 / GDScript. One codebase now runs on iOS, Android, web, macOS, Windows, Linux, Raspberry Pi, and even VR headsets. The watercolor is real-time GPU shader work: paper grain, pigment granulation, edge darkening, and a day-cycle clock that shifts the palette with the light in the room.

Three native Godot plugins, each with a Kotlin (Android) and Objective-C++/Swift (iOS) half:

  • steadily_purchases, written during Shipaton, wraps RevenueCat (purchases-android 10.14, purchases-ios 5.83) plus RevenueCat Ads over AdMob for the rewarded "Pro pass", with Google's consent SDK and device probes for stores that are not Google Play.
  • steadily_auth handles Sign in with Google (Credential Manager) and Sign in with Apple; it predates the event, but its iOS half was first built and shipped during it.
  • steadily_display keeps the screen awake, dims it on schedule for wall use, and gained Save-to-Photos during the event.

Monetization runs through RevenueCat end to end: entitlement pro, one offering with monthly / annual / lifetime packages, App Store + Google Play products, RevenueCat Web Billing with Stripe for people who install off-store (Linux, Windows, sideloaded Android), and a webhook into my backend so a purchase on any platform unlocks Pro on every signed-in device. Judges can see the whole loop: buy on the web, open the phone, Pro is there.

Release engineering is automated: a GitHub Actions pipeline builds the pck/web bundles, uploads to TestFlight from a self-hosted Mac runner, pushes to Play internal, and ships an in-app updater so wall displays that nobody touches for weeks still get new versions. Store screenshots are captured deterministically from a seeded demo year so every set diffs cleanly after a UI change.

A lot of the rest has been practical. I use Steadily every day, notice something that annoys me, change it, and live with that version for a while. I got sick and stopped climbing, so I added rest weeks. I kept forgetting to open habit apps, so I made one you never close.

Challenges I ran into

  • Making a habit tracker that doesn't eventually become annoying. It's easy to make something that feels motivating for a week. Notifications, streaks, badges, and rewards all get attention for a while. The harder question was: will I still want this thing sitting next to me six months from now? That led to a lot of figuring out what not to add, and to treating failure gently. If I got sick, I didn't need an app telling me I'd failed. I needed something that would still be there when I felt better.
  • Godot is a game engine, not a mobile-app framework. A community plugin covers RevenueCat purchases in Godot. Nothing covers RevenueCat Ads: the rewarded-ad flow where RevenueCat's AdMob adapter owns the ad load, ties the impression to the customer, and grants the reward as a server-verified entitlement. RevenueCat ships that adapter for native iOS and Android only; Flutter, React Native, Unity and KMP get a manual token-and-poll path, and Godot is not on the list at all. So I wrote my own bridge for both platforms, Kotlin on Android and Objective-C++ plus Swift on iOS, mirrored method for method: RevenueCat purchases, the AdMob adapter with reward verification (reached on iOS through an experimental SPI), Google's consent SDK, and three boot-time probes that tell a Fire tablet or a Galaxy Store install to buy on the web instead of through Play Billing. About a thousand lines of native code for one plugin. One Godot v2 Android quirk cost a full day: a plugin whose AAR manifest lacks a specific <meta-data> entry is silently never instantiated. No error, no log line, the singleton just is not there. Found it by reading the plugin registry's logcat output line by line.
  • The iOS toolchain. Godot 4.7's iOS template needs the iOS 26 SDK; Xcode 16 fails at link. App Store upload rejected a minimum OS of 14.0 even though the engine supports it.
  • Cheap tablets are the whole point, and they are slow. The target device is the tablet in your drawer, not the one in the Apple keynote. My test bed is a Fire HD 10 with a Mali-G72 GPU that never gets above 30 fps with a full-screen watercolor. The first build hitched 234 ms on every tap: the eMMC's fsync alone cost 100 to 250 ms and it was running in the input frame, and the year re-ink was doing about 1,800 date computations synchronously. Fixes: an async, last-write-wins file writer on a worker thread, the year rebuild moved off the main thread with a generation ticket to discard stale results, pure-integer calendar math instead of engine time calls, and memoized shader uniforms so an untouched cell writes nothing. Worst tap frame is now 50 ms on that device, measured with SurfaceFlinger, not guessed.
  • Cheap tablets also lack Google. Fire tablets and de-Googled Android have no Play Services and no Play Store, so the app probes for both at boot and degrades cleanly: Google sign-in disappears, the paywall routes to web checkout through Stripe instead of Play Billing, and rewarded ads are hidden rather than erroring. I even patched the engine template in a side build so the app boots on Android 6 hardware.
  • Living on a wall is different from living in a pocket. A display nobody touches never phones home, so my "you have stopped syncing" emails fired at people who were fine. Fixed with a heartbeat. The in-app updater assumed a device that is awake at 3 am; a tablet that sleeps at night never fired it. Screens also need to dim at 2 am and wake gently, or you blind yourself getting a glass of water.
  • Pricing for a product that is mostly free. The tracker has to work fully without paying, or it stops being a calm thing on the wall. Pro is sold on convenience (sync, sharing, past years), not on withholding the core loop.

Accomplishments that I'm proud of

  • I've actually kept using it. I've lived with Steadily for about a year, which for a habit tracker feels like a meaningful test. It isn't something I built for a weekend and forgot. It's become part of my environment.
  • It runs on the hardware people already own. Steadily runs at a reasonable speed on a Raspberry Pi and on the old tablets I wanted to rescue from drawers, instead of asking anyone to buy another $300 screen. Getting there meant rebuilding the whole client in Godot.
  • It looks like a painting, not a dashboard. It's finally at the point where I don't feel like I need to hide it when someone comes over. For something that sits out in your home all day, that matters.
  • Shipped. App Store and Google Play inside the window, with a web build, a Mac build, and Linux/Windows/Pi binaries from the same tree. One purchase stack across five platforms, including web-to-app via Stripe, all reconciled through RevenueCat.
  • Since Shipaton opened on August 1: 203 commits across the three repos (123 in the Godot app, 60 on the website, 20 on the backend), 22 tagged releases from 0.9.0 through 0.9.21, ~400 unit tests on the Godot side and ~100 on the backend, all green at every release.

What I learned

More reminders do not mean better reminders. For me, notifications eventually become wallpaper. Steadily works better because it doesn't ask for my attention. It just stays there and lets me notice it.

The unit of a habit matters more than the interface. Some habits really are daily, and Steadily handles those fine. But a lot of the ones people struggle with most, exercise above all, are not, and forcing them into a daily box is why so many trackers get abandoned. "Three times this week" is something a real person can do in a real week.

Consistency isn't perfection. If I'm sick for two weeks, that doesn't mean climbing stopped being a habit I care about. One bad day doesn't mean much. A few months of behavior tells a far more useful story, and I'd rather have something that helps me notice the pattern and adjust than something that tells me I broke a streak.

The best hardware is often the hardware you already own. An old tablet doesn't have to become e-waste because it struggles with newer apps. An app that lives on a wall has to be designed like furniture: quiet, pretty, and tolerant of being ignored for days.

And on the business side: RevenueCat made cross-platform entitlements almost boring, which is the highest compliment I can give infrastructure.

What's next for Steadily

Users. The feature set is in good shape; what I don't have is data from people who aren't my friends and family. It has worked for me for over a year, and I need to find out whether it works for strangers. So the next stretch is marketing and advertising, not code: launch posts, store listing experiments, paid acquisition where the numbers justify it, and actually reading what real users do with it. Learning from that will decide what gets built next.

The goal is a sustainable business without turning Steadily into the kind of app I was trying to get away from, packed with engagement tricks and notifications. Pro sells convenience around a free tracker, and I want to keep it that way.

Smaller items on the list: a Samsung Galaxy Store listing (waiting on Samsung's commercial seller verification), and shared households, where one wall display holds several people's habits.

Built With

Share this project:

Updates

Submission history