Projects #

An incomplete list of projects I have been involved in, currently being back-filled.


Shortcuts automation to turn on Tailscale when leaving an address

Tailscale Shortcuts Actions

I implemented Shortcuts support for Tailscale's iOS and macOS apps. In addition to the feature work, this required some app modernization (doing in-app intent handling depends on migrating to the UIScene APIs). This blog post has more details, but all of the interesting work was on the closed source side of Tailscale.

Current status: Alive and well.


Tailscale user switching UI screenshot

Tailscale Fast User Switching

I implemented the API endpoints, Mac UI and related Mac infrastructure for Tailscale's fast user switching. This blog post has more details, and the open source part of the work is attached to this GitHub issue.

Current status: Alive and well.

Infinite Mac emulating System 7.5.3

Infinite Mac

WebAssembly/Emscripten port of classic Mac emulators, along with integration with web APIs to allow fast loading, data import and export, and networking support. Initially launched at and, there is now a full collection of system software versions available at There's a launch blog post with more details, as well as several subsequent ones with follow-up work. Source code (including for the emulator forks) is also available.

Current status: Alive and well.


Quip TypeScript migration documents

Quip TypeScript Migration

I led the efforts of Quip's client infra team (Rafael Weinstein, Shrey Banga and Erik Arvidsson) to modernize our frontend codebase. We started out with a JSDoc-type-annotated JavaScript codebase that used React.createClass, mixins and global scoping and was type checked, bundled and minified with Google's Closure Compiler. Over a series of "flag day" migrations in mid-2019 we gradually transformed it into a TypeScript codebase that used ECMAScript modules and classes and a Vite- and Rollup-based toolchain. There are a couple of entries on Quip's blog with more details: part one describes the process that we chose, and part two has some anecdotes about how it worked out in practice.

Current status: The same infrastructure was still in place when I left Quip in early 2022.


Instagram Downloader screenshot

Instagram Downloader

My wife wanted an easy way to download photos from Instagram, ideally in a less lossy and tedious way than taking screenshots. While there were existing Chrome extensions that seemed to do this, they seemed shady and/or inefficient (as far as which permissions they required). I had not written an extension for a few years, and this was a good exercise in using the new-at-the-time activeTab and declarativeContent APIs to minimize the permissions needed. Source code is available.

Current status: I had to change the name and it's required some upkeep over the years as Instagram's markup has changed, but it still appears to work as of early 2023.


React.createClass component with JSDoc

Closure Compiler support for React

Quip started to heavily adopt React in late 2014, and a few months into it we realized that we were giving up a lot of type safety in the process. We had been heavily reliant on Closure Compiler to do type checking, but React's API at the time (with React.createClass and mixins) did a lot of runtime type generation that the compiler did not know about. I ended up writing a custom compiler pass that extracted component type definitions and applied them at creation time. This blog post has more details, and source code is available.

Current status: in addition to track React releases, the compiler pass was extended over the years, gaining support for type checking props in 2016, size optimizations in 2017, state in 2018, ES6 modules and classes in 2019. However, Quip completed the migration to TypeScript in late 2019, at which point this tooling was no longer needed.


RetroGit screenshot


RetroGit is a simple tool that sends you a daily (or weekly) digest of your GitHub commits from years past. Use it as a nostalgia trip or to remind you of TODOs that you never quite got around to cleaning up. Think of it as Timehop for your codebase. This blog post has more details, and source code is available.

Current status: Alive and well.

Sample output of dex-method-counts tool, showing per Java package method counts


dex-method-counts is a simple tool to parse Android APK or DEX archives and output per-package method counts. The DEX file format has a 64K method limit that we were running into at Quip, mostly due to code generated from protocol buffer definitions. We needed to know which packages were the biggest culprits (so that we could do some shor-term bandaid fixes), and none of the existing tools of the time had useful output. I wrote a quick-and-dirty tool using an existing DEX format parser. It was enough to unblock us, but we released it in case it was useful to others. This blog post has more details, and source code is available.

Current status: I didn't do much Android development after 2014, so I have not maintained the tool — there are quite a few forks so it must still be useful to some. multidex is also still a thing, though it's mostly transparent to developers.


Cilantro screenshot


Cilantro was a Chrome extension to share pages via Avocado. Ann and I had been using the app for shared tasks lists, and after the removal of social features from Google Reader, we wanted to give it a shot for sharing of links too. This blog post has more details, and source code is available.

Current status: While the extension is still in the Chrome Web Store, we stopped using the app sometime in 2013, and Avocado itself shut down in 2017.

Playback Rate Screenshot

Playback Rate

Playback Rate was a Chrome extension to easily control the playback rate of videos embedded in web pages, back when sites were starting to switch from Flash-based video playback to the HTML5 <video> tag. This blog post has more details, and source code is available.

Current status: The extension was swept up in a 2020 Chrome Web Store requirement requiring that privacy fields be filled out, and was eventually de-listed. It's still installable from source as an unpacked/developer mode extension. It could probably be redone to use the activeTab permission and thus have a much smaller permission footprint.


Intersquares screenshot


Intersquares was my entry into Foursquare's global hackathon (it ended up being a finalist). It computes the locations where two Foursquare users have been together, and has a social media integration for finding intersections with others. This was my first hackathon, and it was a lot of fun, though I did feel pretty exhausted coming into work on Monday after coding all weekend. This blog post has more details, and source code is available.

Current status: Though I dutifully renew the domain name, I never migrated the project off of the App Engine Python 2.5 runtime and thus it stopped working in 2017.


Mail Trends screenshot

Mail Trends is a tool to visualize various statistics about about an IMAP email account, especially a Gmail-hosted one. I was on a "if you can measure it, you can win it" kick, and though that having more insights into my email would allow me to better stay on top if it. This blog post has more details, and source code is available. There is also sample output from running it over the Eron Email corpus.

Current status: Archived, I have not tried to run it in over 10 years (as it turned out, getting more stats did not help with the firehose of email). Gmail still has an IMAP interface, but this is a Python 2 codebase that would require significant modernization.


plusplusbot logo


plusplusbot is a "karma bot" for Twitter. It monitors tweets about a topic with a ++ (plusplus) or -- (minusminus) operator and update a score for that target. This blog post has more details, and source code is available.

Current status: Archived, has not run since November 2009. The service relied on an "auto-friend" flag (where it would automatically follow anyone that followed it), which Twitter has since removed.


Overplot screenshot


Overplot is a mashup between Overheard in New York and Google Maps. I was living in New York at the time, and wanted to visualize things around neighborhoods I knew. The project had surprising technical depth (plotting thousands of markers in Google Maps was hard back then, as was geocoding thousands of hand-entered addresses) and social discoveries (who even decides New York neighborhood boundaries). This blog post has more details, and source code is available.

Current status: Surprisingly, it still works. I had to do some up-keep in 2013 to port it to the more modern Google Maps API, but it has kept working since then. The quotes are still the ones from 2006 though, the scraping mechanism relied on the site's RSS feed being archived by Google Reader (RIP).

Charts in Sourcerer screenshot

Sourcerer Charts

Sourcerer was an internal Google service created by Jorg Brown that provided very fast browsing of Google's monorepo, back when the source of truth was Perforce servers that always struggled under load. Jorg had the realization that all the metadata could fit in RAM, and built an alternate view that was very useful for browsing. I started using in 2005 (when I was on a build cop rotation and needed to quickly track down changes), and made some small improvements to it over the next few years.

My most significant contribution was using it to generate charts of change frequencies by author, directory or description matches. Besides being a navel-gazing sort of tool in the days before GitHub contribution graphs, it also allowed project activity to be tracked and patterns to be observed over time (e.g. how many changes mention "XSS").

Current status: Unknown, but presumably dead. Sourcerer (and its charts) were running when I left Google in 2012 (screenshot is from my last week week at Google), but ChartServer (the service used for rendering charts) was supposed to be turned off in 2019 and I would be surprised if Sourcerer was ported to Piper.