I've always thought git's biggest problem isn't complexity — it's invisibility. You run git merge, git rebase, git cherry-pick, and the only feedback you get is text. The branch structure lives entirely in your head. For simple repos that's fine. But the moment you have four feature branches, a hotfix, and a release branch, your mental model starts lying to you.

I wanted a whiteboard for git. Something where I could draw branches, add commits, merge them visually, and actually see the graph take shape. Not a static diagram tool. An interactive playground where the branch topology updates live as you perform operations. So I built git-floww.

How It Works

The entire thing runs in the browser. No backend, no server, no database. The core is an SVG canvas powered by D3.js that renders the commit graph. Each commit is a node, each branch relationship is a line, and everything is positioned by a layout algorithm that figures out where nodes should sit so the graph stays readable.

State management is Zustand — about 2kb gzipped. It models the git state as a directed acyclic graph: commits, branches, HEAD pointer, stash stack. Every operation you perform mutates this graph, and D3 re-renders the SVG to reflect the new state. The result is that performing a merge or rebase looks like what it actually does — you see branches converge, commits get replayed, new merge commits appear.

D3 also handles zoom and pan on the canvas. You can scroll to zoom, click and drag to pan around. When your graph gets big enough that it overflows the viewport, you just navigate it like a map. The whole app is built with React 19, TypeScript, and Vite, coming in at about 87kb gzipped total. No heavy UI framework, no component library.

The Git Operations

I didn't want to build a toy that only does branch and commit. The whole point was to cover the operations that are hardest to reason about visually. Here's what you can do:

  • Branch and commit — create branches from any commit, add commits to any branch
  • Merge — merge one branch into another with a visible merge commit connecting both parents
  • Rebase — watch commits get replayed onto a new base, the graph restructures in front of you
  • Cherry-pick — pick a specific commit from one branch and apply it to another
  • Stash and pop — stash work-in-progress commits and restore them later
  • Reset — move a branch pointer back to a previous commit

There's also conflict detection. When you merge two branches that have diverged significantly, the merge commit renders with an animated warning ring around it. It doesn't resolve conflicts — this isn't a real git engine — but it visually signals that this merge point is where things got complicated. It's the kind of thing that makes the diagram useful for team discussions about branching strategy.

Every operation supports undo and redo. There's a full history stack, so you can experiment freely. Try a rebase, don't like how it looks, undo it, try a merge instead. It's a sandbox.

Dark Theme Done Right

I'm tired of dark themes that are just "make the background #1a1a1a and call it a day." For git-floww I went with a deep indigo dark theme — rich blues and purples instead of flat gray. Branch lines get distinct colors so you can trace them visually. Commit nodes have subtle shadows. The light theme is a clean slate with high contrast, not washed out.

There's a toggle to switch between them, and it respects your system preference on first load. Both themes were designed so the graph stays readable at any zoom level.

When you're done with your diagram, you can export it as PNG or SVG using html-to-image. Drop it in a PR description, paste it in a Confluence page, put it in a slide deck. The SVG export is crisp at any resolution.

What I Learned

D3's zoom behavior is powerful but opinionated. Getting zoom and pan to feel natural on an SVG canvas took more iteration than I expected. The default behavior fights you if you're also handling click events on nodes inside the same SVG. I ended up separating the zoom layer from the interactive elements so they don't conflict. Once that was sorted, it felt like navigating a real map.

Modeling git as a graph is surprisingly clean. Commits are nodes, parent relationships are edges, branches are just named pointers. Once you have that data structure, every git operation becomes a graph transformation. Merge adds a node with two parents. Rebase detaches a subtree and re-attaches it. Cherry-pick copies a node. Reset moves a pointer. The mental model maps perfectly to the visual output.

Keeping the bundle small requires discipline. It would have been easy to reach for a component library, a CSS framework, an animation library. But every dependency adds weight. Zustand instead of Redux. Raw SVG instead of a charting library on top of D3. No Tailwind — just scoped CSS. The result is 87kb gzipped for the entire app. That's smaller than most hero images.

Layout algorithms are a rabbit hole. Positioning nodes in a DAG so the graph looks clean — no overlapping, no crossing lines, consistent spacing — is a well-studied problem but not a solved-in-five-minutes problem. I went through several iterations before landing on a layout that handles merges and rebases without turning into spaghetti.

What's Next

I'd like to add shareable URLs — encode the graph state in the URL so you can send someone a link to a specific branch topology. Useful for code reviews where you want to say "here's what I'm planning to do with these branches." I'm also thinking about animation for operations like rebase, where commits visually slide from one position to another instead of snapping.

The project is open source and runs entirely in the browser. If you've ever tried to explain a branching strategy on a whiteboard and wished the whiteboard understood git, give it a try.

Try it live or check out the source on GitHub.