Building a Zero-Latency, 13MB WakaTime Alternative in Go

As a developer, I love seeing a breakdown of my most-used languages, active projects, and coding streak which was motivating the next day to continue. So to track I was using WakaTime which was known for it mainly I used it because it had that one feature that you could flex on your GitHub readme.md in your profile page have clocked 5–8 hours daily and see it was so satisfying. But the problem starts here you can only track a week worth of work as more that that is locked behind a payment gateway so you really can’t see your stats of 1 month , 1 year or any accumulated time that you have coded.

(for bg blur i used same bg as github and i use zen as my browser with blur extension)
Why Existing Alternatives Weren’t Enough
Some of you might say “There are already open-source alternatives like Wakapi or ActivityWatch.” It is a completely fair point. In fact, the developer community’s desire to escape SaaS lock-in is growing so rapidly that if you search for WakaTime alternatives today, Google’s AI Overview explicitly groups TakaTime right alongside these established legacy projects. While Wakapi is a fantastic drop-in replacement, it comes with a massive architectural bottleneck: infrastructure overhead. It requires you to maintain a dedicated backend server, while still forcing your editor to run WakaTime’s heavy, Python-based CLI continuously in the background. ActivityWatch similarly requires a persistent local server running 24/7. The goal wasn’t just to clone WakaTime; it was to rethink the tracking engine from the ground up. The ecosystem needed a tracker that was highly customizable and lightweight — an offline-first engine that completely removed the burden of server maintenance and background daemons.
Resource Constraints and some decisions (still works well)
Since I often train machine learning models locally on my laptop, system RAM is extremely precious. Resource management is crucial for me. Because of that, my first architectural change was deciding I didn’t want a background service running all the time. Instead, I built it as an on-demand binary that only loads into memory and executes under specific editor conditions.
With system resources being the priority, I needed to build this with a lightweight, compiled language. Rust, Go, and Zig are the heavyweights in this space right now. I went with Go. I was already fluent in it, and its ability to compile down to a tiny, standalone executable without needing a heavy runtime was exactly what the project required (from my pov).
Why MongoDB? (The Path of Least Resistance)
The final hurdle was the database. I wanted to completely bypass the payment gateway and store my history forever, for free. My first thought was to just store it locally. But if it’s only local, how do you sync it to your GitHub README (the ultimate programmer flex)? I needed a free cloud database.
I initially looked into SQL options like Supabase and PlanetScale, but I quickly backed away. Setting up PostgreSQL or MySQL requires managing roles, permissions, and strict schema configurations.
I went with MongoDB for three very specific, pragmatic reasons:
- It’s just a logging app: At the end of the day, this project is essentially a time-series logger. A document-based NoSQL database is perfect for just dumping JSON heartbeats.
- The schema is evolving: TakaTime is constantly growing. I didn’t want the headache of running SQL migrations or strictly redefining columns every time I decided to track a new metric.
- Atlas is stupid simple: With MongoDB Atlas, it literally takes 3 to 5 clicks to spin up a free cluster and get a connection URI. No complex permission layers. Just grab the string and go.
Plus, if I am being completely honest? I already had MongoDB installed on my local machine. I didn’t want the hassle of installing and learning a completely new local DB ecosystem just for a side project.
The architecture finally took shape: The editor sends a heartbeat -> The Go CLI spins up -> It caches to local SQLite and syncs to MongoDB -> GitHub Actions pulls the data -> Free stats automatically update on your README. I was finally building a tool for myself so that I could own my data and flex my stats.
So my Plan was to create de-coupled architure one to upload all the logs to an DB and another for GitHub Actions to run my code there and generate the md code and update the README.md. So name the uploader taka-upload and actions code as taka-report . I know I just changed wakatime’s W to T that’s all a new name TAKATIME (I am bad at naming ...).
The Editor Integration: Starting with Neovim
I first built it for Neovim. To all the crazy people out there using nvim or vim: hats off. The way you remember the shortcuts, man… it’s literally so keyboard-friendly. I’ve been rocking Neovim for the past 10 months, coming from full-time VS Code. It took a lot of customization, but I am fully on the keyboard now and way less dependent on the mouse.
But back to the binary, right? (Sorry, too much, I got distracted…)
So, in Neovim, we can use lazy.nvim to install it. All it takes is the Git URL and a plugin file. An really easy thing was that I had already learned Lua while learning to customize my Neovim config.
Neovim gives you some great features, like the fact that we can easily build off of it using commands that run on specific actions. Things like entering a new line, adding a buffer autocmd events (BufEnter, BufWritePost), or saving with :w and :wa. So, I thought of searching the official docs to find the exact event hooks I needed.
I swear to God, they have the worst documentation for a beginner to follow.
I could not follow up with the docs at all, so I went to AI. Easy as it gets, it gave me the base code for it. But I want to be clear: I still had to heavily refactor it myself to make sure it triggered my Go binary correctly in the background!
Lazy Vim setup
return {
"Rtarun3606k/TakaTime",
lazy = false,
config = function()
-- Optional: Enable debug mode if you run into issues
require("taka-time").setup({
debug = false
})
end,
}
Tackling the Big Boss: The VS Code Extension
With Neovim out of the way, I had to tackle the big boss: VS Code. Let’s be real, the vast majority of developers are rocking VS Code, so if TakaTime was actually going to be a true WakaTime alternative, it needed a native extension here.
Moving from Lua back to JavaScript felt a bit like coming home, but the VS Code Extension API is a massive beast.
Just like Neovim gives you autocmd events, VS Code gives you a ton of event listeners that trigger on specific actions. I needed the extension to wake up when you change tabs, type a character, or hit save. So, I went digging for the VS Code equivalents, looking for event hooks like vscode.window.onDidChangeActiveTextEditor, vscode.workspace.onDidChangeTextDocument, and vscode.workspace.onDidSaveTextDocument.
I will say this: Microsoft’s documentation is a million times better than Neovim’s. You can actually read it without getting a headache. But honestly? I still used AI to scaffold the initial boilerplate for the extension. Why waste time writing setup code when I could focus on the architecture?
The real challenge here wasn’t the event listeners, it was how Node.js handles background tasks.
To keep my “no-daemon” architecture alive, the JavaScript extension had to use Node’s child_process module to fire off the taka-upload Go binary. I had to use execFile to run it completely asynchronously. If I messed this up and ran the binary synchronously on the main thread, the entire VS Code window would freeze up and stutter every single time someone typed a semicolon.
I had to carefully refactor the JavaScript logic so it just spawns the Go binary in the background, hands it the file data, and immediately forgets about it, letting the binary do its DB syncing and die on its own. Zero lag, zero port collisions
VsCode Market Place Link :
https://marketplace.visualstudio.com/items?itemName=Rtarun3606k.takatime

Edge Cases: Tmux, Timeouts, and Time-Travel Bugs
With the editor sending data to the Go binary, everything seemed great — until I hit the real-world edge cases.
Edge Case 1: The Idle Problem If I get up and walk away from my laptop for an hour, will the tracker consider that a full hour of coding just because the file is open? To fix this, I implemented a global timeout in the editor plugins. If there are no keystrokes or active buffer movements for exactly 2 minutes, the plugin simply stops sending heartbeats to the binary. Idle time solved.
Edge Case 2: The Concurrency Bug This was the tricky one. I am a massive fan of tmux (it makes terminal window management incredibly easy—if you live in the CLI, I highly recommend trying it out). Because of my workflow, I almost always have two or three Neovim windows open in different split panes at the exact same time.
If I am jumping rapidly between two files in two different tmux windows, both editor instances are firing off the Go binary. So, does 1 hour of real human time get logged as 2 hours of bloated coding time? Yes. In fact, this was the biggest bug in my very first release! I couldn't be flexing inflated, overlapping stats on my README. The time tracking had to be atomic; every second logged had to be completely unique.
To solve this, I had to write a “smart query” time-merging function. Before any stats are displayed or pushed to the README, all the raw database heartbeats pass through this function. It looks at the start and end times of every single heartbeat and merges any overlapping blocks.
I am not going to lie, calculating date and time overlaps across asynchronous, concurrent events is a headache, so I used AI to help me scaffold the core math. The result is a rock-solid query that guarantees that no matter how many tmux panes or VS Code windows I have open simultaneously, 60 minutes of real time will only ever equal a maximum of 60 minutes of logged code.
Automating the Profile README
Now for the final piece of the puzzle: the reporter (taka-report). Tracking the data locally and syncing it to MongoDB is great, but the whole point of this project was to flex those coding hours on my GitHub Profile README.
The pipeline for this binary is straightforward: query the MongoDB database -> parse the raw heartbeats -> calculate the total durations -> format the results into Markdown -> update the README.
But how do you dynamically update a markdown file via an API without accidentally deleting the rest of the user’s carefully crafted profile?
I borrowed a brilliant strategy from the WakaTime ecosystem: hidden HTML comment markers. I just ask users to drop these two tags anywhere in their README:
To handle the actual file manipulation, I used the officially maintained google/go-github library. The Go binary connects to the API, targets the user's profile repository (e.g., tarun/tarun), scans the file for those exact markers, and safely injects the newly generated Markdown right between them. Everything else in the file stays completely untouched.
The absolute best part about this? Because taka-report is designed to be executed via a GitHub Actions workflow, the authentication is entirely seamless. GitHub Actions automatically provides a default GITHUB_TOKEN in the environment. Users don't have to manually generate Personal Access Tokens, manage secrets, or worry about security leaks. The binary just grabs the default token, authenticates, and pushes the commit.
It is a zero-configuration setup. You just drop the workflow file into your repo, and your stats update automatically every night.
WorkFlow File
add MONGO_URI to your GhitHub Secrets
name: Update TakaTime Stats
on:
schedule:
- cron: "0 0 * * *" # Runs every midnight UTC
workflow_dispatch: # Allows manual trigger
jobs:
update-readme:
runs-on: ubuntu-latest
permissions:
contents: write # Needed to download releases
steps:
- name: Download Taka-Report Binary
env:
GH_TOKEN: ${{ github.token }}
run: |
# Downloads the latest stable binary
gh release download --repo Rtarun3606k/TakaTime --pattern "taka-report-linux-amd64" --output taka-report
chmod +x taka-report
- name: Generate Report & Update Profile
env:
MONGO_URI: ${{ secrets.MONGO_URI }}
GIST_TOKEN: ${{ github.token }}
TARGET_REPO: ${{ github.repository }}
run: ./taka-report -days=7
Result
<!--takatime-start-->
<h2 align="center">TakaTime Weekly Report</h2>
<p align="center">
<img src="./public/taka-time.png" width="100%" alt="Time Stats" /><br/>
<img src="./public/taka-languages30.png" width="400" alt="Languages" />
<img src="./public/taka-projects30.png" width="400" alt="Projects" /><br/>
<img src="./public/taka-languages.png" width="400" alt="Languages" />
<img src="./public/taka-projects.png" width="400" alt="Projects" /><br/>
<img src="./public/taka-tech.png" width="100%" alt="Tech Stack" />
</p>
<p align="center"><em>Generated automatically by <a href="https://github.com/Rtarun3606k/TakaTime">TakaTime</a></em></p>
<!--takatime-end-->
The Local TUI: Bringing the Data Home (taka-dashboard)
The GitHub README automation was working flawlessly, but a new piece of feedback rolled in: users wanted to see their data locally. Waiting 24 hours for a GitHub Action cron job to run just to see your daily stats can be annoying.
So, I built another binary: taka-dashboard.
Because I had already written all the data-fetching and calculation logic for the reporter, I was able to just reuse the backend code and slap a Terminal User Interface (TUI) right on top of it. I built it using Go’s incredibly popular bubbletea framework. Now, you can just pop open your terminal, type a command, and instantly see your daily metrics, coding heatmaps, and a bunch of other deep-dive stats (you'll have to try it out to see them all!). No more waiting for the 24-hour sync.
The biggest challenge here was the responsive design. If you have ever built a CLI tool, you know that handling different terminal widths and aspect ratios is an absolute nightmare. To fix this, I brought in lipgloss (which is essentially CSS for the terminal). It allowed me to lock in the layouts and flexboxes so the dashboard doesn't explode when you resize your terminal window. There are still a few minor design inconsistencies on really weird screen sizes, but I will definitely be improving that in future updates.

Built for the “Ricers”
One of my absolute non-negotiable requirements for this project was customization. I use Linux, and I rice my distro. I configure my system, wipe it all, and reconfigure it from scratch — that is just my way of enjoying my setup.
So, I built both the GitHub display cards and the TUI dashboard with extreme theming and color customization in mind. I wanted it to seamlessly blend into whatever terminal theme you are already running. You can apply pre-built themes or inject your own custom hex colors directly via the CLI arguments.
Here is exactly how easy it is to apply a custom theme. Let’s say you want to use the popular “Tokyo Night” color palette for your dashboard:
./taka-report -theme nord
./taka-report -days=7 -bg "#0d1117" -text "#00FF00" -subtext "#008800" -bar-bg "#111111" -c1 "#00FF00" -c2 "#00DD00" -c3 "#00AA00" -c4 "#005500"

But let’s be real, tweaking hex codes in the CLI can be tedious. So, I also built a static web generator hosted on GitHub Pages. It acts as an interactive theme builder where you can visually select your colors, preview your stat cards in real-time, and instantly copy the generated Markdown snippet for your README.
Performance & Benchmarks
It is easy to claim “zero-latency” and “lightweight,” but what does that actually look like on a system level? I ran some standard profiling on the taka-upload binary to see exactly what happens when Neovim or VS Code triggers a heartbeat.
Because the binary executes, connects to SQLite, syncs to MongoDB, and dies instantly, the footprint is practically a ghost.
- Execution Time: ~10ms to 25ms. It is so fast that the editor’s main thread doesn’t even notice it happened. You can mash your keyboard, and there is zero UI stutter.
- Peak Memory Usage: ~14MB. This is the absolute maximum RAM it consumes during that tiny fraction of a second while it holds the DB drivers in memory.
- Idle Memory Usage: 0 MB. This is the biggest win. Because it is not a daemon, when you aren’t actively typing, TakaTime simply ceases to exist in your system monitor.
- Storage Footprint: The local SQLite cache stays incredibly small (usually just a few megabytes for months of data) because the heavy lifting is synced to the free MongoDB cloud.
Compared to running a persistent Python background script or a local Node.js server 24/7, the resource savings are massive. My laptop’s RAM is finally fully dedicated to my ML models again.
Wrapping It Up: Own Your Data
So that’s the whole journey. What started as me just wanting to flex my coding hours on GitHub without hitting a paywall turned into a full-blown, offline-first telemetry engine.
We went from a bloated SaaS tracker running heavy background scripts to a hyper-optimized 13MB Go binary. It fires completely on-demand, stores your history in your own database forever, and runs silently in the background of Neovim and VS Code. Zero local port collisions, zero idle RAM consumption, and absolutely no subscriptions holding your data hostage.
Standard Go binaries can easily bloat past 20MB because Go statically compiles the entire runtime. To achieve that 13MB footprint and zero-latency execution, I had to aggressively strip the binary during the build process using this exact command:
env GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w -buildid=" -o taka-upload
Stripping the symbol table and DWARF debugging information (-s -w) is what got the fully functional executable—complete with SQLite and MongoDB drivers—down to ~12.9 MB.
I built TakaTime for myself because I wanted a lightweight tracker that fit into my workflow, but it’s completely open-source for anyone else who feels the same way.
The engine is stable, the GitHub README reporter is running in CI/CD pipelines right now, and the local TUI is ready to be customized. I am still actively building this out — the next major boss fight on the roadmap is building a universal JetBrains plugin to bring this to IntelliJ, PyCharm, and GoLand.
If you are tired of your coding history being locked away, or if you just want to generate some sick, customizable stats for your GitHub Profile, give it a try. Drop a star on the repo, test out the dashboard, or open a PR if you want to contribute!
Check out the project here:
- GitHub Repository: https://github.com/Rtarun3606k/TakaTime
- Wiki : https://github.com/Rtarun3606k/TakaTime/wiki
- Interactive Theme Builder : https://rtarun3606k.github.io/TakaTime/
Happy coding, and go flex those stats.
Related Articles
Subscribe to My Newsletter
Stay updated with my latest articles, projects, and exclusive content.
By subscribing, you agree to our Privacy Policy. You can unsubscribe at any time.



-(1).png)



.png)