Build log2026-09-014 min readMuhammad Ahmad

Building Commit Canvas: from git log to story

The idea was simple. The audit was not. This is the story of v1, the bugs it was quietly hiding, and the rebuild that turned a neat script into a real product.

Contents

    The idea

    Commit Canvas started the way most tools do: I ran git log on a project I'd spent years on, scrolled the wall of subjects, and thought — this is a story, rendered in the worst possible format. Timestamps, gaps, bursts, the 2am fixes, the release weeks: all there, all invisible.

    v1 was a weekend-shaped idea: a Python CLI that turns a repository into one cinematic HTML page. A title reveal, a timeline, an activity ring, contributor medals, a certificate at the bottom. Self-contained output, no server, no account. It worked, and it made people smile at their own history — which is the whole product, honestly.

    The audit that hurt

    Before building v2, I audited v1 the way I'd audit someone else's code. Findings, in the order they stung:

    None of these were exotic. They were the difference between "demo" and "tool" — and every one of them was fixable.

    The rebuild

    v2's architecture came from one constraint: every claim in the story must be traceable to a measurement. Everything else followed.

    One pass, not many

    The whole history is read in a single git log --all --numstat --summary call, with fields separated by an ASCII unit separator (\x1f) that will never appear in a message. Merges are kept and counted. File creation and deletion come from summary-mode lines — the authoritative source — instead of guessing from numstat, which can't distinguish "file deleted" from "file emptied." A 30,000-commit synthetic repository analyzes in about two seconds.

    Deterministic story engines

    Chapters (Beginning, Sprint, Silence, Grind, Launch, Evolution), the shape (Marathon, Climb, Return…), the fingerprint archetypes, and the roast are all plain rules over plain numbers. No AI anywhere in the core path — not because language models can't write, but because a story about your data shouldn't have an imagination in the loop. If the evidence is thin, the tool says the story is just starting.

    One renderer, two doors

    The output is a static shell — inline CSS, inline JS, a JSON model baked in — that renders entirely client-side. The CLI injects data into that shell; the web version fetches the same shell in the browser and analyzes public repos through the GitHub API with the same narrative rules ported to JavaScript. Same story from either door.

    Exports that aren't buttons

    Every export is real: PNG cards for socials, an animated GIF of the timeline (a GIF89a encoder written in vanilla JS, because I refused to ship a fake download button), WebM video via the browser's own recorder, SVG timelines, a README block, and the full self-contained HTML. If a browser can't do something, the button says so instead of pretending.

    What I learned

    1. Audit your own claims first. The most embarrassing bug wasn't in the code — it was in the README.
    2. Deterministic is a feature. The same repository must produce the same story, forever. That's what makes it evidence rather than content.
    3. The emotion is in the recognition. People don't share Commit Canvas because the charts are pretty. They share it because it says something true about them that they'd never articulated.
    4. Offline-first is a product decision. "Your code never leaves your machine" isn't a policy page — it's an architecture. No backend, no database, nothing to leak.

    Run it yourself

    git clone https://github.com/ahmadrrrtx/commit-canvas
    cd commit-canvas
    ./run.sh /path/to/your-project
    # → story.html — open it. scroll slowly.

    And if you find a repository whose story comes out wrong — tell me. The history doesn't lie, but the parser might.