Developing a Web App to Generate Private yet Shareable Diffs
I built a web app to generate private yet shareable diffs without backend.
I built a web app to generate private, shareable diffs by storing the diff in the URL.
Every now and then I need to do a quick diff —compare two pieces of text/code for differences— outside a Git environment.
There are websites where you can paste the two texts, and you get a diff. Are the texts being sent to a server, though? I don’t like that.
To avoid using these sites, I use a snippet to start vim in diff mode with a bash alias. It (mostly) works, but I often want to share the diff. A screenshot is not convenient for the receiver; they can’t copy or edit it.
Recently I learnt browsers can handle big URLs. At a minimum, they’re supposed to support 8,000 characters. In practice:
- Firefox: configurable default of 1MB, ~1 million characters
- Safari: no limit (there may be a 2GB (~2 billion characters) limit on iOS)
- Chrome: up to 2MB, ~2 million characters
After seeing a notes app that stores everything on the URL (HN discussion), I came up with the idea for a web app that generates diffs locally with the ability to share them by encoding everything in the URL.
I used Claude and Gemini to code it. I didn’t want any dependencies: I trusted vanilla JavaScript, HTML, and CSS would be enough.
The main trick was to store the entire two texts, compressed with deflate-raw (see spec), in the URL. That and having an algorithm to generate the diff.
Diff algorithms
It wasn’t my goal, but I ended up learning about diff algorithms.
For a snippet like this:
If you change the order so as to define start_server() first, would you expect this diff?
or this?
The first is the result of the Myers diff algorithm, by Eugene W. Myers. The second diff uses Patience Diff, by Bram Cohen. Myers tries to find the smallest set of line removals/additions that changes one file into another. Patience diff tries to make diffs more human-readable by avoiding confusing matches.
Less is less
I tried to get even shorter URLs than deflate-raw was providing when storing full “Original” and “Modified” inputs. Deduplicating common lines helped, but it gave me less than a 5% decrease in URL length. I dropped this change in favour of simplicity.
Cool stuff
LLMs + me = feature creep. That said, I’m glad I added:
The mini-map
Inspired by VSCode. Useful for navigating through the diff. At first I had buttons to jump to next/previous change; this is way more convenient.
More
- It uses service workers to avoid locking the browser on large diffs.
- Can be installed as PWA for full offline functionality.
- Keyboard shortcuts for faster input/editing, and vim-like navigation (move up/down the diff with j and k).
- Includes “Ignore” options for whitespace differences and quote styles.
- It’s not necessary to build the app, but I use
nodeto run tests on the code. The tests verify the patches generated by the tool are (1) valid and when applied to the original text (2) generate the expected result.
I was worried about the practicality of long URLs. I tested them in Slack, and noticed they get visually truncated, but the content is preserved. Plus, you can always link a specific word instead of pasting the raw URL.
If you want to play around with it, visit diff.osc.garden. The source code is on GitHub. I named it kawari for “change” in Japanese.
Extra: more URL-abusing projects
A few cool projects storing data in the URL, from the Hacker News discussion: