Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
1

Custom Gradients in Kidz Fun Art

from SOS [alt+shift+b] in programming

For a long time in Kidz Fun Art, the tablet app I built, you could draw with a rainbow brush, which is the favourite feature of many people. I noticed my eldest daughter trying to use it in such a way so to only use a subset of the colors and it was really awkward, so I added this fun new feature where a child can intuitively design their own gradient. It was a fun challenge to make it both powerful but still intuitive enough for a 3 year old. I think I achieved it, what do you think?
17th Jan 2025

Stay updated

Get a weekly newsletter with the top 5 articles worth reading every week.

More from SOS

A new home for all my apps: apps.chofter.com

Say hello to apps.chofter.com, the new home for everything I build. The speed at which I ship new things has massively increased with the advent of AI, and I’ve been encouraged by the fact that, as I approach my sixth decade, new ideas for useful & fun things to build still come at me apace. These no longer fit on my personal homepage, which is a combination of work experience, interests, things I made long ago that are no longer relevant (but still interesting), and of course, the many apps & tools I have out there in the wild right now. The site was 100% built using Claude Code, which did an amazing job of inspecting all the various websites, app stores and code bases and constructing a site in 30 minutes or so. I had to push it to make the site more SEO friendly, pre-rendered to HTML rather than over relying on client side rendering, but that was it. So there we go, enjoy the delightful and hopefully useful apps that I’ve already built and will continue to build in the future

a week ago • 1 votes
Clean your Mac & Windows/Linux PC with Disk Space

TLDR: Download the best disk cleanup app in existence from diskspace.io. A fun experiment in writing the same app three times (well kind of 4 times) with AI, and being as optimised and OS native as possible. My Mac recently filled its 1TB drive and my quest to find out where the space had gone was very frustrating. The existing tools for tracking down disk space were slow, clunky and it was impossible to use them until their 30 minute crawl of my hard drive completed. I decided to fix this by building a new app called Disk Space, which takes the great features of my favourite old time app Disk Inventory X, makes it blazingly fast and adds much improved file/folder deletion aesthetics so that you can clean up safely and quickly, as well as highlighting recently created files so you can find what changed more quickly. Let’s get nerdy The three versions, Mac, Windows and Linux, are all mostly independent, with just a little shared C++ code. My goal was to make each app as small, fast, as native to its environment as possible. This meant not using any of the more common cross platform libraries, and leaning on Claude to do the work. MacOS The MacOS version of Disk Space is written fully in Swift, with no dependencies on other libraries. This was the first version I built. It detects how many CPU cores your machine has and optimises itself to maximise the throughput. The size of the installed app is just under 1MB, with about 300KB of that being multiple sizes of the icon, so the app itself is just over 600KB in size. Not bad! Releasing desktop apps for the Mac is actually not too bad an experience. You can choose to put it in the App Store, but then it can be a pain to release a new version. Apple offer a free notarisation service that signs the built app with your Apple Developer credentials, and it works well. For this reason, you can simply download the MacOS version of Disk Space from the site. Windows The Windows version is a direct port of the Swift code to C++. Similar to the MacOS version it optimises its operation based on the number of cores, but it also checks if the drive being scanned is a spinning disk or a solid state drive. If it’s a spinning disk there’s no point running many threads against it, it’s physically incapable of responding, so it caps the number of threads. Since it it just pure C++ with no dependencies pulled in, the installer is just about 600KB, pretty cool. Releasing apps on Windows these days generally means you are forced to either release through the Microsoft Store or pay for quite expensive yearly fees to have your app notarized. Without this, the user will be shown very scary warning dialogs, making the app very hostile to use. Since this is a small free app, I went the Microsoft Store route. Linux The Linux version shares some of the C++ with the Windows version, especially the code that draws the multi-coloured tree map on the right. Similar to the Windows version, it checks the hardware of your storage to best optimize itself and otherwise builds the UI using Linux native code with almost not dependencies. The first version Claude recommended depended on the GTK libraries for the UI, which was convenient, but it meant that using the app on any system that didn’t include those libraries would force the user to download hundreds of megabytes just to get a 500KB app running. Luckily, within an hour Claude had completely rewritten the app to be almost fully self contained. This means that the Linux app, which is packaged as an AppImage file, is just about 600KB all in, and you can simply download it from the site. Epilogue This was a fun experiment in building an identical app for all three operating systems while keeping it as native and optimised as possible. The hardest part was the hardware setup required for testing. I now have on my desk: My MacBook Pro (my primary machine). I do most of my work on this, run Claude and do all testing of the MacOS app. A small but powerful Windows Desktop. This is pretty great, as not only can Claude build and test Windows apps on it, it can also build and test (to some degree) Linux apps too. I use this as the main machine for those two operating systems. An ancient, 2009 MacBook Pro 17″ that I installed Linux on just for building this app. I use this for testing the Linux version on a real machine, not just on Windows WSL. It works relatively well, but with just 4GB of memory I won’t be doing any development on it any time soon. Still, it’s great to make use of the old hardware instead of throwing it away – I knew there was a reason I hung on to it! … a lot of messy crap I need to tidy up. Any day now…. I can’t believe you read this far, thanks! Now go get Disk Space from diskspace.io, your hard drive will thank you

3rd Sep 2026 • 1 votes
Huge under-the-hood upgrade to Animations in Kidz Fun Art

One of the absolutely coolest features of Kidz Fun Art is the ability to create Animations. This was initially inspired by watching my nieces creating an animation on another Android app, so I focused purely on the creation case. This worked well, where most animations were under 50 frames in length, as it takes time to create them. I later added the ability to import Gif images, since it was a relatively simple change – parse the Gif image into frames, save them and boom, you can edit and re-export it. However this exposed a problem: Gifs can be huge, and Kidz Fun Art didn’t work well with thousands of animation frames of data. I wasn’t sure where the bottlenecks were, but at some point the app would just crash if the Gif was big enough, of if the user created an animation over 100 frames or so. This is all now fixed, and the app comfortably imports multi-Megabyte Gif images and provides a better user experience when some operations (like deleting hundreds of frames) are not instant. Using AI to find performance issues & subtle bugs While I have always written the vast majority of the code for Kidz Fun Art by hand (using AI for more complex things like WebGL shaders and C++ based paint brush simulation), it’s been invaluable recently for reviewing and testing the code. I asked Claude Opus 4.8 (the current frontier model as of July 2026) to identify the bottlenecks and it did a great job. There were multiple places where I initially wrote a function to take an action on a single frame that would then save the full animation, but later reused this function in a loop over all the frames. This caused the full animation to be saved hundreds of times in a few seconds, crashing the app. The list of frame thumbnails at the bottom of the screen rendered all thumbnails up front. This is fine for 50 but not for 1000. When saving a Gif, the file was far too large. This is because it wrote each frame in its entirety to the Gif image. The Gif standard obviously supports just writing the pixels that changed from the previous frame, and I wasn’t doing that. Deleting a large animation would take multiple seconds, with not user feedback. This was fine for other media types as they are more or less instant, but in this case it let the user click around the app, then have unexpected things happen 5 seconds later. It made the app feel broken. Gifs that stored some frames with the option to simply restore the previous frame were not handled, making the import of some images be inaccurate. There were a number of places where race conditions between multiple asynchronous actions could cause bugs. The AI was very good at finding places where my code should have been waiting for one to complete before beginning the second. There were multiple places where memory leaks occurred, specifically with not cleaning up event listeners. It found them all. When leaving the app open for days or weeks at a time without a reload, this could have been a real problem. What is better now? Animations now scale up to very large sizes, with instant access to all frames. You should be able to import basically any reasonable Gif image, and it will be exported in a highly optimized manner, with perfect colour matching per pixel. When deleting a large animation, you are told it is being deleted immediately, so it’s obvious the app is doing what you asked it to do. When importing a large Gif, you are shown a dialog telling you that it is happening, and blocking you doing other work until that completes. Many subtle bugs fixed. The frame list is fully virtualized, and scales up to essentially any size of animation. We have tests now! Another great use of AI is for writing tests, in this case laboriously creating dozens of large integration tests. These were invaluable in both validating the deep changes being made and in finding more edge cases and race conditions. Kidz Fun Art now has full end-to-end tests covering animations, layers, comics, cards, drawing, colouring, handwriting, maths and puzzles. I hate writing these, but AI doesn’t get bored, and I look forward to adding more and more regression tests in the future to keep quality high for all the world’s young artists out there.

2nd Jul 2026 • 1 votes
Spirograph fun in Kidz Fun Art

For a long time I’ve wanted to add Spirographs to my (awesome ) drawing app for kids, Kidz Fun Art, and today it’s ready! There was quite a bit of fun mathematics in getting it to feel natural and work with all sizes of circles, but it seems to have worked out very well! You can move the Spirograph around, change the size of the outer and inner circles, and draw in any colours you like. Read more about it on the main blog post here, try it out on the web at https://kidzfun.art , get it for iPad here, or download for Microsoft Windows here.

12th Jun 2026 • 1 votes
Mazers – a WebOS app rises again on iOS & iPad

Way, waaayyy back in 2010, I built a fun little game for the Palm WebOS series of phones called Mazer. I was happy with it, loads of people downloaded and played it, and then WebOS died. I recently found the source code again, and with the help of Claude AI I rewrote it to run on iOS and iPad! Get it for free today from the iOS App Store. (Android version coming soon) There are four different game types You can find your way around a simple maze, or race a terrifying fiery ball to the finish. Over 120 hand crafted obstacle courses to get around with worm holes, force fields, evil fiery balls, and more. My personal favourite, a Pacman like maze where the four ghosts chase your little ball around as you try to open the portal and get outta there!

8th Apr 2026 • 1 votes

More in programming

CalVer 26.0: 10th Anniversary Edition

A decade ago, a little bit of history was made. I didn't realize it, but a colleague made a great point, one of those real mind-changing points that seem too obvious to admit same-day. But, the next day, calver.org was born. At the time my team maintained the Python infrastructure for eBay and PayPal, and we were stuck deciding whether we were really ready for a "major" 1.0 release. Semantic Versioning was the only game in town and "major" means "big", right?! Thankfully, a wiser colleague mentioned: Ubuntu and Twisted don't struggle with version number debates. They slap a date on it and keep shipping. In fact, their date-based versions were even better because you always knew where it stood, in terms of updatedness and support. The only problem is that no one really knew about it. Somehow, this problem solving versioning alternative, arguably as old as history itself, had gone nameless for millenia, conspiring to make me feel foolish in an office meeting. Never again! Ten years of adoption Fast forward 10 years, we've seen CalVer adopted by Apple, Nvidia, JetBrains, and countless others. (We have a timeline!) The site may have more inbound links than any other project of mine. Apple made the biggest jump, at WWDC 2025: iOS went from 18 to 26 macOS from 15 to 26 watchOS from 11 to 26 and visionOS from 2 to 26 All landing on one, consistent number like a car's model year. I still remember the texts from the Venn diagram fanbase of my friends who love Apple and reasonable versioning. No such texts from when NVIDIA announced calendar versions across the GPU Operator, RAPIDS, and its monthly NGC containers, but still very cool. Open source, too: Home Assistant, pip, CockroachDB, and yt-dlp all ship on dates, with plenty more on the users page. The conversation even reached the language core; PEP 2026 proposed versioning CPython as 3.YY, and it almost happened, too. And it's never too late, time marches on! Fixing the notation But I don't think I got every detail right from day 1. That's the main motivator for CalVer 26. It's high time to start righting a couple idiosyncratic token design choices, starting with some additions: Meaning Before 26.0 26.0 Full year YYYY YYYY Short year (6, 16) YY YY Zero-padded year (06, 16) 0Y 0Y Short month (1 ... 12) MM M Zero-padded month (01 ... 12) 0M 0M Short week (1 ... 52) WW W Zero-padded week (01 ... 52) 0W 0W Short day (1 ... 31) DD D Zero-padded day (01 ... 31) 0D 0D Seeing double First, the doubled letters. From the first version (16.6), MM and DD meant the unpadded month and day, which reads backwards to anyone who knows date formats (ISO 8601's YYYY-MM-DD, Java, moment.js, day.js), as some community members correctly pointed out. I was ready to flip them, until I checked what people actually use: most projects with a YY.MM.MICRO badge (conda, Twisted, Ansible's tooling) don't pad, and more than a dozen other version management tools (like bumpver and bump-my-version) implement the old meaning. So, it's too late to flip MM's meaning. Instead, 26.0 deprecates it and offers a more explicit and hopefully clearer option: M is the short month, 0M the padded one, and MM is a technically-retired synonym for M. In case you're wondering, the explicit 0M was me being overinspired by Ubuntu's approach, perhaps: 6.06 pads its month but not its year, and YY.0M says exactly that. To pad or not to pad I think it's worth a detour into why padding is even a thing anyways. It's become important now that new ecosystems have emerged that enforced SemVer formatting semantics, and I wanted clear guidance about on the spec site. SemVer forbids leading zeros outright, so Cargo rejects 26.04.0 and Go modules reject v26.04.0. Even Python's packaging spec normalizes leading zeros away, so you can tag 2026.08.19 if you want, but PyPI will still show 2026.8.19. NVIDIA's GPU Operator docs put it this way: "Zero padding is omitted for month to be still compatible with semantic versioning." CalVer was always intended to drop in where SemVer was used. So 26.0 recommends unpadded (YYYY.M.D) as a sane "pure" default for software libraries. But libraries are not the only objects of versioning schemes. The exception is a version that becomes a filename, an image tag, or an object-store key that gets listed lexically. There, padding keeps 26.10 sorted after 26.09, which is why Ubuntu, NixOS, and NVIDIA's own NGC containers pad. More evidence of teams designing their versions. We love to see it. Our FAQ has a longer discussion of the padding issue, as well. Optional segments There was never any rule against them, but 26.0 makes optional trailing segments more explicit with square brackets. Now, yt-dlp's scheme can finally be written down: YYYY.0M.0D[.MICRO]. For the CalVer badges I could find on GitHub, they all stay valid for now. I've got a note on the deprecated spellings and a new copy-paste badge section for new ones. What else is new? It's always a great time to add more citations to the site. A spec changelog; the spec now versions itself: 16.6, 19.7, 26.0. A FAQ: breaking changes, same-day releases, and padding. The users page, rebuilt by category, with past users of note (schemes change; that's fine) and tooling. Case studies: Apple and NVIDIA in, yt-dlp replacing youtube-dl. Much of the thinking behind these changes happened in the GitHub issue tracker over the years, and 19 or so issues close with this release. That's where ideas for CalVer should go, so by all means, open an issue, and we'll get it sorted! In due time, of course. In closing, I can't believe I still love belaboring these numbers so much. Thanks to all (but especially Mark, Glyph, Hugo, issue reporters, translators, and maintainers) for the discussion, and ultimately making the most timely versioning system a timeless classic. See also 2016 announcement Designing a version My Yap on Why CalVer beats Semver

16 hours ago • 1 votes
"You didn't do it right"

Four sincere attempts at OKRs, four failures, and every time: "you didn't do it right." Maybe the framework doesn't fit you -- so how do you find one that does?

23 hours ago • 1 votes
We solved SQLite's single-writer limitation

Multiple concurrent writers. Multiple processes. Same SQLite. No modifications.

2 days ago • 1 votes
If your team is happy, are you doing a good job?

Brilliant jerks, ZIRP-era managers, and how psychological safety lost the plot. Part 3 of my conversation with Dr. Cat Hicks.

3 days ago • 1 votes
WTF is context engineering? (with real examples)

Say you're deploying an AI assistant that processes online order returns. For it to work, it would need access to your store's purchase policy, item…

4 days ago • 1 votes
📚 BoredReading

You seem to be enjoying this.

Join free to unlock everything.

Create free account

Already have an account? Sign in