Building a Basic Meditation Log

I spent about six hours last month building a simple web app to track my sit times. The goal was straightforward — log when I meditated, how long it lasted, and what my headspace was like afterward. I figured I could use it for maybe three months and then forget about it. Here is how the whole thing worked out. I went with a static site that used localStorage for data persistence. No backend, no database, no authentication. Just an HTML file, a CSS sheet, and a JavaScript file that kept everything in the browser. The logic was brutal: a button that started a timer, a second button that stopped it and wrote the result to a JSON object stored under a date key, and a table at the bottom that read from that same object every time the page loaded. That was the entire first version. Took about forty-five minutes to put together. The trick most people miss is that localStorage has a soft limit around five megabytes, and your data grows with every session. If you log a long form entry with mood notes or session text, you can fill that space faster than you would think. I hit roughly two hundred entries before I started seeing slow downs on page load because the browser was deserializing a bloated JSON blob on every refresh. The workaround was simple — I switched to storing only the timestamp and duration, keeping the mood notes in a separate object that I wrote on a weekly batch instead of per-session. Page load dropped from about 800 milliseconds down to under 200.

What Actually Happened After a Few Weeks

The interface felt fine for about ten days. Then I realized I had built a tool that required me to open a browser, click a button, start a timer, meditate for twenty minutes, come back, click stop, and write a note. That seventh step — writing the note — was the one where most sessions died. I stopped logging on Wednesdays and Thursdays routinely because I would forget until Friday, and by then I had already decided not to bother going back and entering old data. It felt pointless. The fix was adding a quick-input mode. A single click opened a modal with just a duration field and a five-star mood scale. No notes required. That cut the post-session overhead from probably thirty seconds down to about eight. My consistency jumped noticeably after that change.

Going Beyond the Simple Version

If you want actual stats — averages, streaks, monthly heatmaps — you need a aggregation layer. I built a functions file that ran on page load and computed rolling seven-day averages, longest current streak, and total cumulative minutes. The math itself was unremarkable. The gotcha was that once you start computing aggregates from raw logs, you introduce a new failure mode: stale data. If you ever manually edit a past entry in localStorage without re-running the aggregation, your dashboard numbers become wrong. I did this accidentally once, changed a Tuesday entry from twenty minutes to ten, and my weekly average stayed at twenty-two minutes for the rest of the week until I manually triggered a rebuild. You can prevent this by wrapping every write operation in a function that updates the log and immediately recalculates the summary stats in one atomic step. That keeps everything in sync without you having to remember. Another thing nobody mentions is timezone handling. If you travel even a little, dates stored as plain strings like "2025-07-14" become ambiguous when someone views the data on a different machine in a different region. I stopped using naive date strings and started using ISO 8601 timestamps with the timezone offset included. It adds four characters to each stored entry. It saved me from a genuine confusion where I thought I had meditated every day in March but had actually logged three days in two separate timezones and the local date view made them look sequential.

Get the Full Details

Meditation Tracker Printable Daily Meditation Journal | Etsy
Meditation Tracker Printable Daily Meditation Journal | Etsy

Downsides and When This Approach Fails Completely

A browser-only tracker has a single point of failure: the browser. Clear your cache and your data goes away. Switch devices and your history stays on the old one. Use an incognito window and you get nothing. This is not a problem for casual tracking if you are disciplined about not clearing cookies, but it is a real risk if you use multiple browsers or devices interchangeably. I lost two weeks of data once because my wife did a disk cleanup on the laptop and the browser data came with it. If you need cross-device sync or automatic backups, you need a backend. That means either hosting a small Node server with a SQLite file, or pushing to a cloud service like Supabase or Firebase. I looked at Supabase and ended up not using it because the free tier was plenty for basic storage, but adding authentication, an API, and a deployment pipeline turned my forty-five-minute project into roughly a day and a half of work. The decision is basically whether you care about data portability more than you care about not spending the afternoon on infrastructure. For most people who just want to see whether they are hitting their daily target, the localStorage version is adequate. It is not robust. It is not portable. It will break if you wipe your browser data. But it is fast to build, easy to modify, and good enough to catch the habit before you move on to something more permanent.

A Note on Exporting Your Data

I added a CSV export button to mine because I eventually wanted to move the data somewhere else. Exporting is trivial with a few lines of JavaScript that stringify the log object and create a downloadable Blob. The important part is doing it regularly. I set a reminder in my calendar to export every Sunday evening, and it took about fifteen seconds to run. That single habit meant that even after the data loss incident, I still had a week-old backup to recover from. The export is the only thing that makes this kind of tracker worth the effort in the long run. If you want the actual source, I put it on GitHub under a free MIT license. The repo is called diy-meditation-tracker and the files are organized as index.html, styles.css, and app.js. There is a README with setup notes and a note about the localStorage schema I settled on after the second rewrite.