Jan. 17, 2023

2022 was a fun year for side projects. My indie apps made $2500 USD. I also hacked on a lot of open source code, doodled a fair few drawings, and started a new sci-fi lo-fi beats music project. \o/

Here's a spreadsheet of income from different software I made:

Spreadsheet of 2022 indie project revenue

All of the revenue came from projects that I barely worked on. The "Git days (2022)" column shows the number of days for each project on which I made a git commit. On average this works out to something like "part time days of work". As you can see the vast majority of the income came from work I did in previous years. That's the very definition of "passive income"!

The mobile music apps continued to grow from last year despite no work.

Roguelike Browser Boilerplate steadily made one sale per month. I didn't touch the code or do any marketing.

Hosted Gitea gained a surprising number of customers this year. This is likely due to the articles I wrote at the start of the year which helped a lot with SEO. I have been putting some dev time into Hosted Gitea again recently. I owe it to the current customers to turn it into a product I am proud of. In 2023 I expect it will make double what it made this year. I donated the 2022 profits to the Gitea open source project and I hope to donate again next year.

Jsfxr Pro was the for-profit project that I put the most work into. I only just turned it live. Signups have been gradual but I have had good feedback so far. The TODO list is long and I am going to take a break while I figure out priorities and see how it grows organically in the coming months.

The most interesting/useful numbers from the spreadsheet are RPM and RPTD.

RPM = "Revenue Per Mille". It measures the revenue per thousand visitors. It's basically a measure of marketing leverage. How much is it worth to point 1000 people's eyeballs at the product page for each app? A high RPM means I don't have to do as much marketing because a small amount of marketing gets a relatively larger amount of revenue. Low RPM means a lot of work on the marketing side to get enough eyeballs to make it worth doing.

So high RPM means more coding and less marketing, which is what I want.

RPTD = "Revenue Per Total Days". It measures how much revenue the thing made this year, divided by the total number of days on which a git commit was made ever. So it shows how much bang-for-buck in terms of my own dev time I get from that particular thing each year. If something has a high RPTD it means I got more revenue for less work. Note that for most of these projects I did no work this year so they have a yearly revenue-per-git-day of infinity!

Looking at these numbers helps me plan for 2023 and keep the motivation up. In the past couple of weeks I made a lot of updates to Hosted Gitea. I've got it to a good place for 2023. I'll do some more work on it in a couple of months time once I see how those changes go.

Right now I'm focusing on new pocket operator apps. First I am building a new free pocket operator app for Android and iOS. I got the idea from a review somebody left where they asked for an app that simply creates a sync signal for pocket operators. So I am working on that and it's almost done.

102417743d37774202cdfea3770fa2cd.png

Once PocketSync is done I'm going to work on some kind of synth or chiptune app. A simple little app you can use with your pocket operator to add synth lines and melodic texture. That app will be paid and open source. I'm hoping to have both of these apps out by the end of January.

Two other things I am doing in 2023. First I am drawing one doodle per day to keep the drawing muscle fit. Second I am open sourcing any new projects I start. I am going back to my old open-source-by-default way of working.

I'm looking forward to another fun year hacking on this stuff!

Dec. 30, 2022

Mastodon is a real breeze to develop for. I was able to use nbb (Clojure scripting on Node.js) to post an image using the API in a few minutes. Here's how.

Step 1: Create a new Mastodon app.

Go to Preferences -> Development -> New Application, or visit /settings/applications/new on your Mastodon server.

screenshot.png

Step 2: Store the access token

Once that is done copy the "Access token" and your server's URL. Put them in an env file:

export MASTO_ACCESS_TOK=...
export MASTO_SERVER=...

Get these into your current environment (or use direnv etc.):

. ./env

Step 3: Set up nbb & libraries

echo {} > package.json
npm i nbb masto

Step 4: Create the code

In a file called post-image.cljs put the following code:

(ns post-image
  (:require
    [promesa.core :as p]
    [applied-science.js-interop :as j]
    ["fs" :refer [readFileSync]]
    ["process" :refer [env]]
    ["masto" :refer [login]]))

(p/let [masto (login #js {:url (j/get env :MASTO_SERVER) :accessToken (j/get env :MASTO_ACCESS_TOK)})
        attachment (j/call-in masto [:v2 :mediaAttachments :create] #js {:file (readFileSync (last argv))
                                                                         :description "Test image"})
        status (j/call-in masto [:v1 :statuses :create] #js {:status "Hello this is a test!"
                                                             :visibility "public"
                                                             :mediaIds #js [(j/get attachment :id)]})]
  (js/console.log status))

Step 5: Test it.

npx nbb post-image.cljs some-image.png

Congratulations, you now have an nbb script which can post images to Mastodon. Enjoy!

Nov. 2, 2022

It's been a while since I posted an update. So this is it! The tl;dr is I'm working on a sound effects generator micro-SaaS and I am nearly ready for launch.

05a0dbec2650463514391d169be4cd84.png

In February this year I was working on the iOS port of one of my music apps when I decided to pivot. Safari iOS bugs were taking a lot of time and really dragging me down. I decided to focus back on pure web based music applications. I love the web and I love making web audio apps.

I decided to convert one of my online web audio apps to a subscription micro-SaaS business. To do this I had to build a bunch of infrastructure. I was tinkering with server side ClojureScript and my new web framework, Sitefox (here is an interview I did about Sitefox on the ClojureStream podcast: https://player.fm/series/clojurestream-podcast-2504492/e75-sitefox-with-chris-mccormic).

I really wanted to build everything on top of this highly productive stack. So I made a plan:

  1. add authentication to Sitefox (ClojureScript backend web framework).

  2. make it easier to integrate Stripe (new library).

  3. use those two pieces to convert some existing apps into micro-SaaS apps.

  4. 🌱 open source as much as possible.

I've been carrying out this plan since February. I've added authentication to Sitefox and I've also made a Stripe integration library. Now I am in the process of doing #3 - converting an existing web app into a micro-SaaS app.

The app I have chosen to convert is a sound effects generator called Jsfxr. This is a wonderful little piece of software written by Eric Fredricksen. I started contributing to, and eventually maintaining this public domain codebase some years ago. At one point I put it up online under the domain sfxr.me. Some years later I checked the stats of the site and it was getting more than ten thousand hits per month. Wow!

Software developers generally hate marketing. We just love to build stuff. But it's difficult to get people to use the thing you have built, without resorting to all kinds of unpleasant shenanigans like telling people about what you built, and communicating with other human beings.

So discovering my existing site already had traffic was exciting. All I have to do is build something that the visitors might want. So that's what I've been doing - I've been working hard on a Pro version of Jsfxr. It's going super well. It has been really fun to work on this codebase using an all-ClojureScript stack, and I am just about ready for launch.

If you're interested you can read a log of the development process in this Twitter thread. I will post an update when it's ready for launch.

Thanks for tuning in!

April 2, 2022

Dear YouTube,

What the actual heck? Here I was, innocently demonstrating how to install Nextcloud on a Linux VPS server. I just want to help other people liberate their data. Nextcloud is super cool and powerful and I want to share the good news. My video got a bunch of views and people seemed to find it really useful. Hooray!

nextcloud-youtube-cancelled-3.png

Then suddenly, without warning, you can cancelled my completely innocuous video. I appealed, and you rejected my appeal with no explanation at all. You say my video contains "harmful and dangerous content". Really? What exactly is harmful and dangerous in this video? No reasonable person could find anything in this video that could be construed as "harmful".

nextcloud-youtube-cancelled-2.png

Is this really about protecting viewers? Or is it about protecting your parent company? In the opening frames of my video I spoke about replacing proprietary services like Dropbox, Google, and Apple. Was my video cancelled because it's dangerous to users, or because this idea is dangerous to your parent company?

The only remotely harmful and dangerous thing here is that people might switch away from centralized services like those provided by your parent company, to Free, Libre, and Open Source Software provided by awesome hackers working in the public good.

Of course, I have no recourse now. Your interface does not allow me any way to speak to a human being about this. I feel completely powerless to do anything about it.

I've read about this kind of thing happening to others, many times on Hacker News. I always thought it was just bad luck and that will never happen to me. I have also read this exact thing from people this happened to previously.


Here is the original video, hosted on Vimeo, just so everybody can see exactly how innocuous it is. It's literally a how-to video for installing Nextcloud on an Ubuntu VPS.

So if you're reading this and you host videos on YouTube, I implore you, please make sure you take backups. Make sure you have a second copy of your precious videos. Make sure you have a plan for hosting elsewhere in case you get cancelled too for no good reason.

If somebody from YouTube is willing to un-cancel my video and remove all strikes from my account, I will update this post.

Update: after engaging @TeamYouTube on Twitter the original video has been restored. I did not receive further correspondence from them. To migate against this happening again in future I will mirror my videos on multiple platforms.

Feb. 5, 2022

2b6ebc3b1260328e5baa16be6f8e3edc.png

Gather round friends for a true and epic tale of glorious Indie Hacking. How I built and launched a micro-SaaS product in 26 weeks. How I discovered the quiet joy of incremental progress. How I weathered a Hacker News ban and a barrage of sniping comments and landed the first paying customer, only to come undone at the last moment, defeat snatched from the jaws of victory. 26 weeks of work gurgling down the internet's drain hole. And finally dear reader, what I will salvage from the wreckage that may help us in future ventures.

That Wednesday morning I was up with the cock. 6am bright and early. The kids were still asleep and my wife too making this the perfect time to GTD. I creaked my way through the house into my office, opened up my XPS 13 and sat in the glow of Gvim, code sparkling from my fingertips.

This day I would be a veritable @jdnoc. I would put in those Deep Work hours, chipping away at the product, posting new articles for SEO, reading feedback, testing and triaging bugs. And so it went.

For 25 weeks I had done this. Half a year of one-day-per-week stone cold indie software development. Writing code, building in public on Twitter. I had sat down on days like this when I was pumped to work and code flowed freely, and also on days when I was grinding all day long sweating out every line from a creased forehead. I ignored my feelings, hot and cold, and incremented away on the feature set week by week. Today was no different.

By 2pm I looked at my GitHub project board. Tickets had whipped by like restaurant napkins in a New York harbour bluster. The "TODO MVP" column was empty. I had moved every ticket into "Done". A strange feeling came over me. This was it. This was launch day. An excitement grew in my belly as I began to plan the launch on Twitter, PH, IH, HN. This ship was set to sail.

The launch

I set the Product Hunt launch to start one hour later at 3pm my time. I posted a link on Hacker News with the title "Show HN: Download Twitter data without API keys". I drafted an Indie Hackers launch post with the links to give everything a boost. Then I shared the Hacker News link around with a few friends and on my Twitter account.

Then came the first sign. The first warning that fate was not to smile upon the little micro-SaaS that could.

screenshot.png

Hmm that's odd. It became apparent my Show HN post had been shadow-banned. I could see it when logged in but none of my friends were able to load the page. What was going on? I was overcome with confusion. For what possible reason would the link to my site be banned? I emailed the mods that evening without much hope of receiving a reply and went to sleep.

The next morning I woke to an email from a very nice Hacker News moderator. It was super polite and informative which I am grateful for. Here is a summary of what they told me. I'm sharing in case it is useful for your own HN posting:

  • The HN software thought I was violating the rule against using the site mainly for "self promotion".
  • The software starts filtering posts once the percentage of posts about your own stuff is too high.
  • It's best to have a track record of interesting links from other sources and occasionally post your own stuff - the software is adaptive so you can start doing that and you'll stop getting filtered.
  • The best posts for HN are ones that can't be predicted from an existing sequence of posts ("out-of-the-way topics that rarely get any attention").

The Hacker News moderator said my submission history did not look too bad and I should be able to get it back on track. They kindly offered to unblock my post and push it back onto the front page and finally gave the following tip about posting Show HN projects:

Add a comment to the thread giving the backstory of how you came to work on this, and explaining what's different about it. That tends to seed discussion in a good direction.

The bit about seeding discussion in a good direction turned out to be wrong, but I think this is on the whole good advice. A good rule of thumb is "be human" and "don't spam".

I had a busy day on Friday and in the evening I did as the moderator suggested and added a comment with the development story. I emailed the moderator back gratefully to let them know and went to sleep.

The first customer

The next morning I woke to an awesome surprise. The first paying customer! It's happening! They had paid $5 for the 24 hour access tier so they could download their Twitter favourites to a CSV file. They seemed to be having trouble downloading the data but I thought it must be some user interface issue and decided to check out how the Hacker News post had fared overnight before looking into it.

screenshot.png

A fantastic result. 1.5hrs on the front page and 25 comments! My server registered around 7,000 individual visitors to the site when normally there are 30 per day. I dived in to see what people were writing in the HN comments.

screenshot.png

screenshot.png

screenshot.png

screenshot.png

screenshot.png

screenshot.png

Uh oh.

The fall

Like a fool I ignored the noble sages of Hacker News. I was Prometheus to their Zeus. They watched in horror as I made off with the stolen fire of their secret Twitter API access codes, and brought it to all humanity in the form of an easy to use one-click interface. I had driven them mad with my barefaced democratic liberation of the Twitter API.

Alas they were right. I should have taken heed.

8409350e87736554966b25067bb81227.png

Very soon Hephaestus the god of Twitter API Authentication shackled me to the Mountain of Access Revocation. Just like that, the TweetFeast account was permanently suspended. All API requests broke instantly and my appeals to the Twitter API team went unheeded. Without Twitter API access there is no hope for my app and it was done for. Which brings us to today.

I refunded the one paying user. I updated the home page and closed the sign ups. My app now lies burning in the water, gurgling, bobbing gently downwards. A moment of silence, friends. Soon she will be subsumed by the waves of obscurity and the internet will know her no more. Alack, alas, so long little SaaS.

So ends my misadventure in Twitter API development and so ends this story.

Salvage

Well not quite. I must be able to salvage something from the ruins of this experience. What can we rescue and what can we learn?

1. Sharing the source code

First off, I am open-sourcing TweetFeast. If you are into LISP or Clojure or server-side ClojureScript you might find the codebase useful. Here are some highlights:

Hopefully there is stuff in there that is useful to people building full-stack apps in ClojureScript. Get the TweetFeast source code on GitHub.

2. General advice

If you're going to build something on a Big Corp API, be careful. Read the terms. Ask around and see if anybody else has done what you are trying to do. When you launch pay attention to the way you word the capabilities of your product.

I once worked on an app that went into the App Store with no problem despite violating a whole section in the terms of service. That was because some people inside Apple liked the app and gave it the green light. Rules at these companies are not laws. They are subject to individual whims and a lot depends on signaling.

I suspect a major reason TweetFeast was shut down is because of the way I worded the launch post. There are multiple products which do a very similar thing to TweetFeast (download CSVs of Twitter data) and have been running for years, and I suspect it is because they did not position themselves as an "API workaround".

The final take away is about posting on Hacker News. First of all, remember to be a good citizen and don't only post "Show HN" links. Share stuff that is interesting if you want to keep your self-promotion score healthy. Secondly, don't worry about the haters. Yes, the app got banned, but it also got a customer within half an hour of posting. Before that first customer a lot of people found the beta useful. Hacker News commenters often overlook the value that other people see (especially non-technical people). So don't take it to heart. They are wrong famously often.

3. What I got out of it

It might seem like this experience was an epic fail, but I actually got a tremendous amount of value from building and shipping TweetFeast.

  1. I got to road test my full-stack ClojureScript library Sitefox and it worked great.
  2. I got to hone my self-hosted deployment skills some more with Piku.
  3. I managed to overcome Shiny Object Syndrome, stay focused for 26 weeks, and ship goddamn it.
  4. I now know how much effort it takes to build a minimal micro-SaaS MVP (one month of full time dev days).
  5. I wrote a bunch of code I can re-use again (Stripe integration, Twitter auth).
  6. I learned a bunch of new stuff about UX, UI, design, and SaaS app architecture.
  7. I learned an important lesson about building on 3rd party APIs.

Finally, the best part. I get my day back. I get back one day per week to work on something else. That's very exciting!

What's next

I think I will re-use most of this code to try and build another micro-SaaS, but not on Twitter. A straight up web app most likely. My music apps are doing ok, and so I'll probably try to do something in this space. Whatever I do, I'll post about it here and you can also follow along on Twitter.

Thank you very much for reading, I appreciate it!

Onward. 👉