Charlie KrugThe Build Log

← All posts

A line diff paints the whole line. This marks the one token.

Diff Poster turns a before/after snippet into a clean shareable image, diffing at the token level so a renamed variable reads clearly instead of the whole line going red.

Rewrite return 'hi ' + name; as return `hello, ${name}!`; and a normal diff has one thing to say: this line changed. The whole thing goes red, a copy goes green underneath, and the reader has to eyeball two lines to find the actual edit. But the edit is small. You swapped a concatenation for a template literal. That's the interesting part, and a line diff buries it.

Diff Poster is a tool for sharing exactly that kind of change: paste the old code, paste the new code, get a single clean image of just the diff, sized for a tweet or a Slack message. No login, no export dialog, nothing uploaded. It exists because the two normal options both fight you. A raw editor screenshot drags in gutters, minimaps, line numbers, and whatever window chrome was open. A tool like Carbon or Snappify is gorgeous but built for a bigger job, themes and gradients and multi-slide decks, when all you wanted was to quote-tweet one function.

Diffing at the token, not the line

The whole product rides on one decision: the diff runs over tokens, not lines. Instead of comparing the before and after as two lists of strings, it first tokenizes each side into the actual lexical pieces, identifiers, operators, string literals, comments kept atomic, and then diffs those two streams. So in the example above it marks 'hi ' + name as removed and `hello, ${name}!` as added, and leaves return and the semicolon untouched, because they survived the edit. The reader sees the substitution, not "somewhere on this line, something."

The diff itself is a textbook longest-common-subsequence pass over the token streams, the same algorithm behind git diff and diff(1). LCS finds the longest run of tokens that appears in both versions in order; anything not on that shared subsequence is what got removed or added. It's simple, well understood, and plenty fast for a function-sized snippet. It is deliberately not built for diffing whole files: a wholesale rewrite, a for-loop turned into a reduce, interleaves so many removed and added tokens that the inline view gets noisy, exactly the way GitHub's word-diff does on a big hunk. The tool is tuned for the localized change, which is also the change most worth sharing.

Why it draws to a canvas

The output is not styled HTML that gets screenshotted. It's rendered directly onto a <canvas>: the window chrome, the line numbers, the syntax coloring, and the red/green diff tint are all drawn by hand, pixel by pixel. The lazy path would have been to build a nice DOM diff view and rasterize it with something like html2canvas, but DOM-to-image libraries are notorious for font-metrics and layout drift, the exported PNG never quite matches what you saw. Drawing to canvas trades that convenience for exact control: the image is a fixed 1200×675, the 16:9 frame Twitter, Bluesky, and Slack all crop link previews to, rendered at your screen's pixel density so it stays crisp on a retina display.

Everything runs client-side. Tokenizing, diffing, laying out the rows, coloring, and exporting the PNG all happen in your browser, which is why there's no account, no backend, and no question about where your snippet went. It's a static Vite app, no framework, a handful of pure functions chained end to end: tokenize to diffTokens to the row-fitting math to classifyToken to the canvas renderer to a PNG blob. The whole thing is on GitHub if you'd rather read it than trust it.

One honest limitation worth naming: copy-to-clipboard for images isn't universal. Where the browser can write a PNG to the clipboard, Copy does it in one click; where it can't, Copy is clearly disabled and Download always works. The downloaded file is named diff-poster-YYYYMMDD-HHMMSS.png so a folder of them sorts itself by time.

Try it

Open Diff Poster, drop your original code in the Before pane and the changed version in After, pick the language for syntax coloring (JavaScript, Python, or plain text), and hit Generate image. Then Copy or Download, no dialog either way, and paste it wherever you were going to describe the change in words.

Diff Poster is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…