I have written a lot of software. I used to do it for fun. Then I started getting paid for it. Then it stopped being fun. Then I stopped getting paid for it. And now I write software sometimes for fun, but mostly in self-defense.
ongoing projects
A huge collection of free screen savers for X11, macOS, iOS and Android. The collection is over three decades old, and still growing! For X11, it is also the only secure way to lock your screen.
Dali Clock
A melting digital clock for Unix, macOS, iOS, Android, PalmOS, WebOS, JavaScript, and more. If I have used an OS, I have probably ported Dali Clock to it.
Displays the lyrics for the currently-playing track in iTunes or Spotify. Can also bulk-download lyrics for multiple tracks. There are many like it. This one is mine.
We play music videos all day long at DNA Pizza, and on the DNA Lounge webcast when the club is not open, and take online requests. This software is how we do that.
Command-line tool to download YouTube, Vimeo, Tumblr or Instagram videos. Or, install the youtubedown.cgi bookmarklet to download the video you are watching from your browser.
All-purpose image and video resizer command line script. Resizes images or videos to fit within maximum and minimum sizes and aspect ratios; truncates videos to a maximum duration; de-rotates image EXIF; reduces the frame rate of anim-GIFs; converts anim-GIFs or HLS/M3U8 to MP4s; converts MP4s to anim-GIFs; extracts thumbnail images from videos; removes letterbox padding; normalizes audio volume; has presets for the various social media sites.

historically significant
Lucid Emacs
I was the primary developer of Lucid Emacs, the first variant of GNU Emacs with a real GUI and modern desktop integration. It eventually became XEmacs. (As per longstanding tradition: "why cooperation with RMS is impossible".)
Mosaic Netscape
I was one of the initial employees of Mosaic Communications Corporation, later known as Netscape. I was responsible for the Unix versions of Netscape Navigator through release 1.1. I came up with the name "Mozilla".
Mail & News

I designed, and Terry Weissman and I implemented, the Netscape Mail and News clients, versions 2.0 through 3.0. HTML email is probably my fault. Netscape Mail was our contribution to the proof of the Law of Software Envelopment:
"Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can."
Netscape 4.0 was not my fault. During that dark period, I worked with Lisa Repka on S/MIME in an attempt to keep everyone's email encrypted and secure from the prying eyes of the government and other snoops. That didn't really work out.
Terry, Will Scullin and I re-wrote Netscape Mail in Java, as Grendel. That didn't really work out either: the project was cancelled before we could finish it. But we threw the source over the wall in 1998 anyway. (Here's a browsable copy.)
Mozilla Dot Org
I was the accidental impetus for Netscape's decision to release the browser source code, and I was one of the creators and curators of the Mozilla Organization during the first year of its life. We coordinated the open source development of the browser, which eventually became Firefox.

I resigned from both mozilla.org and from (what came to be known as) the Netscape division of America Online just before AOL took over.
DNA Lounge
I am the proprietor of the world famous DNA Lounge, one of San Francisco's most popular and award-winning nightclubs. It turns out that it takes quite a lot of software to run a nightclub, particularly since we have audio and video webcasts which have been running 24/7 for more than two decades. I've published bunch of my DNA Lounge-related software as well.

smaller yet still kinda large projects
The Cyberizer
This takes a bunch of videos, finds the scene breaks in them, shuffles them randomly, and appends them back together with a burst of static between. Exterminate All Rational Thought.
The DNA Lounge live stream has a realtime chat component. This is that. It's not really IRC, but is a JavaScript front end talking to a trivial backend WebSocket server.
Rewrite the links in an HTML file to point to the Wayback Machine instead of the original site. Attempts to use a contemporaneous version from the archive based on the file date (or earliest git date) of the HTML file.
A simple, very small Web Socket server.
Resizes images for web pages on first demand, and caches the result. There are many like it; this one is mine. If your large images look like "/images/A/B/C.jpg" this lets "/images/scaled/640/A/B/C.jpg" serve up a 640px wide version, and so on. It can also convert GIFs to MP4s.
I took an old payphone and turned it into a fun toy by putting a small computer inside of it.
I stuck a small Linux computer inside a vintage-1982 Ann Arbor Ambassador 60 dumb terminal.
An application for testing the behavior of various streaming-network-audio players. It creates a test MP3 stream that produces metadata in various ways, to let you see which variations work with which players.
2012, 2016
I built an Arduino-based controller so that I can open and close my apartment's curtains from the command line. This is the source code and a description of the hardware.
Have you ever wanted to run a really, really old web browser, like one from 1994? I have. If you have, you may have discovered that they don't work with most modern web servers, because they spoke HTTP/1.0 instead of HTTP/1.1. Specifically, they don't send a "Host" header, and don't understand the "charset" parameter in "Content-Type". However, if you point one of these antique browsers at this proxy server, it will translate. (To run those old browsers on modern Linux systems, you'll also need some old libraries.)
This program reads files and copies their contents to stdout at an arbitrary bitrate, along with other bells and whistles. It's basically the heart of a streaming audio server: you want to serve data at audio rates, not full network rates.

Other features include: inserting metadata either in Icecast/Shoutcast style, or with a synthetic ID3 tag; limiting the output to a byte range across the whole set of files to implement HTTP "Byte-Range" audio seeking; and bursting out the first few seconds of the output to fill the client's first buffer quickly.
A program (and web page) that generates collages from randomly-selected images on the web, updated about once a minute.
A dissociator: a program that collects statistics on bodies of text, and then generates random text based on those numbers. (There is a web version here, too.)

wordpress plugins
I have a blog. It's pretty popular. Here's some code I wrote related to that.

A WordPress plugin that replaces the standard comment submission form with a WYSIWYG rich-text editor (Trix). It includes a toolbar for bold, italic, links, embeds, blockquotes, code, and lists.

A WordPress plugin to save and display the geolocation of each commenter's IP address. Hello to all my friends in the Russian bot farm industry!

A WordPress plugin to locally mirror commenters' Gravatars and serve them locally, rather than loading them from gravatar.com on each page load, thus both speeding things up and eliminating an off-site webbug.

A WordPress plugin that makes your shortlinks shorter, by encoding 7+ digit post IDs into 4 characters, and using a shorter URL prefix.
Herp Derp
A WordPress plugin that herps all the derps. All comments are replaced with "herp derp". You may find that this makes my blog easier to read. You may find that it makes your blog easier to read.

marginal hacks
The following are mostly small utility scripts, rather than full-fledged applications. These are tools that I wrote for myself to fill a personal need. This means that they are not necessarily very polished, but possibly you'll find some of them useful. Please take most them in the context of "one-hour hacks that have lived on far longer than expected."
Implements Peter Saville's cryptosystem as seen on FAC73, FAC75 and FAC93.
If you post links to your WordPress blog on Mastodon, this script will download all of the Mastodon comments and re-post them on the corresponding WordPress blog post. This is unidirectional and not realtime: it is not an ActivityPub implementation.
Post text and images to a Mastodon account. It can also do a simple incremental backup of your own posts.
Make "@you@yourdomain" work an alias for your Mastodon account elsewhere. (Not as useful as it sounds.)
IMG SRCSETs, how do they work?
iTunes XML
iTunes used to auto-update the "iTunes Music Library.xml" file, allowing third parties to examine the library. It stopped doing that in 2019, as of macOS 10.15. If that became a problem for you, a solution is included within the jwzlyrics source package.
Convert a macOS Calendar archive file (.icbu) to a standard set of .ics files (one per calendar rather than one per event).
"The date is now Tuesday, March 178th, 2020."
A few years back, someone hired me to port youtubedown to JavaScript.
Upload things to Tumblr. Remember Tumblr?
The official Instagram API has no mechanism for uploading photos. YOU HAD ONE JOB. This program fixes that omission. I reverse-engineered the secret, internal protocol that the Instagram mobile apps speak. It was a huge pain in the ass. Instagram Hates The Internet.
This is a stripped-down descendant of my earlier Facebook uploader that kinda-sorta still works. It can also kinda-sorta post to Instagram through the official API instead of the unofficial one. But only if the Instagram account is attached to a Facebook Business page (not a user account) and it can only post to the feed, not stories.
Scan a directory to find any photos taken near a given latitude/longitude, or print out the distance of each photo.
This does some simple static analysis of PHP programs to detect errors like using variables before they are initialized, undeclared globals and so on. PHP is terrible. This program is also terrible.
Edit your Youtube playlists and favorites from the command line.
Create a Youtube playlist from an iTunes playlist of music videos, assuming those videos contain metadata that says which Youtube URL they originally came from.
Reads a list of RSS feeds and download every Youtube or Vimeo video mentioned in them, using youtubedown. It keeps a list of already-downloaded URLs to avoid repeats. Also has a killfile.
Compare two .ics (iCal) files, and emit one with the events that are in the first but not the second.

Many sites (like Facebook) let you export events as .ics files which you can import into iCal; however, sometimes those imported events are not editable. This application will munge them into editability prior to importing them into iCal. Open the .scpt file in the AppleScript Editor, save it as an Application, and make that application be the default application for .ics documents.
For doing a partial one-way sync of iTunes libraries, using rsync, ssh and AppleScript. This script gets the contents of a playlist in the local iTunes, and ensure that those tracks (and only those tracks) exist in the library of the remote iTunes, with identical metadata.

A perl script that extracts the log of SMS messages from an iPhone (actually, from the iPhone's backups inside iTunes) and logs the messages to text files, with one file per conversation per month (analagously to the way most AIM and IRC clients archive things, instead of just dumping it all into one file). It's careful to never delete old archived messages, even if the phone's SMS log has shrunk or vanished.
This is a command-line utility for posting to a WordPress blog. Unlike the XMLRPC interface, this does not necessitate storing the clear-text password of a WP user in a file; it relies on normal Unix access control instead.
This script can mirror posts to your Twitter account into your Facebook and Instagram accounts. There are other ways to accomplish this, using Facebook apps or other commercial offerings, but this one is mine. Longer explanation on my blog.
munge videos

This is an iTunes AppleScript to process newly-imported music videos. Among other things, it: marks the track as being a "Music Video"; sets the "Comments" field to the resolution in pixels of the video; sets the "Volume Adjustment" based on the video's overall audio volume; copies the year, genre, rating, and capitalization from older versions of this same track; and adds this new track to the playlists of an old track that it appears to be intended to replace; and the old version is marked as to-be-deleted.
This is how I post to my blog via email. It takes an incoming mail message, extracts the images and saves them to a directory of your choice, then constructs HTML from those images and the rest of the message, and posts that to WordPress, Twitter and/or Livejournal. If there are images and they have location data in them, it marks the post with that location. It also allows you to write your text using Markdown instead of HTML.
This is a CGI that gives your web server prettier directory listings for directories full of MP3 files: instead of just listing the file names, the directory listing will include the ID3 data for name, artist, album, etc. It will also include a link to generate a zip file for each directory.
This perl script walks through a directory of MP3 files and deletes any ID3v2 frames that aren't used by iTunes. I found that I had a lot of useless crap in my MP3 metadata, left there by whatever software happened to rip them or touch them in the past. This removes all the cruft that iTunes can't edit (since if iTunes can't edit or display it, it might as well not exist).
This perl script walks through a directory of MP3 files and points out trivial spelling differences in band names, e.g., if sometimes you used "Pixies" and sometimes used "The Pixies".
This script provides remote control of Denon tuners (e.g., Denon AVR 2805, 2313CI, X2000 and similar) via their ethernet or serial port. It speaks the AVR/AVC control protocol language, and lets me, among other things, change the volume on my stereo by typing things like "tuner volume=-30dB".
There are a zillion programs out there that will try to automatically download album artwork for iTunes tracks, mostly by searching Amazon or the Apple store, but I've found that just feeding the artist and album name into Google Images and dragging the images in by hand is the most reliable way. This script gets the list of selected tracks from iTunes, and opens multiple browser windows running Google image searches.
Generates a static HTML page listing the music that has been added to your iTunes library in the last 90 days. It does this by interrogating the running iTunes via AppleScript. (It's possible to generate such a list manually from iTunes by creating an "added in the last 90 days" smart playlist and exporting the list; but this script is suitable for use from cron.)
This program takes a bunch of images and randomly tiles them into one large image. The images need not be of the same size or aspect ratio. Back when I used a film camera, I used to cover one of my walls with snapshots, but now that I use a digital camera, I no longer have prints lying around. I wrote this program to make it easy to cover my wall with digital prints in the form of large posters.
A parser for the (undocumented) .emlx mailbox files used by Mail.app on macOS X 10.4+. You can use this to convert Mail.app mailboxes back into standard BSD "mbox" files, for importing into other software.
A few bookmarklets (clickable JavaScript functions) that manipulate the URL of the page you're looking at:

[ up ]     Go up one directory.
[ + ]     If the URL contains a number, increment it.
[ - ]     If the URL contains a number, decrement it.
An Emacs mode for editing OpenBSD "pf.conf" files, with font-lock highlighting.
This script converts an HTML document into a multipart/alternative mail message, with a text/plain first part, and a text/html second part. It produces very readable text/plain parts.

The most interesting part of this script is that it contains a simple HTML parser that does a reasonable conversion of HTML to plain text. It handles most interesting tags: it does nested indentation for UL, OL, BLOCKQUOTE, etc; it handles PRE; it handles character entities; it wraps paragraphs. (It does not, however, handle TABLEs.)

Quoted-printable encoding will be used when necessary. When QP is used, lines are broken at word boundaries instead of merely every 72 characters: this means that the QP-encoded text will be highly readable in raw form, unlike most QP text you've probably run across in the past.
This generates static HTML photo galleries with thumbnails. For an example of what it looks like, see the DNA Lounge photo galleries.
Paste your raw chat logs into this web page, and it will convert them to pretty HTML with wrapped lines, clickable links, inlined images and so on. Works with IRC, AIM, text messages, whatever. Can produce HTML that looks like iPhone-like chat bubbles.
I guess everybody has written their own version of this; well, here's mine. This script checks for dead and/or moved links in the given HTML files. It parses the HREFs out of the HTML, then does a GET on each of them (not a HEAD, because that doesn't work with all sites) and prints out a list of URLs that were not retrievable, or that redirected to elsewhere.
A dead-simple HTML-editing mode for Emacs. I hate the HTML mode that XEmacs uses by default: it does too much second-guessing of my typing (automatically inserting matching close-tags before I've typed them and crap like that.) This is the minimalist HTML mode that I use: it has a proper syntax table, correct paragraph delimiter, and basic, restrained use of font-lock patterns.
This is a package that makes Emacs's gdb-mode much more usable by making almost everything that shows up in the *gdb* buffer be clickable, and have a context-sensitive pop-up menu of commands on the object that have been printed. (Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.)
Have you ever wanted to convert the contents of a newsgroup into a mail folder? I did once, and so that's what this script does. Point it at your news server, tell it what group you want, and optionally, what message to start at, and it will write out a properly-quoted mbox file (the kind used by Netscape, Eudora, and just about all of the Unix mail readers.)
This is a script that prints out some statistics about the messages in an mbox folder, e.g., to show who the most prolific posters to a mailing list are. It prints out a table of stats sorted several different ways.

long obsolete
Some old stuff that history has passed by. I've been at this a while.
(No longer works.) If you mirror your WordPress blog to your Facebook account, this script will download all the comments posted on your Facebook account and copy them to the corresponding WordPress post.
(I think this stopped working some time around 2018.) Backs up your Twitter direct-message conversations to the local disk, one text file per person with whom you have conversed. (Note that while Twitter provides a way to download an archive of your public posts, that archive does not include your DMs.)
Reads the schedule for the SXSW Music festival, intersects it against highly-rated tracks in your iTunes, and generates an iCal calendar file of bands of interest.
Let's say someone has mailed you an attachment, and you need to move that file up to your web server. Let's say you're reading your mail on an iPhone or iPad. Since Mobile Safari doesn't support the <input type=file> form element, (update: it does, as of 2021 or so) there's no easy way to accomplish that. However, with this script, you can configure a secret email address that will unpack any attachments sent to it into a directory of your choosing.
This script downloads an archive of your Livejournal and its comments. It can be used to import your LJ into a WordPress blog. It can also be used to set all of your posts older than a certain age to "no new comments allowed".
(This mostly doesn't work any more. Delete your Facebook.) This script can create an RSS feed of your Facebook stream, your friends' photos, etc. This indignity is necessary because the RSS feeds that Facebook provides barely work. Also includes a killfile for users or apps. Can also back up your Facebook private messages to the local disk.
This script can create an RSS feed of your Facebook stream, your friends' photos, etc. This indignity is necessary because the RSS feeds that Facebook provides barely work.
Tip Calculator
My first (and last) Palm Pre application, a restaurant tip calculator.
(This almost certainly doesn't work at all any more.) Given a URL of a photo gallery, this downloads the largest versions of all of the photos. It understands the gallery formats used on Flickr, Facebook, Tumblr, Picasa, OvaHere, Google Drive, SF Weekly (and related sites), Instagram, and sites using SmugMug or Zen Folio. It's easily extensible for new idiosyncratic gallery formats.
A perl script that runs sms2csv to extract the log of SMS messages from a Palm Treo and logs the messages to text files.
When I used Linux, this script was how I got pictures off of my digital camera. It files each image that it moves from the card into an appropriate directory of the form YYYY-MM-DD, and does some clever tricks to divide multiple "sessions" on the same day into different directories: e.g., when 30+ minutes has passed between photos, it starts a new directory. It worked on both Linux and macOS, though these days I use Lightroom.
A wrapper for wget to download numerically contiguous URLs based on a format string; for example, "wgetn https://host/img_%04d.jpg 201 299" to download img_0201.jpg through img_0299.jpg. Especially useful when trying to mirror galleries that have stupid directory structures (where simply using --mirror would download too much.)
This script lets you extract the URLs from your Mozilla history file, sorted by last access time. There were scripts to do this with every version of Netscape Navigator, because it stored history in a straightforward Berkeley DBM file. Sadly, the Mozilla folks saw fit to replace that with a brand new database called "Mork" for which no tools exist. So I wrote this to parse Mork and spit out the URLs. Mork sucks.
A small HTML validator: really all this does is make sure your tags are balanced, that your tables aren't missing TRs around the TDs, and that the local files that any relative URLs point to exist. There are much more fully-featured validators out there, but I haven't found them very useful: when all I want to know is "you left out a </UL>", they tend to spend their time whining at me about "where's your DTD?" and similar nonsense.
I raed a scrmabled-up peice of text taht cliamed that
    "it deosn't mttaer in waht oredr the ltteers in a wrod are, the olny iprmoetnt tihng is taht the frist and lsat ltteer be at the rghit pclae. The rset can be a total mses and you can sitll raed it wouthit a porbelm. Tihs is bcuseae the huamn mnid deos not raed ervey lteter by istlef, but the wrod as a wlohe."
so I worte tihs scrpit to scrmbale txet in taht way. Hey, it wroks!
A selection of tools for "screen-scraping" web pages to create RSS feeds. Back in the days before every web site provided an RSS feed, this was useful for creating your own feeds from their HTML.
X11 only. This is a hybrid GTK/OpenGL program for experimenting with the OpenGL lighting model. It lets you set colors and positions of lights and see what effect they have on a textured, rotating teapot.
(Now-obsolete) MP3 Jukebox software for Unix, from the days before I used iTunes.
Linux Kiosks
For many years, DNA Lounge included public internet kiosks; this page explains how I constructed and secured them.
X11 only. Obsolete. This is a simple standalone GUI mail composition tool for Unix. The 4.0-era versions of Netscape and early versions of Mozilla and Firefox didn't do anything sensible by default when you clicked on a mailto: link. This program could be used as the handler for those URLs.
A predecessor of the XJack hack from XScreenSaver; I won't spoil the joke, but will instead just tell you: "man sh | jack".
Do you hate your job? Are you only still there because you're waiting to vest? I feel your pain, brother. The only thing that kept me from leaving Netscape in 1997 and walking away from a dumptruck full of cash in frustration was this script. I ran this every morning for at least a year: it prints out the following motivational message:
    Today's NSCP price is $__._; your total unsold shares are worth $____. You are __._% vested, for a total of ____ vested unsold shares ($____). But if you quit today, you will walk away from $____.

    Hang in there, little trooper! Only _ years __ months __ days to go!
It's amazing how this script can put it all back into perspective and keep you from going postal and strangling someone. Fill in your numbers, and let it remind you not to do something you'll regret later.

As I'm not maintaining this any more, you may also be interested it a descendant of it over on dzm's site. He's added more features.
A (now-obsolete) program for re-mapping your keyboard on Unix.
In 1991, I wrote this program, "The Insidious Big Brother Database", which was an address-book and note-taking system that was tightly integrated with the Emacs mail and news readers. It was pretty popular until 1997 or so when even the last hold-outs stopped reading their mail in Emacs.
It is 1,223,146 decimal digits (which is 1.0e1223146) and the object representing it consumes 524 KB of RAM.
This prints j-cards for cassettes, cartridge tapes, 8mm tapes, DATs and CD jewel cases. It does all kinds of automagic font scaling and has the ability to include graphics. It is implemented completely in PostScript. This was one of the first popular programs I ever distributed online, way back in 1988. I haven't touched this since 1994, when I stopped using cassette tapes.
The companion PostScript program for printing labels for VHS video cassettes. Last modified: 1991.
Lisp Machines
Back before the Computing Dark Ages of the Nineties began, I hacked on Lisp Machines. If you've still got an Explorer or Symbolics, you might find some of the stuff I wrote in the good old days useful. It's archived in the CMU AI Repository.

© 1985-2022 Jamie Zawinski <jwz@jwz.org>
so much to do, so little time.