Load site modules...

Mccormick.cx

Mccormick.cx

an avatar

Chris McCormick - independent software developer

Chris McCormick - computer programming

🌐 Visit Mccormick.cx 🌐 Mccormick.cx besuchen

✍️Write rieview ✍️Rezension schreiben 🏷️Get Badge! 🏷️Abzeichen holen! ⚙️Edit entry ⚙️Eintrag bearbeiten 📰News 📰Neuigkeiten

Webfan Website Badge

Tags: chris_mccormick_-_computer_programming computer mccormick programming

Rieviews

Bewertungen

not yet rated noch nicht bewertet 0%

Be the first one
and write a rieview
about mccormick.cx.
Sein Sie der erste
und schreiben Sie eine Rezension
über mccormick.cx.

Mccormick.cx News

I Measured How Fast I Ship

https://mccormick.cx/news/entrie...

"How long until it's done?" It's the question every software developer dreads. John Carmack famously responded with "when it's done." He knew the inherent complexity of software development. Software estimation is hard.

It's not impossible though. Carmack walked back "when it's done." Scopes are flexible. There is data. We have shipped before and inferences can be made.

Why measure?

Knowing your shipping velocity is incredibly useful. Even a ballpark figure is helpful to make better decisions, set realistic expectations for yourself and clients/bosses, prioritize projects effectively, and generally reduce the stress of the unknown.

Time is the most precious resource we have. As a solo dev and compulsive project starter I have to worry about the opportunity cost of starting a new project. I need to know what I'm on the hook for if I commit to shipping a project. As a freelancer I need to de-risk projects for my clients and myself so they run smoothly and everybody wins.

So I analyzed 36 of my shipped projects. These are my results.

How I measured

Notes on measuring

"Shipped" means the software is online, does The One Thing it's supposed to do, and real users are using it. It doesn't mean the issue tracker is empty. I've basically ignored ongoing maintenance for the purposes of this analysis, and I excluded projects that have become long term maintenance projects.

I work on multiple projects at the same time. That's why I work 3 part time days per project on average. I know from measuring hourly invoiced client work that a day active on a project roughly translates to one part time day of work.

One big caveat to note is about LLMs. Almost all of this software was written before I adopted LLMs into my workflow. I'll say more about the impact of LLMs below.

You can certainly argue with the methodology here. Number of commits is similar to lines-of-code - it is at best only loosely correlated with productivity and software quality (and sometimes inversely correlated). It's an imperfect measure. There's a lot of uncertainty in "approximately one part time day of work". In the end the goal was not to get perfect values but rather to get ballpark figures to help me calibrate, plan, and make decisions.

Results

Category Avg Days Min-Max Days Commits/Day Weeks to Ship
Client Projects 45 5-129 6.01 2-43
Micro-SaaS 76 35-106 5.00 12-35
Music Software 52 20-136 3.77 7-45
Games 70 48-84 5.74 16-28
Open Source Apps 26 6-60 4.11 2-20
Open Source Libraries 35 5-101 4.59 2-34

commits_per_day.svg

Note: you can find the "raw data" table below for the full dataset.

Insights

Client projects follow a pattern: While they range from 5 to 129 days, there's a concentration in the 15-56 part-time day range, or about 5-19 weeks of calendar time. So what I can tell clients is an MVP will probably take 2-4 months of calendar time from start to finish. Simple projects can occasionally be completed in under a month. More complex ones might extend beyond 4 months. I can divide client projects into smaller apps and MVPs versus longer projects requiring more maintainance and updates and use that to inform and schedule client work.

Revenue-generating projects take longer: Micro-SaaS projects in particular take an average of 76 days (about 25 weeks), which is longer than client work. More generally all revenue-generating projects took longer on average (54.4 days vs. 37.6 days for non-revenue projects). The added complexity of marketing, payments, and other business jazz, creates overhead that is easy to underestimate. It takes more effort to build things people pay for. It's also more risky than client work. Most of these projects fail to generate much revenue.

Game dev is consistent: My games take 16-28 weeks with a somewhat consistent velocity. That suggests game development is quite predictable, which makes me want to build more games. If I know what I am getting into it makes it a much less risky proposition.

Music software is worth focus: Music software is both fun to work on and reliably generates side-project income. Even small projects can generate some income. I already knew I should focus on music apps, and this analysis backs that up. I can probably build small online music apps in 20-25 days or 8 weeks each and increase side-project profitability. That said I seem to be less productive on music projects judging by low commits-per-day. I'm not sure why that is. Maybe because music apps are more technically challenging to build.

Commit velocity varies by project type: Games and client projects have the highest commits per day (5.74 and 6.01 respectively). I think that's because I'm directly accountable to somebody else with client projects. Games are just fun to work on so it's easier to put dev time into them. As noted, music software has the lowest velocity (3.77) even though I enjoy working on it.

How I ship

There are three main components to my stack that help me ship fast.

  1. ClojureScript. They say programming language doesn't matter but Clojure definitely makes me faster. It's a highly expressive language, which translates into fewer lines of code per feature. Less lines of code, as well as other language features like immutability and functional code, means "faster to develop" and less time wasted on bugs. Then there's the great tooling with a strong focus on iterating during development.
  2. Piku. I ship multiple apps onto a single Linux VPS with Piku. Deployment is a simple matter of "git push". This means I don't waste time configuring things, and it's far cheaper than other options for projects that don't take off (which is most of them). As an open source project it is also quite hackable if I need to do something special. [Disclaimer: I'm a sometime co-maintainer of Piku.]
  3. Project management. You can watch a video I made about how I project manage myself using GitHub project kanban boards. Absolutely key is to constantly manage scope creep. I am the biggest source of scope creep and the technique to manage that is keeping two lists - one for the critical path and one for "later".

A note on LLMs

I performed this analysis before adopting LLMs into my workflow. With LLMs many development tasks are now significantly faster. I don't want to oversell this, as there are definitely many times when you go down an unproductive rabbit hole trying to coax the LLM to actually solve the problem and it becomes a net negative, but when it works it's like magic and you can shave hours of work off a task. One other negative is generated code with errors that passes testing but has hidden bugs.

Probably three things will improve to make those net negatives less likely: 1. Software developers like me will learn when and how to use LLMs and when to avoid them. 2. The tooling and prompting will improve to eliminate the failure modes. Maybe one day the LLMs will know when to say "I'm not going to be good at this task, don't waste your time". 3. Verification and testing methods will get better to help the models stay correct and recognise and fix their own errors.

So it will be interesting to re-run this analysis in a couple of years on projects where an LLM was involved and compare the results. I expect the number of days to build the same type of projects will trend lower.

These days, armed with LLM tooling, I find myself taking on small projects and bits of situated software that I previously would have been too busy for. That trend will probably continue and more custom software will get written in less time with LLMs in the loop.

On the whole, having a natural language machine intelligence at my finger tips when coding is not something I expected in a million years and is quite incredible! I'm very grateful this timeline came to pass.

Conclusion

It's useful to know approximately how long a given piece of software is going to take me to build. I feel more confident in planning now. I can roughly predict how many web apps I will have shipped by the end of the year. I can be more confident in giving client estimates. I can take on some fun projects like games and small tools knowing they aren't going to eat up all of my available dev time.

I highly recommend running the same type of analysis on your own codebases. All you need is a bit of git spelunking with git summary and then some of logging of your hours to get ballpark figures for your own productivity like days-worked-per-week and hours-per-commit.

One final thing I am incredibly grateful for is that I get to write so much software. I'm obviously addicted to it. The ratio of client work to side projects is skewed towards doing my own thing, and I know that I am very lucky to be able to choose to spend my time on what I want. It's a huge privilege.

Raw data

projects_by_days.svg

Category Project Days Commits C/D $
Games rogule.com 48 275 5.73
Games asterogue.com 84 432 5.14 ✓
Games SmallestQuest 77 490 6.36 ✓
Music Software Melody Generator 49 232 4.73 ✓
Music Software PocketSync 20 107 5.35 ✓
Music Software BeatGenerator 69 196 2.84 ✓
Music Software 8bitmusicmaker 25 82 3.28
Music Software PdDroidParty 136 350 2.57
Music Software speccy 20 71 3.55
Music Software po-loopsync 42 171 4.07 ✓
Micro-SaaS hostedgitea 101 485 4.80 ✓
Micro-SaaS TweetFeast 60 295 4.92 ✓
Micro-SaaS Transcript Generator 35 214 6.11 ✓
Micro-SaaS jSfxr Pro 106 443 4.18 ✓
Open Source Apps twiiit 6 18 3.00
Open Source Apps slingcode 60 270 4.50
Open Source Apps svg-flipbook 23 63 2.74
Open Source Apps livereload.net 15 55 3.67
Open Source Apps todoMini 29 170 5.86
Open Source Apps makesprite.com 22 108 4.91
Open Source Libs cljs-tiny-slides 5 29 5.8
Open Source Libs cljs-josh 8 31 3.88
Open Source Libs jsfxr 53 200 3.77
Open Source Libs sitefox 101 416 4.12
Open Source Libs bugout 52 178 3.42
Open Source Libs sitefox-payments 28 61 2.18
Open Source Libs roguelike js boilerplate 28 180 6.43 ✓
Open Source Libs itwriter 8 57 7.13
Client Projects Client Project 1 33 95 2.88 ✓
Client Projects Client Project 2 15 200 13.33 ✓
Client Projects Client Project 3 56 182 3.25 ✓
Client Projects Client Project 4 5 33 6.60 ✓
Client Projects Client Project 5 20 71 3.55 ✓
Client Projects Client Project 6 34 291 8.56 ✓
Client Projects Client Project 7 71 354 4.99 ✓
Client Projects Client Project 8 129 632 4.90 ✓

6.3.2025 05:42I Measured How Fast I Ship
https://mccormick.cx/news/entrie...

Advantages of Generating Clojure with LLMs

https://mccormick.cx/news/entrie...

So far Clojure is not the best choice of language when it comes to generating code using LLMs. This is probably because it is a niche language and so it is less well represented in the training set. LLMs frequently hallucinate functions that don't exist and other problems when writing Clojure code, and they seems to simply have more trouble writing good Clojure code.

However there may be some advantages to using Clojure when generating code with LLMs. These boil down to Clojure being very concise, and the LISP syntax being easier to verify.

In my experience it's possible to get better outcomes when generating Clojure by providing relevant context and code examples. In future we might see code generating LLMs that are explicitly trained on more Clojure code, which would probably also help.

4.2.2025 01:12Advantages of Generating Clojure with LLMs
https://mccormick.cx/news/entrie...

Everything is Special

https://mccormick.cx/news/entrie...

We have a tendency to view things as fungible. That's a fancy way of saying "substitute one thing for another." It's how we make sense of the world. We say "that thing is like that other thing, let's compress them into a single category."

Fungible is a term from the world of economics. When we look at a coin with one value on it and a different coin with the same value, we treat them as the same object. We can swap one for the other and get the same practical outcome. This is also true of most products of capitalism. One Mars Bar is much like another and you can interchange them without worrying too much. This is generally good and helpful and makes things work properly. Capitalism biases us quite strongly towards viewing reality as composed of fungible items.

acorns-2.svg

Even in nature we often treat objects as fungible. If somebody says "go and find two acorns" and we find seven acorns on the ground, we can generally pick any two.

No two acorns are the same though. We just pretend they are for the sake of efficiency. They are the same to within an acceptible tolerance. Two coins aren't the same either. If you're into numismatics you know a slight difference between two coins can mean a lot, and no two coins are actually identical after a while.

At the very least, even if you have two objects that are identical to a very high tolerance, they do not occupy the same physical space. Their locations in the universe are different and so are their histories.

These days the bias towards treating things as fungible is increasing. Maybe it's due to the permeation of economic thinking into every day life. Maybe it's to do with the increasingly virtual experience of the human condition. It's got to the point where we even treat other humans as fungible in many situations. The person who served you your coffee. The driver who picked you up this morning. The actual physical human being who clicked the like button when they saw your post on social media.

oak-leaves.svg

It's probably a bad idea to treat humans as fungible. It probably doesn't lead to the kind of world that is fun to live in. It's even more absurd than treating acorns, coins, or Mars Bars as fungible. Humanity is enumerable. There are a limited number of us and each of us is tremendously complex. Each of us evolved uniquely from millions of iterations of change and environmental influence. Every person alive has a unique genetic signature, phenotype, environmental context, consciousness, psychology, and place in history. They're simply not the same. The person who liked your social media post, drove you to your destination, served your coffee. All completely unique and deeply complex entities.

It's true that most of us will be forgotten. It's true that we might not be well known by many people in our time. So what? It's also true that we are physically unique and literally special (definition: "distinct among others of a kind").

In the film Fight Club the main character Tyler Durden says "you are not a beautiful and unique snowflake." I think that's wrong. Every snowflake is unique, every coin is unique, every acorn is unique, and every human is unique. You are distinct among others of your kind.

I'm trying to remember this when I interact with people each day.

31.1.2025 03:54Everything is Special
https://mccormick.cx/news/entrie...

ClojureScript Tiny Slides

https://mccormick.cx/news/entrie...

ClojureScript Tiny Slides is grug-brained presentation software.

It's built with Scittle so it is developed and runs 100% in the browser.

Source code: https://github.com/chr15m/clojurescript-tiny-slides

I made a video about it.

cljs-tiny-slides.gif

Origin story

Recently I was at Barcamp London and I decided to give an impromptu talk. I had most of a talk written already, but no slides. For the Nth time I evaluated the available presentation software and I couldn't find any which fit my admittedly strange set of preferences. So I did what any sane developer would do and built my own presentation software in the 15 minutes available. 😅 This was only possible because of the amazing tooling available in the Clojure ecosystem.

By the way, Barcamp London is a fantastic unconference and definitely worth checking out!

5.1.2025 09:37ClojureScript Tiny Slides
https://mccormick.cx/news/entrie...

What Was Good About PHP

https://mccormick.cx/news/entrie...

Developers optimize for different things in different situations. Developers at big companies with big teams optimize for process and unit tests and scaling. I optimize for building web apps fast and getting them out into the world.

PHP Logo

One of the best ways to do this in the 2000s was with PHP. You could very quickly build a small web app and get it live on the internet. It would be good if we could bring some of these features back to modern development.

What was good about PHP?

These features meant all you needed was your text editor and a webserver and you could quickly turn your idea into reality with minimum overhead.

2.12.2024 13:57What Was Good About PHP
https://mccormick.cx/news/entrie...

You Can Play Asterogue In Your Browser Now

https://mccormick.cx/news/entrie...

Asterogue header graphic

tl;dr: you can play the new version in your browser here 👉️ https://asterogue.com

This is just a quick note to let you know I re-released my sci-fi roguelike Asterogue for the web, so you can now play it in your browser. It works on phones and desktop browsers. The first few levels are free to play.

Asterogue is a "juicy" graphical coffeebreak roguelike that is pretty much directly inspired by the original Rogue in terms of scope and features. You descend 17 levels into the heart of an asteroid to find The Orb and save the universe. There are a bunch of different monsters which get progressively harder as you descend. Instead of magic there is technology and you can pick up nanotech items and beakers of chemicals to buff your character (or hurt them if you get unlucky).

I received a lot of feedback from players since the first release for Android and Windows and this release includes some changes based on that. Here's a list of quality of life improvements and major features that were added:

Thank you to Andry Bethpalko who helped implement some of the new features. 🙏

The game was always built with web tech but I only released it on Android and Windows at first because that seemed to be the right way to release a game. Well I realized maybe the right way is the wrong way. Rogule does well on the web so why wouldn't my other roguelike game? Now I'm trying out a web release to see if I can make it easier for more people to play Asterogue. So far this is working well and the game is getting more daily players than it ever did as a native app. I'm super grateful for that!

Asterogue initial release analytics

(It's a post for another time but I am increasingly of the view that native apps are past their prime and web based apps are the future. Yes I know PWA enthusiasts have been saying this for a long time, but after seeing stats on how much people dislike installing native apps versus visiting web pages, I think we may actually already be in this world and nobody noticed.)

Another big change is the payment model. The original Asterogue was like most other games in that you simply buy it in the app store or on Itch and download the game. This time I am trying a new experiment with this and instead of buying a downloadable binary, you can play the first few levels free in your browser and then you pay one-time to unlock the full game online if you want to continue. I think this strikes a nice balance for players as you get to try it out and only continue if you're actually into the game once you have picked up the vibe. I haven't really seen this done before with web based games so it's all a bit of an experiment.

Thankfully it seems this model is working for people as the game is making sales already. People seem to be ok with paying one-time to unlock the full game in the browser. Most of all though I am just happy to have people playing and enjoying the game instead of it sitting forgotten and lost in the app store piles. As I said I'm feeling very grateful my little game has new life. Thanks to everybody who has tried it! 🙏

Thanks for reading and I hope you enjoy playing it!

4.11.2024 11:32You Can Play Asterogue In Your Browser Now
https://mccormick.cx/news/entrie...

I Made Makesprite for Generating Sprites

https://mccormick.cx/news/entrie...

makesprite.com is a simple open-source online app I made for generating sprites for games.

animation.gif

The first time you open the app it downloads a set of default prompts and sprite sheets. These are a useful starting point for generating your own. You can click the "Re-run prompt" button and the prompt that was used to generate that spritesheet will be loaded. You can also use the "Copy prompt" button if you want to use it elsewhere. Before you can run a prompt you'll need to go to the settings page and enter an OpenAI key since the app uses the OpenAI API to generate the images. Click "Send" to send the prompt to the API and after a while you will receive a spritesheet back.

This post is available as a video on YouTube:

d457a991b132a5c01f3fdcd299e9c219.png

Once you get the spritesheet it will be stored locally in your browser. You can then tweak the image by using the fill tool to remove any unwanted background colors. Once you find sprites you like you can use the "extract sprite" mode. This will copy the sprite to your clipboard as well as providing an interface to download it. You can also favourite, download, or revert spritesheets to their original form if you make a mistake when removing background.

Makesprite uses OpenAI's DALL-E to generate the images and comes with a bunch of user-interface enhancements to make it easy to organize and extract game sprites. It is a 100% client-side browser app and nothing is stored on the server side. Because it relies on DALL-E for the image generation you'll need an OpenAI key to use it. Future versions may integrate other image generators.

Makesprite doesn't do anything you can't do directly with DALL-E. You can use DALL-E directly with the same prompts. However it does enhance the experience of the sprite-generation workflow specifically:

Limitations

The sprites generated by DALL-E are limited. They aren't animated. They are pixel based not vector based and aren't always cleanly separated from the background. They suffer the usual gen-AI issues with missing/extra limbs, elements that make no sense, bad reflections, weird shapes and shadows, etc. It is difficult to get any consistency between different generated sprite sheets. DALL-E is really bad at creating pixel art so I've focused on non-pixel-art prompts. Sometimes DALL-E throws things in that have nothing to do with the prompt. Finally, just like Copilot and ChatGPT, DALL-E is probably trained on non-public-domain and sometimes copyright data, and that might pose ethical or legal problems.

All that said, I think these sprites will be good enough for some use-cases.

If you're making a simple game with limited animations, and if consistency doesn't matter that much, or you only need a small number of specific sprites, or if you're only looking for simple flat token images, then makesprite might fit the bill. If you just need placeholder graphics to give your demo or gamejam game the right look or feel it might be good enough. I think makesprite could be great for gamejams where you just need something fast that fits the theme.

If you generate something that is good enough for placeholder graphics you could also take it to a real game artist once your game is ready, and pay them to create real game art using the makesprite output as a reference or inspiration. I'd be really happy if more work for real artists was an outcome from tools like this.

About the tech

I had heaps of fun building this app with ClojureScript. I've built a lot of small web apps with Reagent and CloureScript now and the workflow has only got better. At around 1000 lines of code and 21 days of part time dev this is probably one of the fastest apps I've built. I chose to deploy it to GitHub pages instead of going my usual route of deploying with Piku. As a predominantly front-end app hosting it on GitHub should make it faster due to their CDN.

This was fun to make. I hope you find it useful. Enjoy!

5.9.2024 13:45I Made Makesprite for Generating Sprites
https://mccormick.cx/news/entrie...

Generating ClojureScript Reagent Apps With Claude AI & Scittle

https://mccormick.cx/news/entrie...

Claude AI has a mode where it can generate something called "artifacts". One of the things you can do with this is generate simple single page web applications. It generates the web app and then mounts it in an iframe so you can quickly test it and give feedback. This gives you a fast iterative process using the AI to refine the web app incrementally.

This is pretty cool but I would much prefer a web app written in ClojureScript with Reagent forms instead of JavaScript or React. ClojureScript is more concise and I find it leads to less bugs and is faster to work with.

Note: this post is available as a YouTube video.

Claude AI app artifact generation

There is a version of ClojureScript that runs entirely in the browser called Scittle, created by Michiel Borkent who also created the babashka suite of Clojure utilities. Unfortunately Claude can only use libraries that are available on cdnjs when generating web apps, and Scittle was not available. So I raised a PR with cdnjs and it's now available to use in Claude generated artifacts.

What all this means is you can now prompt Claude to generate small ClojureScript apps and it will generate clean ClojureScript code. I've set up a basic repository with a ClojureScript + Reagent prompt and example HTML file you can give to Claude to get it started.

The best way to use this repository is to create a new project in Claude, and copy the prompt and the example in as the default project prompt. A project is Claude's way of letting you use the same prompt multiple times.

As a simple example, you can generate a basic compound interest calculator using the prompt from the repo and adding the following text to the end:

Please generate a simple compound interest calculator.

You can test the resulting app and see the code here. The code it produces is a single page HTML app with inline ClojureScript and I've shared the cljs part here:

(require
  '[reagent.core :as r]
  '[reagent.dom :as rdom])

(def state (r/atom {:principal 1000
                    :rate 5
                    :years 10}))

(defn calculate-compound-interest [principal rate years]
  (for [year (range 1 (inc years))]
    {:year year
     :balance (* principal (Math/pow (+ 1 (/ rate 100)) year))}))

(defn input-field [label key type]
  [:div
   [:label label]
   [:input {:type type
        :value (get @state key)
        :on-change #(swap! state assoc key (js/parseFloat (.. % -target -value)))}]])

(defn result-table []
  (let [{:keys [principal rate years]} @state
    results (calculate-compound-interest principal rate years)]
    [:table
     [:thead
      [:tr
       [:th "Year"]
       [:th "Balance"]]]
     [:tbody
      (for [{:keys [year balance]} results]
    ^{:key year}
    [:tr
     [:td year]
     [:td (str "$" (.toFixed balance 2))]])]]))

(defn compound-interest-calculator []
  [:div
   [:h1 "Compound Interest Calculator"]
   [input-field "Initial Principal ($): " :principal "number"]
   [input-field "Annual Interest Rate (%): " :rate "number"]
   [input-field "Investment Duration (years): " :years "number"]
   [result-table]])

(rdom/render [compound-interest-calculator] (.getElementById js/document "app"))

I hope this will be useful to people who want to build with LLMs and ClojureScript. Enjoy!

13.8.2024 19:57Generating ClojureScript Reagent Apps With Claude AI & Scittle
https://mccormick.cx/news/entrie...

Installing Xubuntu 22.04 on a Dell XPS 13

https://mccormick.cx/news/entrie...

Last week I installed Xubuntu 22.04 on a Dell XPS 13 (9305). It was flawless. Everything just works out of the box. 🤯

This is completely amazing for me because I have been installing GNU/Linux on computers since some time in the 1990s when Pentium was a thing. I remember staying up all night tweaking xfree86 modeline configs just to try and get a terminal window to appear without looking squashed or destroyed by scanlines, worried about the warnings that I could permanently damage my parents' monitor. I have spent a ridiculous amount of time, often at 2am, tearing my hair out in a tumultuous relationship with Linux over the years. Lets not even get into WiFi drivers or printers. 😅 So when I get to run my favourite operating system without the tradeoff of having to put up with the difficult parts, it feels like magic.

Last week was the first time I can remember that I have installed Linux and not had to edit a config or run a script to get something working. Everything just works, first time, no issues. It's wonderful, and I feel incredibly grateful for the millions of volunteer person-hours put into making this operating system and software stack. ❤️ You people are amazing!

I'm going to celebrate by showing you a screencast of how fast various applications start up under Xfce on Xubuntu on this 3 year old second hand Dell XPS 13. I see posts these days about the dire situation on Mac and Windows where startup times have got really bad and people are lamenting the good old days when apps were snappy. Well guess what? You can have those good old days. You just have to run lean software (yes with all the tradeoffs and caveats that entails, but you might be surprised by what you can run under Linux these days).

In this screencast I'm launching a bunch of different applications. The keys I am pressing show up at the bottom of the screen. First I use a key combination (alt-tilde) to launch the Xfce terminal but it's so fast that you can't actually see the time between when I press the keys and when the terminal shows up, so they appear to happen simultaneously.

Then I use Application Finder to launch various applications by name. The time between hitting Enter and the window showing up is what to look out for. For the record the slowest application on my system is Thunderbird email client which takes 3 seconds to launch. Enjoy the show!

6.6.2024 19:50Installing Xubuntu 22.04 on a Dell XPS 13
https://mccormick.cx/news/entrie...

Generating VST Plugins With ChatGPT

https://mccormick.cx/news/entrie...

tl;dr: check out LuaVST on ChatGPT if you want to generate some VST plugins.

I've been doing weekly beats this year and it has been a lot of eustress fun (my best song so far is "smectite canyon gambit"). I found a nice positive feedback loop between composing electronic music and writing software for dopeloop.ai. Composing helps me figure out which features are important on the software side.

screenshot.png

In recent weeks I've been tinkering with Protoplug. It's a piece of open source software which allows you to write VST plugins in Lua. It turns out Lua is efficient enough to do DSP processing on modern CPUs. You can write the code interactively in the embedded editor, which makes for a smooth iterative workflow. I am using Protoplug with OpenMPT as a host running on Wine and really enjoying it.

After tinkering for a bit I had the idea to take the Protoplug API and some examples and feed them to ChatGPT to see if it could generate plugins from a written description. If you want to try it yourself you can go here: LuaVST GPT. Note, I am using GPT-4 and I haven't tested this with GPT-3.5. You will need to install the Protoplug VST into your host and then copy the code from the chat session into the VST's built-in editor.

Results

So how good is it? I don't like AI hype. I'm going to try to be objective and honest.

  1. Good: it can generate plugin boilerplate really well. If you just want to get something up and running that is a bit more tailored than copy-pasting one of the examples then it works well. You can say something like "create me a plugin that pitches all incoming MIDI notes down by one octave" or "create me a plugin that generates a pure sine tone at 440Hz" and it will do a resonable job that is usually bug-free.
  2. Okay: it can modify your existing code. If you can't be bothered looking through the API for how to implement something you might get a pretty good first pass out of it by pasting your code in and asking for a change. For more complex changes to the code it is probably going to create a lot of bugs. One thing that would significantly improve this would be automatically feeding any errors back to the GPT. At the moment you have to copy-paste errors and often you will figure out what is wrong faster than the AI will.
  3. Bad: asking it to do something complex like "simulate a full TB-303 with incoming MIDI and take into acount the non-linearities as documented by Devilfish creator Robin Whittle in 1999" it is going to do a very poor job. Even the first part of that ("simulate a TB-303") is going to be too much to ask of it. I tried with a few different prompt variants and it couldn't get there. I think this is where the hype of AI falls down. At this point in time only a human practitioner with years of experience, a nuanced understanding, and the ability to iteratively listen to the output as they code, is able to work their way towards a really good bug-free implementation of a complex plugin.

An example of a session that went well was when I used an online graphing calculator to come up with a distortion algorithm, and then I gave the equation to the GPT and asked it to write a plugin. I tweaked the code a little bit but on the whole it was a good implementation and did what I wanted. A distortion algorithm is one of the simpler types of plugins to code from scratch of course.

In the end building this has saved me some time and typing. I am able to work with the output from the GPT and get fairly useful advice from it without having to keep the whole API in my own head. This feels like a microcosm of the larger usefulness of modern LLMs. Productivity boosting but not job destroying.

5.5.2024 16:50Generating VST Plugins With ChatGPT
https://mccormick.cx/news/entrie...

Moving Back to London & 2023 Review

https://mccormick.cx/news/entrie...

On the last day of 2023 we hopped on a plane to London. We've been here two and a half months now. Michelle and I lived here in 2009 and we've come back again with kids. I guess you could call this a kind of digital nomadism, except we're doing it the wrong way around moving to a country with a higher cost of living. We've already had some wonderful experiences exploring the UK and Italy, and we're looking forward to more! I feel very lucky to be able to do this with my family.

50c84a0ccb1cd1a42402903e2e291632.png

2023 was a good year for me in software development. Here's a recap of things I shipped. Most of these things are built with ClojureScript.

Open Source

I started the year building a new open source Pocket Operator sync app called PO Sync (source code on GitHub). It's a ClojureScript web app which I ported to iPhone and Android native using Cordova (Capacitor is what I'd use today). I did this to get warmed up for what I thought was going to be a year of shipping small music apps. More on that later.

In March I open sourced Roguelike Browser Boilerplate (source on GitHub). Sales of the boilerplate fell after I did this but I don't mind. It feels better to have this available as an open source project.

At the start of February I open sourced TweetFeast (source code here) in response to the catastrophic failure of that micro-startup. I thought releasing the source might be a way to salvage something from the experience. Hilariously TweetFeast actually went on to generate some revenue after it "failed" which was a big learning experience for me. More on that below.

At the end of February my open source web game Rogule went viral and started to get thousands of players per day. That kicked off after I listed it on /r/webgames and then it was picked up by Hacker News, re-tweeted by the GitHub twitter account, and got some press in Japan. I wasn't really sure what to do about going viral so I just watched and made a few small updates to fix things throughout the year.

What's interesting is Rogule had sat dormant for one full year with only a few players per day. There was no change to the software when it went viral. I learned a big lesson from this which is sometimes you don't need to add more code or features to make something work, you just have to get it in front of the right people. In other words, marketing. I've got some big plans for Rogule but haven't been able to give it much attention lately.

In the middle of 2023 I shipped livereload.net which is a fully client side web development live reloading utility. This is similar to Slingcode but more stripped back. Neither of these utilities get very much use but I learned a lot and honed my craft so all good.

In August I released a little utility called aish which is an AI shell command helper. I use this quite a lot on the command line. It has saved me many hours of looking things up when doing sysadmin type of things. Building this simple bash script was one of my first forays into ChatGPT's API. Interfacing with a proprietary 3rd party API is not my favourite thing and I'm looking forward to a future where it's easy to install and run local open source GPTs on our laptops and servers.

At the very end of the year I re-released SVG Flipbook. It's an online app for doing Inkscape animation. A couple of years ago I tried to commercialize this but it didn't work out. I decided to remove the website and just point people directly to the open source version of the app.

Throughout the year I put out several releases of Sitefox, a backend web framework for ClojureScript in the pattern of Django, Rails, etc. I really didn't want to build Sitefox but I had to. I'm all in on full-stack ClojureScript now and I needed something to replace Django. Biting the bullet and building it in the end turned out to be a good decision as just about everything I built this year was built on top of Sitefox. Even if nobody else uses this project it has saved me so much time as to be more than worth it.

Finally this year I open sourced a couple of libraries I am using to build apps for dopeloop.ai. The first is a small collection of music making UI elements for ClojureScript and the second is a collection of webaudio functions for ClojureScript. Both of these are fairly specific to the work I am doing on Dopeloop but I wanted to have them out there as a reference for anybody else building music stuff with cljs.

Micro-SaaS bootstrapping

In 2023 I contined my adventures in bootstrapping small online business projects. I learned a ton and managed to double the revenue from these projects over the previous year.

revenue-2023-annual.svg

The projects averaged $700 MRR overall and I managed to do my first $1k AUD month at the end of 2023. This trend has continued into 2024 and I've managed to do $1k MRR in January and February. I am not sure if I'll be able to double the annual revenue again in 2024 but that's my goal.

revenue-2023.svg

With the $1k MRR milestone it feels like I've unlocked something fundamental about how online businesses work. I think I've got a grasp now on the full suite of skills you need as a solo developer to ideate, build, launch, market, and sell software online. NOw bUy mY eBoOk!!! Just kidding.

Anyway, here is some detail about where the revenue came from in 2023.

Hosted Gitea

c1e49f993cd0f10d0bfc9b99c1ab35b3.png

Hosted Gitea is a hosting service for Gitea, an open source git code sharing web app. Hosted Gitea is an alternative to GitHub for people who want to get away from big corporation run code hosting. In 2023 I made 172 commits which included many improvements to security, stability and user experience. Probably the biggest user-facing change was introducing tiers for larger machines with more disk space etc.

The MRR grew by 200% during the course of the year. This basically comes down to search traffic. I don't do much marketing for this site, mostly just making improvements to the service.

Sfxr Pro

a46e78c5cfbaed150e37cd64e4cbc585.png

Jsfxr Pro is an online app for generating retro sound effects. It also grew 200% last year and continues to grow this year too. I am thinking about adding this app to the dopeloop.ai suite of apps. The way it would work is everybody with a Sfxr Pro subscription or a Melody Generator subscription would get full use of the other apps as well. It's a bit of work to make this happen but hopefully I'll get to it later in the year.

TweetFeast

e82c70380ed75bad76cc6321b2119ac8.png

TweetFeast was a micro-SaaS for downloading tweets and follower data.

I learned a really valuable lesson with TweetFeast. A few months after shutting down the app I started thinking about it again. There had been changes at Twitter. I saw that there was still traffic coming to the site from the "download followers" search term, which meant people still wanted to export their follower lists. So I spent a day ripping out all of the other functionality except the follower downloads, and applied for a new API key.

Hilariously, people started to pay for it and months later after "failing" it became a profitable app, reaching $100 MRR for a couple of months. All up it made $600 USD before further changes to Twitter APIs caused it to break again. This was a big surprise to me. I learned there may still be latent potential in time invested in failed business ideas. There's always the possibility I missed something the first time around so I shouldn't give up too easily.

Later I tinkered with an AI tweet generator but that never really took off. I'll probably sit on this domain name for a couple of years until I think of something else to do with it.

Transcript generator

f4463156b6051937d20ea863c963720e.png

I thought of the idea for Transcript Generator a while back when I first started tinkering with these new GPTs. There is a lot of hype around AI, and LLMs have many issues, but one of the things they are good at is summarizing and editing existing existing text. I record YouTube videos periodically and I thought it would be interesting to generate articles from those videos.

I sat on this idea for a while and then I realized it could be something useful to other people bootstrapping online businesses. Creating content to rank well in search engines is a difficult part of bootstrapping so this could be helpful. After I started looking at the tech I realized it would not be hard to bang this together and ship something. So I did that and shipped the first version at the start of September. Then I left it alone for a while to see what would happen.

The visitor numbers went in the right direction and so near the end of the year I decided to spend "just a week" shipping a paid subscription version. The transcript download features would be free but users would pay for the AI features like article generation.

Of course it took me longer than a week (programmer estimate!) but I finally shipped the paid version in early 2024. You'll have to wait to find out what happened but basically there are now customers paying for this utility.

Dopeloop (Melody Generator)

d701e63896acc140d4c26a8f97d92c7a.png

The smartest thing I did all year was shipping a Pro version of Melody Generator.

At the end of May I was making updates to the audio engine and general improvements. I had been vaguely thinking about a paid version of Melody Generator but something was holding me back. At the end of 2022 what had looked like the smartest idea was building music apps for Android and iOS and I thought that was the right way to go.

What finally pushed me over the line was this thread from Danny Postma where he talks about how much traffic you need to make a sustainable online business. He was talking about search results per day in the hundreds, but the term "melody generator" had thousands, and I was already capturing a good proportion of that traffic. What's more my numbers were going up.

So I abandoned my idea of working on on native music apps and started working on a Pro subscription version of Melody Generator. I shipped it on July 27th and immediately started seeing sales. It's not huge yet by any means but I can see a clear path forward to good healthy numbers with Melody Generator and the dopeloop.ai suite of online music apps.

What is wonderful about this is I found a way to avoid building native apps. I've always been uncomfortable with shipping software for the walled garden ecosystem of app stores. I don't think it's good for developers or for users. The open web is much better. The problem is that it's easier to reach paying users through an app store. Now that I've got the web app revenue above what I was making in the app stores though, it also makes economic sense to avoid them. Much better to ship pure web apps that people can use without installing anything.

Later in 2023 I also worked on a new style guide for Dopeloop apps. I also got a new app called Beat Generator pretty far along. I'm not yet ready to launch that one yet so keep it under your hat. I'm excited to finish it and ship in 2024!

Tinkering

Aside from these open source and commercial projects I also tinkered on a whole bunch of pie in the sky tech. Algorave stuff generating impulse tracker modules, Bluetooth LE sync for music apps, PNG metadata hyjinx, generating game assets with AI, a tiny browser game engine, a joplin blogging plugin, and a variety of ClojureScript experiments.

Hopefully this year I'll find some time to write up the results of those experiements where they could be more widely useful.

Plans for 2024

Now that we're a bit more settled in London I've been thinking about the year ahead. Work has continued apace on my existing projects and client work of course. My goal is really to not start anything big and new but to drill down into growing these projects. I want to focus on growing dopeloop.ai and the "online sound making apps" side of things as a whole.

I also have a weird idea about a way to ship open source web apps with a one-time license for pro features. Shipping web based software means people don't need to install any app, either client or server, and making it open source means they can avoid various bad failure modes (such as companies shutting down or selling user data). Making money doing this means it becomes a sustainable activity that it's easy to find time and energy for.

Some people are surprised to discover that even the Free Software Foundation is in favour of selling to make projects sustainable.

Many people believe that the spirit of the GNU Project is that you should not charge money for distributing copies of software, or that you should charge as little as possible—just enough to cover the cost. This is a misunderstanding.

Actually, we encourage people who redistribute free software to charge as much as they wish or can.

For me that is a kind of holy grail. To build free and open source software and to make money doing so. I think the world would be a better place if more people were able to do this and it's something I continue to aspire towards.

Anyway, for the short term I am plan to continue focusing on what is working. Thanks for reading, and have a great 2024!

24.3.2024 11:31Moving Back to London & 2023 Review
https://mccormick.cx/news/entrie...

CSS Animations for Game Juice

https://mccormick.cx/news/entrie...

tl;dr: have some CSS animations to make your browser games juicy!

particles.gif

My favourite game engine is the browser. You get so many batteries included when you use the browser as your runtime. Sprites, animation, sound, mouse, keyboard, touch, gamepad, fonts, text handling, localization, concurrency, networking, 2d, 3d, and a weird XML based scene-graph called "The DOM". The list goes on.

I'm not even talking about canvas based games. These days when I build games like Rogule, Asterogue, and Smallest Quest, I sometimes use <canvas> but I always use the DOM.

One cool thing I've discovered about using the DOM as your 2d game's scene graph is you can offload a lot of CPU intensive effects to a high performance declarative graphics language called CSS. This frees you up to write far less code, and do the more interesting stuff with game logic in your procedural code.

Smallest Quest

It was Juice It or Lose It that inspired me to put more juice into my games. The talk makes the case that "game feel" is a major part of what makes games fun. Game feel is a mixture of animation and sound in response to interactivity and in-game events. It's the difference between a flat game and one that pops.

When I started building Asterogue (a solo-developed space based sci-fi roguelike) I got to wondering - is CSS good enough for video games? I was using web tech to build the game already. Could I do all of the visual effects using only CSS animations?

Asterogue game play

I put together this collection of juicy CSS game-feel animations as a test and I've been building on them ever since. Feel free to use them in your own browser games!

https://chr15m.github.io/juice-it/

Before I made Asterogue I would have used JavaScript to manually script sprite animations. Manually coding animations takes a lot of time and effort. It's also not very performant. That means the game ends up less juicy than it should be. Why not speed the process up using the domain specific animation language built right into browser?

boing.gif

It turns out CSS is absolutely good enough for a large class of browser based 2d games.

In the end I was able to build and ship a juicy graphical roguelike in about 1.5 months. I used Electron and Cordova to build the Asterogue binaries for desktop and mobile. I used plain old CSS animations for game feel. I saved on code using pure JavaScript with just one library (rot.js). Asterogue is only 2k lines of code which helped a lot with debugging and development speed. Browser based debugging tools are also absolutely fantastic during game development.

I would highly recommend this path to anybody making 2d games. The browser is a killer game engine.

screenshake.gif

Have fun!

10.10.2023 13:04CSS Animations for Game Juice
https://mccormick.cx/news/entrie...

Replacing React With Preact in ClojureScript

https://mccormick.cx/news/entrie...

Today I put together a small test repo to check how much space is saved when replacing React with Preact in a ClojureScript project.

I used npm init shadowfront prtest to get a basic project up and running. This creates a simple one page Reagent app with a button you can click.

(ns prtest.core
  (:require
    [reagent.core :as r]
    [reagent.dom :as rdom]))

(defonce state (r/atom {}))

(defn component-main [_state]
  [:div
   [:h1 "prtest"]
   [:p "Welcome to the app!"]
   [:button {:on-click #(js/alert "Hello world!")}
            "click me"]])

(defn start {:dev/after-load true} []
  (rdom/render [component-main state]
               (js/document.getElementById "app")))

(defn init []
  (start))

I made a build to check the size of the resulting js binary. Then I uninstalled react and react-dom and installed preact@8 and preact-compat. Then I updated shadow-cljs.edn to add the following clause into the :app build:

:js-options {:resolve {"react" {:target :npm :require "preact-compat"}
                       "react-dom" {:target :npm :require "preact-compat"}}

This asks shadow-cljs to alias those React modules to the Preact compatibility layer throughout the whole stack.

I ran make to build the project before and after the change and got the following results:

  1. With React = 292k
    $ du -hs build/js/main.js 
    292K    build/js/main.js
  1. With Preact = 172k
    $ du -hs build/js/main.js 
    172K    build/js/main.js

A 41% size reduction (120k) for a simple one page app seems pretty good. Most of the remaining 172k would be ClojureScript core and libraries such as Reagent.

Update: shadow-cljs lets us generate a build report. Here's a build report with React and then Preact:

React ClojureScript build report

Package Weight %
react-dom @ npm: 18.2.0 128.22 KB 45.8 %
org.clojure/clojurescript @ mvn: 1.11.60 115.71 KB 41.4 %
reagent @ mvn: 1.1.0 22.93 KB 8.2 %
react @ npm: 18.2.0 6.49 KB 2.3 %
scheduler @ npm: 0.23.0 3.96 KB 1.4 %
org.clojure/google-closure-library @ mvn: 0.0-20230227-c7c0a541 1.12 KB 0.4 %
Generated Files 932 0.3 %
src 490 0.2 %

Preact ClojureScript build report

Package Weight %
org.clojure/clojurescript @ mvn: 1.11.60 115.61 KB 71.4 %
reagent @ mvn: 1.1.0 22.94 KB 14.2 %
preact-compat @ npm: 3.19.0 9.24 KB 5.7 %
preact @ npm: 8.5.3 8.18 KB 5.1 %
preact-context @ npm: 1.1.4 2.55 KB 1.6 %
org.clojure/google-closure-library @ mvn: 0.0-20230227-c7c0a541 1.12 KB 0.7 %
Generated Files 931 0.6 %
prop-types @ npm: 15.8.1 801 0.5 %
src 490 0.3 %

10.9.2023 00:42Replacing React With Preact in ClojureScript
https://mccormick.cx/news/entrie...

Announcing livereload.net

https://mccormick.cx/news/entrie...

Hello! Today I am very excited to announce a thing I've been tinkering with for the past month or so. You can find it at https://livereload.net.

It's a simple online utility that enables live reloading web development for your local HTML/JS/CSS projects. It's easy to use and you don't have to install anything. Just drag your web project folder onto the window and your index page will show up. When you edit the files on your local machine they will live-reload in the browser and you'll see your changes immediately.

That is basically all there is to it. I've found live reloading to be so useful in my own development and I wanted to make it easy for anybody to get this feature without complicated command line build tooling. I discovered a browser filesystem feature that allows polling for file changes and realized I could use it for this, and so I did.

So there you have it. If you have any feedback do let me know. Enjoy!

screenshot.png

23.6.2023 07:38Announcing livereload.net
https://mccormick.cx/news/entrie...

ClojureScript UIs in 500 Bytes

https://mccormick.cx/news/entrie...

tl;dr: you can generate very small (less than 1k) JS artifacts from ClojureScript with some tradeoffs. I worked out a list of rules to follow and made the cljs-ultralight library to help with this.

Photograph of a glider in the air

Most of the web apps I build are rich front-end UIs with a lot of interactivity. Quite often they are generating audio in real time and performing other complicated multimedia activites. This is where ClojureScript and shadow-cljs really shine. All of the leverage of a powerful LISP with its many developer-friendly affordances (editor integration, hot-loading, interop, repl) brought to bear, allowing me to quickly iterate and build with fewer bugs.

On many projects I find myself also needing a small amount of JavaScript on a mostly static page. An example would be a static content page that has a form with a submit button that needs to be disabled until particular fields are filled. It seems a bit excessive to send a 100s of kilobyte JS file with the full power of Clojure's immutable datastructures and other language features just to change an attribute on one button.

In the past I resorted a tiny bit of vanilla JS to solve this problem. I have now discovered I can use ClojureScript carefully to get most of what is nice about the Clojure developer experience and still get a very small JS artifact.

Here's an example from the Jsfxr Pro accounts page. What this code does is check whether the user has changed a checkbox on the accounts page, and shows the "save" (submit) button if there are any changes.

(ns sfxrpro.ui.account)

(defn start {:dev/after-load true} []
  (let [input (.querySelector js/document "input#update-email")
        submit-button (.querySelector js/document "button[type='submit']")
        initial-value (-> input .-checked)]
    (aset submit-button "style" "display" "none")
    (aset input "onchange"
          (fn [ev]
            (let [checked (-> ev .-target .-checked)]
              (aset submit-button "style" "display"
                    (if (coercive-= checked initial-value)
                      "none"
                      "block")))))))

(defn main! []
  (start))

This code compiled to around 500 bytes. It has since been updated to do a bunch of different more complicated stuff and today it compiles to 900 bytes. I'll talk about some of the special weirdness and language tradeoffs in a second, but first here is the shadow-cljs config I used.

{:builds {:app {:target :browser
                :output-dir "public/js"
                :asset-path "/js"
                :modules {:main {:init-fn sfxrpro.ui/main!}}
                :devtools {:watch-dir "public"}
                :release {:output-dir "build/public/js"}}
          :ui {:target :browser
               :output-dir "public/js"
               :asset-path "/js"
               :modules {:account {:init-fn sfxrpro.ui.account/main!}}
               :devtools {:watch-dir "public"}
               :release {:output-dir "build/public/js"}}}

The first build target :app is for the main feature-rich app which does all the complicated stuff. I am fine with this being a larger artifact as it does a lot of things for the user including realtime generation of audio samples.

The second target :ui creates a file called account.js which is just 900 bytes. It gets loaded on the accounts page which is statically rendered. The reason for two completely separate build targets is otherwise the compiler will smoosh all of the code together and bloat your artifact size. It is easiest just to keep both codebases completely separate.

When compiling I found it useful to have a terminal open watching the file size of account.js so I could see real time when the size ballooned out and figure out which code was making that happen.

So what tricks do we have to use in the code to get the artifact size down? Here is a brief list of rules to follow to stay small. If you break any of these rules your artifact size will balloon.

  1. Do not use any native Clojure data types. Don't use vec or hash-map or list for example. Instead you have to use native JavaScript data structures at all times like #js {:question 42} and #js [1 2 3]. That also means you will have to use aget and aset instead of get and assoc. It means we are dropping immutablity and other data type features.
  2. Do not use Clojure's = operator. I know that sounds mad but what you can use instead is ClojureScript's coercive-=. This function does a native-style surface level JavaScript comparison. This means you have to give up the value based equality comparison you can use on deeply nested datastructures in Clojure.
  3. Do not use certain built-ins like partial. Other clever built-ins like juxt are probably going to be bad for file size too. As far as I can tell it's anything that uses immutable Clojure types under the hood. For the specific case of partial you can use #(...) instead to do what you need.
  4. Use js/console.log instead of print.
  5. Use (.map some-js-array some-func) instead of (map some-func some-js-array)

Generally as far as possible you should stick with native JavaScript calls and data types.

If all of this sounds onerous remember that the idea here is to only do this in situations where you have a small codebase giving the user some small amount of interactivity on a web page. So that's the tradeoff. You still get LISP syntax, editor integration, hot-loading, repl, and lots of other nice Cloure stuff, but you have to forgo immutable datastructures and language features like partial.

I have created a small library called cljs-ultralight to help with the UI side of things. It uses browser calls and returns JS data types. You can use it to perform common UI operations like attaching event handlers and selecting elements, without incurring too much overhead.

The library applied-science.js-interop also works with these techniques. Require it like this: [applied-science.js-interop :as j] and you can use j/assoc! and j/get and friends. Note if you use j/get-in or other functions that take a list argument, you should instead pass a JavaScript array which works well.

Also note that @borkdude has a couple of very interesting projects under way in this space. Check out squint and cherry for more details.

26.3.2023 12:15ClojureScript UIs in 500 Bytes
https://mccormick.cx/news/entrie...

Roguelike Browser Boilerplate is now open source

https://mccormick.cx/news/entrie...

be288dc6b22d9de42a1c2d2c4c47cb10.png

Hello! Just in time for 7DRL, Roguelike Browser Boilerplate is now open source.

The boilerplate is a JavaScript based game template that takes care of all the annoying stuff like splash screen, start screen, credits screen, instructions screen, settings screen, menus, pixel styled UI, win/lose condition screens, sound effects, animations, etc. so you can get on with making the actual game.

It's ROT.js based and includes example implementations for monster, inventory, level gen, etc. It works on mobile and desktop.

The license is MIT so you can do what you want basically, including using it in a commercial game.

Enjoy!

2.3.2023 06:43Roguelike Browser Boilerplate is now open source
https://mccormick.cx/news/entrie...

Python Will Be Number One

https://mccormick.cx/news/entrie...

I predict Python will be the most used programming language among developers world wide by 2032. This post contains my reasoning.

First take a look at this chart.

Chart of most used programming langauges 2022 from Statista

(Chart source. In the chart Javascript is #1 with 65%, HTML/CSS is #2 with 55%, SQL is #3 with 49%, Python is #4 with 48%.)

It looks this way because of the web. The world has chosen web tech over everything else because it is fast and easy to make software that runs on the web. It is fast and easy to let other people run your software on the web. You don't need anybody's permission. The only developer dependency you really need is a web browser, and the web browser is a feature rich runtime environment that can be programmed to do a huge variety of user-facing multi-media things.

The reason why Python is fourth on that list is because it is the easiest thing to use for just about everything else. From data science to web backends to ops glue code and micro-controller snippets. If Python ran in the browser it would probably be the easiest thing to use there too. There have been several attempts to bring Python into the browser. PyScript, based on Pyodide, is the latest and the best so far.

I took it for a spin the other day. It is quite large (900k) and it is quite a bit slower to load than native JS. It does this annoying thing of hard-coding a loading spinner instead of letting the developer take care of that. Clearly though, this iteration of Python in the browser is now good enough for a large number of front end use cases. Here is my minimal hello world PyScript HTML file to get you started:

<!doctype html>
<html lang="en-us">
  <head>
    <title>PyScript test</title>
    <meta charset="utf-8">
    <script defer style="border-radius: 12px 12px 11px 11px;width:auto;max-width:100%;height:auto;" frdl-lazy-src="https://pyscript.net/latest/pyscript.js"></script> 
    <style>
      body { max-width: 800px; width: 100%; margin: 1em auto; font-size: 2em; }
      #pyscript_loading_splash { display: none; }
      py-script { display: none; }
    </style>
  </head>
  <body>
    <div id="app">Loading...</div>
  </body>
  <py-script>
    from js import alert
    Element("app").write('PyScript was here!')
    # alert("hi")
  </py-script>
</html>

If you have a Python heavy dev team and you're building some internal web tool that doesn't have to load instantly, it's a no-brainer. Even if you're building a larger public facing app, it's probably good enough. Lots of front end apps today are this heavy and load about as slowly anyway. I have no doubt the size and speed will improve over time.

I think a lot of people will start using Python in front end browser code soon. They like Python already, and now they can use it in the web browser. It will only take a large-ish minority of projects using Python to bump it up above JavaScript, since it is already so popular in other domains. I expect that will happen in the not too distract future.

I don't think JavaScript will go away. It is the most popular programming language in the world and the syntax is not that much less accessible than Python. I think both languages will remain popular, and hopefully many other languages too, but I do think Python will become the most popular language.

Here are my own browser dev preferences in case you are interested. I no longer write as much Python or JavaScript code as I use full-stack ClojureScript. I like JavaScript and I am grateful that Eich was there at the right time and place to create this amazing LISP in C's clothing. The web would not be the same without the swiss army knife network language that is JS. I enjoy Python too. It is a very accessible language and that is an important reason why it is so popular.

Note, I have used the word "easy" here on purpose. Simple is not always easy, and simple is probably more important than easy, but the world sure does love easy.

10.2.2023 05:25Python Will Be Number One
https://mccormick.cx/news/entrie...

PO Sync Pocket Operator Sync App

https://mccormick.cx/news/entrie...

My first app of the year is out, hooray! \o/ It's a simple app to sync pocket operator devices. It outputs a sync signal from your phone which you can plug into your pocket operator's left input to drive it using a 2.5mm male-to-male stereo audio cable. It works well with the p0k3t0 Sync Splitter.

You can get it for Android and iPhone:

PO Sync connected to a phone

This was a fun app to build. I made it because somebody left this review on one of my other apps on Google Play:

Using this for the PO sync feature. I like that most; everything else is okay... I think a great idea would be to make an app with just the PO sync feature and a tempo slider or wheel, plus an on/off

So I knew there was at least one person who wanted this app. It was simple to implement and I got to use my favourite programming languge, ClojureScript. I love it when people need software that I know I can put together quickly. You can get the source code here:

https://github.com/chr15m/PocketSync

2023 is going to be the year of pocket operator apps for Dopeloop and I. I hope to make at least 4 new music apps. I'll post back here when I release them (and also to newsletter + Dopeloop subscribers).

30.1.2023 10:20PO Sync Pocket Operator Sync App
https://mccormick.cx/news/entrie...

Micro-Startup Plans for 2023

https://mccormick.cx/news/entrie...

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!

17.1.2023 13:33Micro-Startup Plans for 2023
https://mccormick.cx/news/entrie...

Post An Image To Mastodon Using Nbb

https://mccormick.cx/news/entrie...

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!

30.12.2022 01:08Post An Image To Mastodon Using Nbb
https://mccormick.cx/news/entrie...

Jsfxr Pro Retro Sound Effects Generator

https://mccormick.cx/news/entrie...

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!

2.11.2022 01:43Jsfxr Pro Retro Sound Effects Generator
https://mccormick.cx/news/entrie...

YouTube Cancelled My Nextcloud How-to Video

https://mccormick.cx/news/entrie...

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.

2.4.2022 08:37YouTube Cancelled My Nextcloud How-to Video
https://mccormick.cx/news/entrie...

Come Warm Yourself By The Flaming Wreckage Of My Micro-SaaS

https://mccormick.cx/news/entrie...

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 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. 👉

5.2.2022 07:07Come Warm Yourself By The Flaming Wreckage Of My Micro-SaaS
https://mccormick.cx/news/entrie...

A Space Ship For Lizards

https://mccormick.cx/news/entrie...

My son asked me to make a spaceship for his lizard toys. This is it.

ship.gif

screenshot.png

hoop-ship-render.png

Lizard persons not depicted.

10.1.2022 03:54A Space Ship For Lizards
https://mccormick.cx/news/entrie...

Doodle CSS HTML theme

https://mccormick.cx/news/entrie...

The other day I released Doodle CSS. It's a simple hand drawn HTML/CSS theme. You can use it make a web page look like a hand drawn mockup.

d77bd024c239dd5c922438b89a36f253.png

Since then it has garnered an astonishing ~500 GitHub stars, blowing past all of my other open source project repos. It made it to the front page on Hacker News and friends tell me it got featured in multiple tech newsletters.

It only took me a couple of days to put together Doodle CSS. The idea is one I've had for a long time though. The right combination of knowledge and motivation finally arrived last week and spurred me into action to implement it.

What a strange world it is building in public. Imagine spending your whole life as an open source developer with low-key popular niche repos, and then a literal scribble that took two days to make is what everyone latches on to.

It's useful feedback though, and that is the value in building in public. It seems like I've hit upon some deep seated doodle desire with this release. I feel like there is something bigger here I should develop. I will think about this some more.

For now I am happy with this as an open source project that other people are enjoying, and I'll admit the brief glow of popularity is very enjoyable.

19.12.2021 02:53Doodle CSS HTML theme
https://mccormick.cx/news/entrie...

Doodle Rogue Tileset

https://mccormick.cx/news/entrie...

Hello reader! I'm excited to announce the release of the Doodle Rogue tileset. It's a free hand drawn tileset containing tiles and sprites that I used in my game Smallest Quest.

scene-goblin.gif

I thought it would be fun to set the graphics free and see what other people do with them. Let me know if you make something using Doodle Rogue!

26.11.2021 12:46Doodle Rogue Tileset
https://mccormick.cx/news/entrie...

Build Full-stack ClojureScript Websites With Sitefox

https://mccormick.cx/news/entrie...

Sitefox is a back-end web framework for ClojureScript. I built it because I wanted to use ClojureScript to build websites and apps instead of Python and Django. It's inspired by frameworks like Django, Rails, and Flask.

Sitefox Logo

The easiest way to try it is to use one of the npm create scripts.

Use npm create sitefox-nbb for a simple website with no Java tooling dependencies.

Use npm create sitefox-shadow-fullstack for a full stack app using shadow-cljs.

Features

Here are the features it supports so far:

There are a ton of examples in the examples folder on GitHub.

If you build something cool with Sitefox let me know!

19.11.2021 01:12Build Full-stack ClojureScript Websites With Sitefox
https://mccormick.cx/news/entrie...

Melody Generator iOS

https://mccormick.cx/news/entrie...

melody-generator-iOS-mockup.png

After many days of debugging (and filling out a bajillion forms) Melody Generator is finally available as an iOS app!

apple-app-store-button.svg

It's also available on the Google Play store.

You can still use the web app without installing anything at dopeloop.ai/melody-generator.

Enjoy!

15.11.2021 12:38Melody Generator iOS
https://mccormick.cx/news/entrie...

How I invalidated my best startup idea

https://mccormick.cx/news/entrie...

It's a simple fact that most startup ideas are not going to work out. Just like most tweets never get a retweet, most GitHub projects have no users, and most Hacker News posts never get an upvote. That's the simple truth.

If most ideas are no good the most efficient way to find good ideas is to discard the bad ones as quickly as possible. People talk about "validating" their business ideas by doing market testing, but "validating" has a bias towards a positive outcome. In reality the outcome is usually negative. So don't validate your startup ideas, invalidate them.

I've found it useful to take a scientific attitude. When you have a business hypothesis you need to run experiments to see if your hypothesis matches with reality. In the words of Richard Feynman, "if it disagrees with experiment, it's wrong." Just like Newton poking his eyeballs with knitting needles it helps to be a little detached. Not your retinas though. You'll need those.

237c179e06f8c49e31f5aa72eb7da2d1.png

This is the story of how I invalidated my greatest ever micro-SaaS idea.

A couple of weeks ago on my morning run, high as a kite on endorphins, I came up with a magnificent idea. I was going to change the world by helping noble open source developers get funded. I'd do this by helping people grow their GitHub sponsors with perks for their sponsors.

I spent the whole day doing some deeper research. There was so much going for this idea. Here is the list of "pros" I wrote down and breathlessly emailed to my entrepreneur friends:

Foolproof. I started fantasizing about what I would say in my Indie Hackers Podcast interview when it hits $10k MRR. How one has to stay humble and wait for the big idea. How one has to work hard and stay focused. How one must listen to the customer you idiots, listen to the god damned customer!!!

The idea was perfect. Except for one thing. Nobody actually wants this software. I know this because I invalidated it.

I set a concrete goal. If I can't convince 100 people to sign up for the launch in 2 weeks then I will give up on the idea. John O'Nolan got 30,000 sign ups from one blog post about his idea before building anything. Surely if my idea is any good I can get 100 signups.

Invalidation #1 - friends

The first thing I did was tell some of my developer friends. Their feedback was interested but luke-warm and skeptical. None of them wanted this product. Nobody said "take my money!"

yeah I like your gh idea too but unsure if its good or bad

Ok, that's interesting. Maybe it's just my particular developer friends who don't want this? Friends try hard not to hurt or offend you so "your idea is ok" should really be downgraded to "your idea sucks".

I should also note that one of my friends is an open source developer actively taking donations. They should be the target demographic, but they were not very interested.

Invalidation #2 - search

It's always good to check search traffic. Is anybody out there looking for your solution already? You can check Google search volume, Reddit, and Twitter.

For Google search volume I use Ahref's free Keyword Generator tool and also the SurferSEO plugin.

Ahrefs said there are 80 searches for "github sponsors" per month. Not great. SurferSEO said 880 searches per month. That's better, but I trust Ahrefs more. The keyword is also very broad. Searches for more targeted keywords like "how to get github sponsors" were very low.

A Twitter search for "github sponsors" shows there is a lot of chatter. Out of all of the data I collected this Twitter chatter is probably the strongest pro-validation signal.

A Reddit search likewise shows a bit of traffic for people talking about "github sponsors" but the volume was lower than Twitter.

So I built a landing page

I created a simple signup page where people could get notified of the launch by signing in with GitHub. That should get high quality signups from real GitHub users. It could not be easier. All they have to do is click the "sign in with GitHub" button.

c42afca0681de1595fbe1b9f2404d73e.png

I wrote up the idea in the clearest way I could, explaining the benefits. This also allowed me to proof-of-concept the tech stack and GitHub API integration and make sure I could actually deliver the features. You can see the site at GHPerks.com.

Then I started the next round of invalidation testing.

Invalidation #3 - landing page

I posted about the site and the idea in a bunch of places.

  1. I wrote a tweet asking if I should pivot from my previous micro-SaaS idea to this.

screenshot.png

161 impressions. 4 people clicked through. No signups.

  1. I posted to my local Linux users group mailing list. It has thousands of open source people on it.

1 reply. No signups.

  1. I posted on Indie Hackers with the title "Looking for devs who want to grow their GitHub sponsors".

33 views. 3 upvotes. No signups.

  1. I posted a final tweet as I felt like the first one didn't really communicate it well.

screenshot.png

214 impressions. 12 people engaged. No signups.

In the end the MVP landing page had hundreds of open source developers visit and nobody signed up to hear about the launch.

This is a big warning sign that nobody wants this product.

Invalidation #4 - competitors

At the start when I first had the idea I did some competitor analysis. Did anybody else have this idea already and was it working for them? People often see competitors as a bad thing, but usually it just means there is a healthy market already where you can offer a differentiated product.

I re-discovered the story of Caleb Porzio who had grown his own GitHub sponsors. I found his MVP of a similar idea https://sponsorsyrup.com and remembered that I actually signed up for this thing.

It made me wonder why he hadn't posted any progress updates. Why had it not launched yet? I had never received any emails about it. I couldn't help thinking, is it because Caleb didn't get the interest he hoped for?

I also discovered https://onlysponsors.dev which is a similar idea. Eduardo has 84 sponsors on GitHub, but are they sponsoring him for this or for his work on Vue.js? From what I can tell there are not many people posting on the site.

This research helped me think about differentiators. What would I do differently? These projects both appear to be closed source but I would stay open source. I would also position it differently from Only Sponsors, and offer different features.

In the end this is an invalidation. Both of these people have huge followings on Twitter and GitHub and they have existing sponsorships. Only one of them has shipped and there doesn't seem to be much activity. With my small audience it would be a lot of work on the marketing and distribution side.

Invalidation #5 - target audience

I contacted some open source developers I know who have substantial sponsorship on GitHub. I even contacted devs who explicitly said they have this problem. There was some interest but not huge, and some strong warning signs:

Dev 1:

Currently I don't have any ideas for "sponsor" only content. I tried this for a while with my videos and secret links, but I gave up on that

When I tried something like this with the videos eventually I felt like I was spending too much time on pleasing sponsors with extra stuff rather than my OSS, so that would be one reason not to go there. When I asked my sponsors about this, most of them said: we are sponsoring for your projects, not for the perks, so you don't have to spend extra time on those perks.

As for special content: I did a few "hidden" videos but eventually I also just wanted to share that with everyone. I guess I'm just bad at keeping things a secret or away from people.

Dev 2:

I have done basically nothing to promote it. probably should be doing more on that front but its mostly organic

no rewards no. can't think of anything useful

Dev 3:

I’ve thought this through a few times and talked to a few other maintainers a while ago. My first impression was: The target group (open source devs) love to build things on their own and they don’t have money to spend. That’s both very tough to deal with. Also, I know some teams failed with similar products (probably for that reason).

This is some great feedback. These people are the exact open source maintainer target audience with existing GitHub sponsors. They are telling me no, we don't want this, and other people have failed at it already. Strong invalidation.

Conclusion: invalidated

The strongest message is the signups. Remember at the start I said I was aiming for 100 signups in two weeks? Only four developers signed up to hear about the launch. Three of them are friends and one is my brother.

My hypothesis was "this idea is so good I will get 100 signups in two weeks". Now I have strong empirical evidence to falsify it.

So this isn't the right app for me to build. It could still be a valuable idea for someone. The right person with a high level of passion and commitment might be able to make it work. Maybe Caleb or Eduardo will succeed with it (and I hope they do).

For me though it is nice to have given the idea a good chance and see it through to invalidation. Running this experiment was fun. Now that I have invalidated it I can skip the pain of building all the features only to discover nobody wants it. I feel very good about that!

21.10.2021 07:17How I invalidated my best startup idea
https://mccormick.cx/news/entrie...

Bootstrapped Apps Update

https://mccormick.cx/news/entrie...

Bootstrapping a one-person software business is a long and winding road. I have the luxury of time and resources on this quest but I know others aren't so lucky. I'm interested in uncovering the secret sauce to launching businesses that work. I love open source and I want to open the sauce, pop!

I have three developments to report. Two experiments and one free tool you might find useful. Here goes.

Experiment #1

First off I want to tell you about an experiment which was a raging success! It was a raging success in the sense that I got an experimental result. The result was I was not able to exclude the null hypothesis. In other words it failed completely.

The Dopeloop Melody Generator online app is now receiving about 9k visitors per month. Most of them come from organic search for terms like "melody generator". My hypothesis was simple: some percentage of those 9k visitors will pay for a native version of the app.

It took me a while but I finally managed to get the paid version of the app in the Google Play store last week. I put a banner on the free online app linking to it and a sales page explaining the new features (mainly "download melodies as a wave file"). Then I went to sleep.

Here's what the banner looks like at the bottom of the app:

screenshot.png

The next morning I woke up to the first sale. Success! People were clicking through. If I even got one sale per day then this experiment would be worthwhile. It would prove to me that search-engine-to-product-purchase is a reliable channel.

Alas, there was a bug in this first version, and that sale was refunded soon afterwards. I fixed the bug and uploaded a new build.

One week later and there have been zero more sales. According to the stats very few visitors are clicking through from the online app to the paid app. Why? Are the features of the paid app not compelling enough? Are they on a different platform or OS? Are they just disinterested passers by? Now my task is to iterate on this channel and figure out the answers to some of these questions.

Experiment #2

Ok now for an actual success. Not a raging success but a more hopeful data point.

Last year I released a sci-fi roguelike game called Asterogue. The game took me 1.5 months of part time development to make. I marketed it by tweeting development updates, writing blog posts, and launching on a bunch of different sites and forums.

For my first real commercial game it was a moderate success. It had 38 sales on Android and 27 sales of the Windows version and made about $500 AUD. I was pretty happy with this outcome because I was fully expecting a total flop.

Games are generally bursty. All of the sales are made at the start and they drop off rapidly as the novelty factor wears off, and Asterogue was no different. One thing I kept noticing was the Android version was getting a trickle of ongoing users who were giving it five star reviews.

Super clean and streamlined Sci-fi roguelike without all the cryptic baggage. Definitely a hidden gem that I hope gets more exposure.

Super clean and streamlined Sci-fi roguelike without all the cryptic baggage. Definitely a hidden gem that I hope gets more exposure.

Despite some rough edges people seem to genuinely enjoy the game. This particular review got me thinking. How could I get more exposure for this game?

What I did was put the game on sale for $0 free free free for one week. I have read about other developers doing this and getting a burst of new users. My hypothesis is this: if I can get a burst of new users and exposure, the game will get a second wind of actual sales once the free period ends.

So far I have one day of data and the results have exceeded my expectations. More than 1000 new users on the first day of the sale. None of my games has ever had this many downloads before. Even ones that I gave away for free from the start.

I guess people really like free stuff. Even moreso, they like stuff that used to be paid and is now free. Zero-dollar sales are a great way to drive traffic to your project.

I don't know yet whether this will mean more people buying the game when the sale is over. Will soon find out.

Oh by the way you can still get the game for free if you want to try it:

A free tool for prioritizing

There are always too many things to do. There are more things on your TODO list than you can do in a lifetime. I used to get all tied up trying to figure out what to focus on. Paralyzed by indecision I'd end up not doing anything at all.

The RICE framework is a method of helping you prioritize. The formula is ( Reach x Impact x Confidence ) / Effort. I made a tool you can find at riceprioritization.com that uses this formula to help you rank your priorities.

You can use it on daily tasks, long term goals, or any other type of time/cost option you're considering. It's completely free and open source. Hope you find it useful, and any feedback is most welcome!

24.9.2021 03:18Bootstrapped Apps Update
https://mccormick.cx/news/entrie...

What I'm working on right now

https://mccormick.cx/news/entrie...

We're just over half way through 2021 so I thought I'd post an update about the projects I'm working on right now.

tl;dr: music apps, a new game, and a micro-SaaS app.

e14731676178847bb6de32b9a97dcc92.png

A few months back I was feeling frustrated. I had a bunch of projects on the boil and I kept switching between them. I'd switch to whatever was most exciting at any particular moment. I felt like I wasn't making any progress. Context switching is the mind killer.

For a while I tried the 3-month-focus technique from this blog post. I couldn't sustain it. I'm too easily distracted by shiny new projects. I enjoy the thrill of chasing new ideas. Most of them don't pan out but some turn up genuinely interesting results, so I'd like to keep that optionality in my life.

I think I've finally found a balance. Shiny Object Syndrome versus getting things shipped. This strategy has been working well for me this year. What I've done is break down my projects into different areas of interest and then allocate one day to each area. I came up with these major areas of interest:

I assign one day each week where I only work on the one area at the exclusion of the others. I do the most impactful (or interesting) task in each area on the assigned day.

There is overlap between these fields. I am selling my music apps online at the moment so "Music Tech" is also part of "Indie hacking". One of my music apps is open source. Any work I do on decentralized technologies is part of "open source" too, etc.

Here was my side-project schedule for most of this year:

Here is what my schedule looks like now:

At the moment there are two days of game dev because I am crunching to finish Smallest Quest. I should be done in a month. Then I can give Friday back to open source software maintainance.

There's also client work of course. I work exclusively on client work on Tuesday. On the other days I do client work too, but I also give time to my side projects.

This schedule gives me enough variety to hold my interest and at the same time I feel like I am progressing each area. Here are the actual projects I am focusing on in each bucket right now.

Monday: PO LoopSync & Melody Generator

music-apps.png

I just shipped a big update to my pocket operator Android music making app. I've been working on this app for months. Last year I shipped an SLC and recently I've been pushing updates with new features.

Now that I have shipped that, I will switch to working on Melody Generator. People have been asking for features, and one specific feature I want myself is audio export. The biggest thing to do here is ship native apps. Melody Generator gets around 300 visitors per day. I am hopeful that some of those 300 visitors will convert to paying customers when I release native apps.

I also need to ship iOS ports of both of these apps. I'm working on getting the tech set up to do that.

8 bit music maker

screenshot.png

I've been tinkering this fun little music app called 8 bit music maker. It's a tiny tool for making 8 bit chiptune music. I'm not actually work on this on Mondays - I squeeze dev in between other projects. I've been having a lot of fun building this and it is just about at MVP. If you want to follow development of this app I am posting short updates on YouTube.

Wednesday & Friday: Smallest Quest

fightey.gif

Last year I released a sci-fi Roguelike called Asterogue. This year I'm building Smallest Quest. It's a new roguelike with the aim of being friendly enough for kids to play. My kids are pretty into turn based games which is what inspired me to make this. I came up with an asset pipeline that lets me hand-draw everything in the game and this has been fruitful and very also very satisfying work. It is nice to be able to just doodle out whatever asset I need in the game. I'm aiming to have this ready by the end of August.

If you want to follow progress on this project I post updates on a Twitter thread here. This is part of a new game development experiment I'm running with 3 friends. More on that later.

Thursday: TweetFeast

screenshot.png

My small success with selling apps and getting search engine channels working has convinced me to build another subscription software service. I've built two SaaS products already and both of them flopped. One was more successful than the other, gaining two customers, but neither took off in any meaningful way. There were a couple of things I didn't understand before:

I think I have answers to those points with my new project. TweetFeast helps you download Twitter data without code or API keys which is a pain point for people who need to do analysis on Twitter data. There are products that do this already but not very well. Lots of people are searching for Twitter sentiment analysis solutions, so I am going to use that as a wedge into the market. TweetFeast provides sentiment analysis of tweets out of the box.

If you want to follow progress on this micro-SaaS project I have a Twitter thread where I post updates about it.

I'm using ClojureScript

Not all of my projects are built with ClojureScript but that's the direction I am moving in. Running ClojureScript "full stack" in the browser and on the backend in Node has been delightful, thanks to shadow-cljs. About half of my projects are now fully ClojureScript.

So that's everything I'm working on at the moment. I would love to hear from you if you have any comments or feedback.

Thanks for reading!

21.7.2021 10:31What I'm working on right now
https://mccormick.cx/news/entrie...

How I beat Google at their own game

https://mccormick.cx/news/entrie...

Recently I hit a milestone. My procedural melody generator became the number one result for the Google search "melody generator". Even better, I beat out Google's own product "Chrome Music Lab".

f124fe00baa5c6e4310380e041ba439a.png

Ever since I set my sights upon the "melody generator" keywords I knew it would be an epic struggle. I knew that Google themselves held the top position. I knew that I would have to pull out all stops and hack at my best to attain the prize.

Well I did it. Google has 135,000 employees and I vanquished them all. I beat them at their own game. I climbed the greasy mountain of Google engineers and stood upon their faces with my heels in their eye sockets and my toes filling their nostrils and I thrust my flag aloft and howled victory into the wind. Search result perfection.

I know this won't last. Their internal systems will figure out why their product isn't number one and get it back up there. They'll stick a team on it with laser eyes peering machine-like from behind Google glassholes as they methodically dismantle my indie challenge.

They'll change the copy, or the product, or the algorithm. Like a scene from a Miyazaki movie I'll be swallowed up by the writhing mass of engineers and my software will be snuffed out like an unwanted kitten. This too shall pass.

I am at peace with this. All wins in life are but a momentary flash of brightness that quickly fades. Yes they will take away the ranking, but they can never take away the glorious memory. It's our story to tell to our grandchildren by the light of the scanlines. The noble tale of that one time a lone music tech hacker reached the top of Google and out-googled Google themselves. Perfection is possible my friends, it is simply transient.

PS To those friends of mine who work at Google, I hope it's clear this piece is tongue-in-cheek. Sorry about stepping on your face.

google-step.png

7.6.2021 12:14How I beat Google at their own game
https://mccormick.cx/news/entrie...

One million people saw my dumbest tweet

https://mccormick.cx/news/entrie...

The word "meme" was coined by Richard Dawkins in his book The Selfish Gene. A meme is an idea that replicates itself from mind-to-mind, just as a gene replicates itself through organisms. The word "meme" is itself a meme, and it is spreading from my mind to your mind through the medium of this post.

We are in the midst of a pandemic caused by a real virus, and simultaneously, we are in the midst of a meme pandemic. Never before in history has it been possible for ideas to spread as quickly through populations as they do now. Just like DNA, some memes spread faster and wider than others. Some memes are fit.

Meme fitness is not the same thing as quality of thought. As I write this sentence it seems almost too obvious to write down. Stupid ideas can be just as fit as good ideas.

The central thesis of Twitter seems to be that good ideas bubble up. That fit memes are good ideas. Are they? Do good ideas bubble up, and are the ideas that bubble up any good?

Sometimes the stupidest ideas bubble up.

A few weeks ago I was the lucky host to two memes that infected my mind and reproduced to create a new meme. Naturally, like the good little meme vector I am, I tweeted the new meme. Here it is.

screenshot.png

Amazing right? This meme is somebody else's joke graph with a Mandalorian quote slapped on it. It took me 10 seconds to make. One million people saw this work of genius and ten thousand of them "liked" it.

This is absurd.

I've cut back my time on Twitter. Social media is the faustian bargain. It says, "play the game and earn attention. All you have to lose in return is control of your own mind".

Social media are the Trojans at the gates of your mind. Their wooden horse is magnificent. It glistens with all of the fittest memes. I let those Trojans into the fortress of my mind, and it was a mistake.

The way that Twitter controls your mind is with the numbers. Metrics are an addictive drug. Twitter is literally an incremental game. Click button. "Bring value". Number goes up.

I knew it was bad when I woke up at 3am thinking about what to Tweet. The only reason to be awake at 3am is if my kid needs a hug or my friend needs another drink. I knew it was bad when I found tweet-thoughts invading my mind on my morning run. My morning run is sacred. It's the only time I have experienced anything close to a stroke of genius. Instead of that I was twarting unstopple mental flatulence. Horrendous mind-farts. What a waste.

Twitter is useful. It is a tool, like a pencil, or an axe. It would be stupid to let a pencil control your mind, and it does not make sense to get angry at an axe. You should use your tools, and not be used by them.

Twitter does not use me any more. I use Twitter. I use it logged out. I use it intentionally, and on the schedule I set, and for the duration I choose. I do not accept the cookie from Twitter. I browse a smart person's timeline once a month as if it were a blog. I post occasional updates about what I am building. I use it to spread the word about the software I am writing and then I log out before it ensnares my mind. I've turned off all of the numbers with Calm Twitter by Yusuke Saitoh. I could not give a rat's ass how many likes I get. It's amazing.

Today on my morning run I saw the perfect arc of a leaf gliding down to rest on the pond. The surface rippled outwards. I smelled petrichor.

This is the way.

9.5.2021 01:21One million people saw my dumbest tweet
https://mccormick.cx/news/entrie...

What I learned bootstrapping side projects in 2020

https://mccormick.cx/news/entrie...

In 2020 I made $693 USD from side projects. I contributed to several open source projects and made about a thousand commits on GitHub. I shipped a commercial game, a SaaS product, an IDE, two music apps, a handful of open source utilities, and a couple of websites. I love building stuff!

What I learned:

The side project income mostly came from:

The SaaS products I worked on failed to generate more than a few cheeseburgers. 🍔 Good thing I love cheeseburgers. If you're doing a first time project and you want some quick wins then probably don't start with SaaS.

Most games follow a typical pattern. They earn all the money in a big burst at the start and then the income falls off to zero. I've heard this from talks by other indie game developers as well. There is a novelty factor in games. This was true for my game too.

I mainly got the word out on Asterogue by building the game in public. What that means is I posted in multiple game dev forums about progress as I was making the game. It was a big effort and the launch went alright as a result (as opposed to most projects which launch to crickets). My fastest sales were from Asterogue but they've fallen to zero now.

The two music apps have done better. I've had 3 months of steady sales from them. They're making a combined revenue of about $80 USD per month.

Beat Maker 3-month revenue

Beat Maker 3 month revenue AUD

PO LoopSync 3-month revenue

PO LoopSync 3 month revenue AUD

I tried something new with PO LoopSync. I found demand before building. I studied a forum where enthusiasts of pocket operator devices hang out and I took notes. The notes revealed patterns in what they find important. Once I figured out what they want I made some mockups and asked them if it was what they wanted. When they said yes I built it.

This worked well and sales were good right from the start. Building stuff that people already want is more fun for everyone.

I've been building in public on different projects for a while now. It basically means I tweet about what I am making. Also, this very blog! When I launch something, people such as yourself can find out about it easily and tell others. That helps a lot to get the word out. Thank you for that!

I actually don't like building in public very much. It's uncomfortable. I especially don't like social media. So I've set up a system that allows me to interact with social media in an efficient and minimal way, but still be present. Maybe I'll post about that some time.

My problem up until now has been the bang-then-crash of launches. Initial interest and sales that then trail off. I've found two good ways to fix that this year. I guess you'd call these activities "marketing".

The first one is to post maintainance updates. Every time I update an existing project I try to post something about it. This probably seems obvious to a lot of people but it wasn't obvious to me. Each time I post an update I notice a small spike in interest and/or sales.

For example there was a gamejam for roguelike games this month. Leading up to the jam I made weekly updates to Roguelike Browser Boilerplate. I posted about them on the roguelike sub-reddit forum in their "saturday sharing" section. A bunch of new sales came in! Posting updates works.

The second thing I have tried is building useful websites that link to my apps. Marketing people call this SEO. I call it "building a useful website that links to my app". Not as catchy I guess. My friend Tobias showed me how effective SEO can be. There is also a good article about SEO by jdnoc that I learned from. Some people use SEO in a deceptive way but for me it's about giving my work the best chance to be found by people who are looking for it.

The first site I built is all about pocket operators. I used the research I did earlier to make a page about the most useful stuff. This got me on the front page of search for the phrase "pocket operator apps". Lo and behold one of my apps is a pocket operator app! So people find the page and then they can find my apps too. Here's a graph of the search volume on that site:

screenshot.png

Another site I built is a free web app for generating random melodies. It's a procedural melody maker that generates midi melodies you can download. There's a link from the free web app to my other apps.

The name for this is apparently "side project marketing". So my side projects are being marketed by my side-side projects. The free web app shows up on the front page of search for a group of terms around "ai midi melody generator". One of the apps it links to is a random beat generator so the audience is very similar. Here's the graph of search volume for the melody generator:

screenshot.png

So I think these two sites are a fairly steady channel where people find my apps. They're searching for "pocket operator apps" and "melody generator" and they find those sites and then they also sometimes click through and buy my apps.

There is one other way people are finding the apps. I have a free app on the Play store. It's an app for building music apps. So people who are already interested in music apps and development can find that, and then some of them also find my other apps.

The final thing I will note is that being open source has not hurt sales. Beat Maker is open source and I haven't open sourced PO LoopSync yet. So it's an imperfect A/B test but it's still useful. There isn't really much difference in the sales. There is basically only upside to being open source for somebody at my scale. People like it, and it builds trust, and it fits my ethics.

Anyway, I hope this is useful to somebody. I'll continue to post updates about my dev adventures and things I have discovered.

24.3.2021 23:29What I learned bootstrapping side projects in 2020
https://mccormick.cx/news/entrie...

A Hand-doodled Roguelike Tileset

https://mccormick.cx/news/entrie...

I've been working on a free hand-doodled roguelike graphics tileset called Doodle Rogue. It's an alternative to the standard console and pixel roguelike graphics tilesets out there.

I'm relatively new to drawing. It's taken a couple of years of drawing badly to get to the point where I am comfortable enough for a tileset. This post is to show you some betas of the tileset I'm working on, and also the art that has inspired this work.

Doodle Rogue work-in-progress sketches

Here are the tileset beta sketches.

Doodle Rogue scenery sketches

Doodle Rogue scenery sketches

Doodle Rogue item sketches

Doodle Rogue item sketches

Doodle Rogue character sketches

Doodle Rogue Characters sketches

Doodle Rogue mockup sketch

doodlerogue-mockup.png

There is a ton of work still to do on these. I need to redraw them as vectors and clean them up, and finsh drawing the remainder of the tiles.

Research & references

I used Pinterest to explore different reference styles for the tileset. Here are some of the artists I am using for inspiration and referencing. There are actually a lot more references than this but these are some anchor points that have stood out.

You can find my game-draw reference pinterest feed here if you care to follow along.

Dom 2d

bcf576cf19a7b5075d8cbfb70be1b06e.png

2277f9dc389942e29ce37969e583637a.png

3068953c09c073fefadd2203884b1bec.png

Dom2d has been a big inspiration for my drawing in general. I love the balance he strikes between quick-draw simplicity whilst still looking good.

Slowquest (Bodie H)

slowquest.png

I love the gritty detail in Bodie's drawings of items and dungeons. Studying his work has taught me a lot about texturing with lines.

Timecowboy

fb5d302f9953831353a3ac0bf976cc49.png

ea26c3c5a6c45d8e8747ac0fb34fb9b9.png

Timecowboy is a web comic artist.

Varguy

3b438d2dfcadecbb6c656fd7f8424287.png

Varguy has the rare skill of the ligne claire artists, combining seemingly simple shapes and lines into images that really come to life.

Mike Yamada

065373d751bf6c2b5606fa5ad2b4627d.png

21e97cdfde20f4ab5266003599439ad6.png

a0248a2048cf0640a8bceb0fd3d4fb8f.png

I found Mike Yamada's work when reading The Noisy Garage to my kids. I love his animal characters.

Moebius

3c56504ac0aec329ffcdb1e22e7b1c6c.png

790be18ced20bf0a9713ecc2d7441965.png

That's everything for today. I hope you've enjoyed these images.

30.1.2021 12:29A Hand-doodled Roguelike Tileset
https://mccormick.cx/news/entrie...

A procedural MIDI melody generator

https://mccormick.cx/news/entrie...

I've just released a melody generator that I've been working on for a while. It's a small web app that you can use to procedurally generate looping MIDI melodies and then use them in your own music.

The fractalesque algorithm it uses to generate melodies is one I came up with when I was writing a lot of algorave music a decade ago. The MIDI melodies are rendered to sound using the wasm port of Timidity by Feross.

Enjoy!

6.1.2021 07:26A procedural MIDI melody generator
https://mccormick.cx/news/entrie...

Random Hip Hop Beat Generator App Re-launch

https://mccormick.cx/news/entrie...

In October 2010 when the Android store was just a spring chicken, I built a minimal open source app that generates random hip hop beats called "Can of Beats". I had been getting into procedurally generated music and so I was able to build the app in a matter of a couple of weeks, and then I uploaded it onto the Android store just to see what would happen. It sold ok, paying our internet bill for a while, but as I had no idea what I was doing, I soon got bored of the low sales numbers and basically ignored it for 10 years.

Over the lifetime of the app it generated a couple of thousand dollars worth of sales. Knowing what I now know about the compounding effects of building things in public, I should have spent the 10 intervening years doubling down on that first trickle of installs, and shipped a ton of minimal audio apps, one after the other. Ah well, hindsight is 20/20!

They say the best time to start compounding any investment is yesterday. So in the spirit of that I've been working through November on some new minimal audio apps, and I'm also re-launching the original beat generator app with some new features!

Random hip hop Beat Generator Android 
app

Get it on Google Play

One feature I'm particularly excited by is the Pocket Operator sync. If you don't know what this means, don't worry, it's for a tiny niche of people who are super into it. If you are curious about what a Pocket Operator is, look it up, they're awesome!

26.11.2020 09:30Random Hip Hop Beat Generator App Re-launch
https://mccormick.cx/news/entrie...

Bootstrapping and Convexity

https://mccormick.cx/news/entrie...

Nassim Taleb has this idea of "convexity". There's a bunch of complicated maths but it's actually quite a simple concept once you get it. This concept is useful for indie hackers and people bootstrapping businesses, because it gives you a general strategy that works in the presence of high levels of uncertainty (i.e. real life).

Is your business idea any good? Is there a market? Is the timing right? None of these questions matter so much if you choose convex processes.

So what is convexity? If a process is convex it means it has a big potential up-side and a limited potential down-side. Take a look at this graph and imagine your project is a marble that is pushed randomly to the left or right.

Taleb convexity 
graph

If it is given a push to the left then it will roll down the slope. If it is given a push to the right it will go up the slope. You can clearly see that there is a maximum depth the marble can go if it goes left, but it can go much higher if it goes to the right. Given enough force to the right it's going to fly off and your project will make a million dollars! So the down-side is limited, but the up-side is unlimited.

If some activity maps to this graph then you can say it's convex and those are the types of things you want to do. What you want to avoid is activities or processes where the opposite is true: all pain and no gain.

One of Taleb's insights is that we should not worry about the direction of the push on the marble because we simply can't know it. It's an uncertain world and the force on the marble is effectively random. We only fool ourselves if we pretend to be able to predict it. How many times have you launched something or tweeted or posted and not had the result you expected? You're going to be wrong a lot. Convexity embraces being wrong a lot.

So how does this apply to bootstrapping a business? All you gotta do is make sure you are picking convex activities at each step of your project. Decide if a given process or activity or action has a limited down-side and an unlimited up-side, and do it if so. Do lots of these convex activities to uncover reliable up-side and run-away successes.

Non-convex indie hacker processes (bad)

Here are some examples of non-convex indie hacker processes that you should avoid:

Quitting your job to go all-in on your unprofitable side project. This is non-convex because the down-side is ruin. If things go wrong you end up on the street with no job and a failed startup.

Spending a long time building without shipping. This is non-convex because the down-side is a loss of an unbounded amount of your most precious resource: your time on this earth.

Taking VC funding. Contraversial I know, but I beleive this is non-convex because you are locked into one business, beholden to the whims of investors, and you have given up all optionality (another Taleb idea I'll discuss below). Basically there is unlimited down-side in being beholden to somebody else's goals and not being able to do anything about it because they hold the purse strings.

Taking out a bank loan before you have paying customers. Taking on debt seems to be non-convex generally because of the unbounded way debts can grow.

Convex indie hacker processes (good)

Retaining some freelance hours while you work on side projects. This is convex because the down-side is limited (you still get to eat if you fail) and the up-side is unbounded if one of your side projects takes off.

Time-boxing your development and shipping frequently. This is convex because the time you lose if you build something that nobody wants is capped, but something you ship might actually catch on.

Posting on Twitter, Hacker News, and marketing in general. Marketing is convex because there is only a small amount of reputational risk and you have to be reeeally annoying to reach that level. The unbounded upside is having your product discovered by a market that really wants it.

Building in public. This is convex for the same reason as marketing. If you fail, or look like a fool, it's just not that bad. Upside is exposure, reputation, and viral adoption of what you're doing.

12 startups in 12 months. This strategy looks insane but if you look at how it works out for somebody like Pieter Levels it is convex. It's common for people to not finish their 12 startups and often the reason is because they also have the option to exit early on success (see below). It's a safe way to practice Taleb's "systematic convex tinkering".

Optionality

Another property you want is optionality. Let's take the Pieter Levels 12-startups-in-12-months example. Here you not only have convexity but you also have the option to take one of the 12 startups and run with it. Nobody is going to care if you stop at startup number 7 because it became a raging success that demands your attention. This is in fact exactly what Levels did. He launched Nomad List and Remote Ok, and then when it was clear they were popular, he took the option and went back and pumped them up.

So you also want to do things where you have the option to retain any up-side that is achieved.

Marketing is another good example. Lets say you post on Hacker News ten times and nine of those times you hear crickets, but one of the times your post blows up. Optionality means capturing that attention somehow so you can use it again later. So for example you might have an email list that people can opt in to. When a post does well you capture the up-side by letting interested people subscribe to your list.

Side note: are successful founders just lucky?

Elsewhere Taleb and Ole Peters and others talk about the concept of ergodicity, and two different types of probability: time vs. ensemble.

Ensemble probability is when you look at all indie hackers, and then the sub-set of "successful" indie hackers, and then figure out the probability of indie hacker success based on those two numbers. When I wrote that post which blew up it was about the ensemble probability of indie hackers, so it is actually a bit misleading because ensemble probability is the wrong way to look at it.

Time based probability is when you take a single indie hacker running many experiments, and look at their journey over time. It's the probability that one of their business experiments goes big.

The safest place to be is in the second category, running many convex experiments over time with no chance of ruin on any given experiment. You want to take the time based view because that is the one you actually have in real life. You are one single person, not many people in parallel.

Some successful founder stories are actually only visible in the ensemble category. They made one thing, got the golden ticket, and made a million dollars.

When taking the advice of successful founders you would do well to look at whether they are ensemble or time based. Did they try a ton of different ideas before succeeding? Good. Have they had more than one success or just one? Time based successes are the ones you want to learn from because they are repeatable. Individual successes from the ensemble are less useful as they may be due to good luck rather than good processes.

Conclusion

Run many business experiments sequentially, cap the down-side (time/cost/ruin), retain the option to capture any up-side.

Good luck! You won't need it.

PS You have the option of following me on twitter to find out about stuff I'm making. ;)

McCormick convexity 
graph

Footnote: I hope I have understood Taleb's ideas correctly, but if I have made any mistakes they are 100% my own.

5.11.2020 03:32Bootstrapping and Convexity
https://mccormick.cx/news/entrie...

It's Asterogue Launch Day

https://mccormick.cx/news/entrie...

Since I was a kid I've always loved the roguelike genre. These procedurally generated mostly-text-based games have a wonderful depth and I've long been fascinated by the leverage and replayability you get from the procedural aspect.

Asterogue gameplay 
gif

For the past 10 weeks I've been working on my own roguelike. It's a sci fi game with tile graphics set in the interior caverns of an asteroid. I've had heaps of fun building this and I hope you'll have as much fun playing it.

Here's the announcement on Twitter if you care to help me out with a retweet. Thanks so much!

Enjoy!

30.10.2020 08:56It's Asterogue Launch Day
https://mccormick.cx/news/entrie...

Roguelike Browser Boilerplate

https://mccormick.cx/news/entrie...

Recently I launched this web based template project that you can use to make your own roguelike game. If you've ever wanted to make your own roguelike game and you know a bit of web development then this is for you.

I'm also recording a series to screencasts which show you how to customise the template to make your own game. It's a walk-through of the whole process, basically a graphical javascript roguelike tutorial which you can follow along with.

Enjoy!

31.8.2020 07:27Roguelike Browser Boilerplate
https://mccormick.cx/news/entrie...

Space Elk's Lounge Room

https://mccormick.cx/news/entrie...

DSC_0002.JPG

14.8.2020 01:50Space Elk's Lounge Room
https://mccormick.cx/news/entrie...

In The Wilds

https://mccormick.cx/news/entrie...

DSC_0037.JPG DSC_2425.JPG DSC_0052.JPG DSC_0046.JPG DSC_2454.JPG DSC_0053.JPG

22.7.2020 04:32In The Wilds
https://mccormick.cx/news/entrie...

Slingcode Is Out

https://mccormick.cx/news/entrie...

At the end of June I finally shipped Slingcode, the browser based code editor I've been working on for several months.

The response was overwhelming:

This has been a humbling experience and I'm grateful to every person who checked it out and those who gave me feedback. Thank you!

Since then I've been recording screencasts for getting started with technologies like React, Vue.js, and SVG live-coding in Slingcode. You can find the screencasts at slingcode.net/screencasts.html or on the YouTube playlist embedded here:

I hope this tool and the screencasts are useful to you.

Enjoy!

17.7.2020 01:51Slingcode Is Out
https://mccormick.cx/news/entrie...

Un-template Python HTML Library

https://mccormick.cx/news/entrie...

Un-template is a minimal Python library to modify and render HTML. The idea is to start with a pure HTML document, choose the bits you want to modify using CSS style selectors, and then change them using declarative Python datastructures. This means you can use pure Python to replace sections of HTML and do all of the "template logic", similar to how declarative front-end frameworks work.

Install it with pip install ntpl.

Here's an example. It's a Django view function that returns a web page. A file called index.html is loaded, two HTML tags with IDs content-title and content are replaced with new content. A link tag with ID home is replaced completely with a new link tag.

from ntpl import slurp, replace, replaceWith, render

template = slurp("index.html")
faqs = markdown(slurp("FAQ.md"))

def faq(request):
    html = template
    html = replace(html, "#content-title", "FAQ")
    html = replace(html, "#content", faqs)
    html = replaceWith(html, "a#home", render(["a", {"href": "/"}, "home"]))
    return HttpResponse(html)

That's basically all there is to it. You can use it like this instead of Django's native internal templating language.

You can get the source code on GitHub or install it with pip.

It is inspired by Clojure's Enlive and Hiccup, this library uses pyhiccup and Beautiful Soup to do its work.

2.6.2020 11:36Un-template Python HTML Library
https://mccormick.cx/news/entrie...

The Zero Customer Heuristic

https://mccormick.cx/news/entrie...

When you're building some MVP or SLC it's tempting to over-think technical choices early on. It's tempting to build in all kinds of features and infrastructure.

"People might want a PDF," you think to yourself, "so I'll also build and deploy a PDF rendering server!"

"I'll need a way to measure everything my ten million customers are doing at scale so I'll deploy this elastically scaling mega analytics doodad framework server and hook it up to every user action in my app!"

cricket.gif

It's much easier to imagine new features than it is to actually build them. Before you know it there is a vapourware castle shimmering on the horizon.

I've found a useful thought pattern to combat this type of over-engineering. For every technical question just think to yourself:

What will my zero customers think of this?

When you are building the first version of something there are literally zero users. Unless it is the core function of the thing you are building, making a PDF rendering server or deploying a giga-scale analytics system is not what you should be doing. Your zero users don't care about those things! Getting even one person to actually use your thing and tell you if it's good or not is what you should be doing. Talking to people and asking them what they need is what you should be doing.

Let's protect the most scarce resource of all: our own time. At the start of the project let's make a bee-line towards shipping the best possible implementation of the core function of the software so we can find out if its useful and good.

8.4.2020 06:27The Zero Customer Heuristic
https://mccormick.cx/news/entrie...

Slingcode: Personal Computing Platform

https://mccormick.cx/news/entrie...

Recently I was teaching one of my kids a bit of web coding. This is way more complicated than it should be. There are so many moving parts - configuration, build systems, editors, hosting requirements, certificates etc. just to get a simple web app running. Why?

I thought back to when I was learning to code with my mother on our Apple IIe. The computer was ours. The code was ours. The data was ours.

fdf0e437a027e6f3f3622f67d65e0ef0.png

I thought back to shareware. First diskettes and then FTP. I thought about my first website and those first Multi User Dungeons. Nethack and newsgroups and bulletin board systems. Trading ware with friends. I remembered running Linux for the first time on a Pentium. The freedom and power of getting to do what you want with your computer and a network connection. How can we take our computers back?

I started thinking about what a personal computing platform would look like today. A platform that a kid could jump right into and start coding. A platform where a kid could build cool stuff without asking for permission. Systematic convex tinkering with computers. Where they could own their software, and their data, and their device.

A way to make, run, and share web applications without needing site hosting, SSL certificates etc. An app repository and a text editor in a single web app. A way to share apps peer-to-peer, directly between trusted friends, family, and associates.

Then I re-read this:

  • Personal computers – in the original visions of many personal computing pioneers (e.g. many members of the Homebrew Computer Club), the PC was intended as personal property – the owner would have total control (and understanding) of the software running on the PC, including the ability to copy bits on the PC at will. Software complexity, Internet connectivity, and unresolved incentive mismatches between software publishers and users (PC owners) have substantially eroded the reality of the personal computer as personal property.

This desire is instinctive and remains today. It manifests in consumer resistance when they discover unexpected dependence on and vulnerability to third parties in the devices they use.

Nick Szabo in Trusted Third Parties Are Security Holes.

This is the important thing. It has to be self sufficient. It has to work properly whilst depending as little as possible on third parties.

c02481139c1de01523209cad3767a3ff.png

Last Monday I started building this. It's called Slingcode. With it, you can write, run, and share your own web applications directly in the browser. You don't need any build system, hosting provider, SSL certificate, or any thing else. You don't even need an internet connection. Just a web browser and the single HTML file containing the Slingcode web app.

Stay tuned.

15.3.2020 12:23Slingcode: Personal Computing Platform
https://mccormick.cx/news/entrie...

The Forest Moons of Yendor

https://mccormick.cx/news/entrie...

Last week I entered the 7 Day Roguelike Challenge. I used the the px3d game engine with Blender and ClojureScript to build a game prototype.

Screenshot of the game

forest-moon-bear.gif

Screenshot of the game

Screenshot of the game

14.3.2020 05:17The Forest Moons of Yendor
https://mccormick.cx/news/entrie...

Sub-Dunbar Software

https://mccormick.cx/news/entrie...

I've been thinking about this idea lately of software written for small groups of mutually-trusting humans, like families. I like to think of this type of software as "sub-Dunbar software", named after Dunbar's number:

suggested cognitive limit to the number of people with whom one can maintain stable social relationships

Sub-Dunbar software is any software that is optimized for groups of this scale and intimacy. It's cozy.

337754d47c018b8c5e0b1378e4791c9b.png

Photo by Dan Gold on Unsplash

Here's some reading related to this idea:

it would not surprise me if we moved away from "public square" online dynamics to "small intimate online dinner party" online dynamics.

Tommy Collison

I didn't know it at the time, but todoMini is an example of this type of software. I wrote it for my family, not for scale, and it won't change. I hope to build more sub-Dunbar software.

a146ba6154b224489a665333822b241a.png

Photo by Kelsey Chance on Unsplash

19.2.2020 03:16Sub-Dunbar Software
https://mccormick.cx/news/entrie...

Linux Conf AU 2020 Talks

https://mccormick.cx/news/entrie...

Last week I was on the Gold Coast for Linux Conf AU. I gave two talks:

Piku: git push deployments to your own servers

px3d: a free software browser based pixel 3D nano-engine in ClojureScript

Other talks

I've compiled a list of talks I got a lot of value from which you can find on Twitter.

gold-coast.jpg

Enjoy!

22.1.2020 03:12Linux Conf AU 2020 Talks
https://mccormick.cx/news/entrie...

The Only Truth Will Be Cryptographic

https://mccormick.cx/news/entrie...

Photographs are easy to fake. So much so that there is a turn of phrase to describe it. People say something is "'shopped" when they are skeptical regarding the veracity of an image. This refers to the image editing program Photoshop which is often used to modify images. For example the magazine industry routinely modifies the photographs which appear in their pages.

8c1849c1304d48d92e635e494b919288.png

Recorded audio is even easier to fake.

In recent years we're discovering that even video, with its high information bandwidth, is easy to fake via carefully trained neural networks.

Seth Godin notes that anybody now has the ability to generate photos of completely fake people:

it's worth confirming the source before you believe what you see

-- Seth Godin

Where does this leave us? How do we reliably confirm the source? Physical reality prevents us from receiving most information first-hand. If most information that is not first-hand can be faked how can we ensure authenticity?

Cryptographic signatures will save us

The answer is good old cryptography.

One way to authenticate media with a high degree of certainty is to have it cryptographically signed. This provides a level of reputational consistency. If a president signs every speech they make with a certain cryptographic key then you have a way to check that the president who gave the first speech very likely is the same president gave the latest one.

Much more so than relying on indications of fakeness from the item of media itself. Additionally, the slightest alteration of the media will render the signature invalid and everybody will be able to see that it has been tampered with.

How far away are we from public figures cryptographically signing their statements? From people's phones signing the photographs they take? From organisations routinely signing blog posts, tweets, and everything they output into the world?

8.1.2020 03:20The Only Truth Will Be Cryptographic
https://mccormick.cx/news/entrie...
Subscribe

🔝

⬆️

⬇️