<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Get Info: #software</title>
    <description>Posts tagged “software” — Blog of independent game and app developer Matt Sephton. Featuring vintage Macintosh, game development, digital artwork, Japanese esoterica, video game reviews, hacks and tips, and much more.</description>
    <link>https://blog.gingerbeardman.com/tag/software/</link>
    <atom:link href="https://blog.gingerbeardman.com/tag/software/index.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 11 Jul 2026 16:22:12 +0000</pubDate>
    <lastBuildDate>Sat, 11 Jul 2026 16:22:12 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>

    
      
        <item>
          <title>Today I shipped 20 apps and a screensaver</title>
          <description>&lt;p&gt;Shipping software is &lt;em&gt;hard&lt;/em&gt;. My secret to doing it successfully is a combination of careful scoping, with strict avoidance of feature creep. But even then, it’s still insanely difficult to ship something.&lt;/p&gt;

&lt;p&gt;Shipping multiple pieces of software one after the other in quick succession would be an even more gruelling task. Nobody in their right mind would choose to do such a thing…&lt;em&gt;right?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Right?&lt;/p&gt;

&lt;p&gt;I must have missed the memo because I’m shipping 20 apps and a screensaver…&lt;em&gt;today!&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;14 × macOS apps (Mac App Store)&lt;/li&gt;
  &lt;li&gt;3 × macOS apps (TestFlight)&lt;/li&gt;
  &lt;li&gt;2 × iOS apps (App Store)&lt;/li&gt;
  &lt;li&gt;1 × macOS app (GitHub download)&lt;/li&gt;
  &lt;li&gt;1 × macOS screensaver (Web download)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get them at &lt;a href=&quot;https://www.gingerbeardman.com/apps/&quot;&gt;gingerbeardman.com/apps/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/wall-of-apps.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;twenty&quot;&gt;Twenty!?&lt;/h2&gt;

&lt;p&gt;Yup. So I think it’s a bit of a myth that software takes huge teams to make. Some of the most successful software of all time was created by individuals: MacPaint, HyperCard, Minecraft, RollerCoaster Tycoon, BitTorrent, Stardew Valley, WinAmp, Flappy Bird, Vim, the World Wide Web, HTML, URLs, and HTTP (those last four by the same person, Tim Berners-Lee). Or perhaps it was made by power duos: Photoshop, Doom, Quake, Google, WhatsApp, Figma, Sensible Soccer. It’s still quite common today, especially in the indie scene, but perhaps it’s not talked about as much?&lt;/p&gt;

&lt;p&gt;I started making software in &lt;a href=&quot;https://blog.gingerbeardman.com/2025/03/14/digging-up-the-past-with-discmaster/&quot;&gt;1990 on the Atari ST, moved to Windows and the web in 1995&lt;/a&gt;, then to Macintosh in 2000, and iPhone in 2007. My apps and games have been &lt;a href=&quot;https://www.gingerbeardman.com/archive/tektonix/featured.htm&quot;&gt;featured in publications around the world&lt;/a&gt;, been &lt;a href=&quot;/2004/06/20/wire-hang-redux/&quot;&gt;downloaded millions of times,&lt;/a&gt; and &lt;a href=&quot;https://blog.gingerbeardman.com/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;one of my games—which happens to be only 39 KB—received a “best game of the year” accolade alongside Mario and Zelda&lt;/a&gt;. The only time I wasn’t actively making and releasing software was during my time working for Apple as a Technology Evangelist, because they don’t allow it.&lt;/p&gt;

&lt;p&gt;So I had a bunch of apps that I’d built for myself over the years, and friends kept encouraging me to release them. Making an app for yourself is one thing, but shipping an app to the public is much more difficult. If I was to release one app every month it would take me almost two years to release them all! Doing the &lt;em&gt;App Store Dance&lt;/em&gt; to that kind of beat is my idea of hell. So I decided to do it “once” for all my apps. It still took a couple of months to get them ready, and a further month to get them all approved. I do not advise anybody else do this, because it was painful. But I did it, and here we are!&lt;/p&gt;

&lt;h2 id=&quot;wth&quot;&gt;WTH!?&lt;/h2&gt;

&lt;p&gt;To ship this many apps at once I had to optimise my workflow to an unprecedented level: Xcode projects set up as similarly as possible to reduce cognitive load, shell scripts to automate common build and test tasks, a script to generate changelogs ready for submission, another to automate incremental website builds based on changed files, and one more to generate screenshots of my screenshots, followed by &lt;a href=&quot;https://asccli.app&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asc&lt;/code&gt;&lt;/a&gt; to automate uploading data to App Store Connect.&lt;/p&gt;

&lt;p&gt;Code hygiene got the same treatment. I ran &lt;a href=&quot;https://github.com/peripheryapp/periphery&quot;&gt;Periphery&lt;/a&gt; across every project to find and remove dead/unused code, which shrinks the binary. Then &lt;a href=&quot;https://github.com/kucherenko/jscpd&quot;&gt;jscpd&lt;/a&gt; to spot copy-paste duplication or similar code sections and refactor them into shared helpers, which can sometimes &lt;em&gt;increases&lt;/em&gt; binary size slightly, but it’s a price worth paying for maintainability across so many codebases. There are several Xcode/compiler optimisations that can help strip unused data from the final binary, if you’re careful. I also “cheat” by adding only the largest icon size to macOS apps to avoid over 100 KB of additional icon size variations.&lt;/p&gt;

&lt;p&gt;For the website I created a static site generator that uses a simple templating system to generate a static site for 20 apps, the whole thing is spat out in only 36ms. Each app has its own markdown content file with YAML front matter, markdown description for the press kit, privacy policy, RSS feed XML (managed by my app &lt;a href=&quot;https://www.gingerbeardman.com/apps/feedit/&quot;&gt;Feedit&lt;/a&gt;), maybe some FAQs, an SVG icon, and a bunch of screenshots in both light and dark mode. The press kit is zipped up on a full build and the static site is uploaded to my server over WebDAV. I even managed to hide a fun little mini game in there! The time and effort spent on this system over the last couple of months was roughly equivalent to building another app. But it was worth it.&lt;/p&gt;

&lt;p&gt;Most of my apps cost a small amount up front. That’s a way to show your appreciation for the time I spent making them. It also means I never need to use ads, tracking, or subscriptions to make it worth me doing this. After all making software is my job, not a hobby. All apps are actively maintained—if an app hasn’t been updated in a while, it means it has reached a stable state, rather than it being abandoned. Supporting older versions of macOS comes for free when you’re not tied to dependencies that have their own requirements and release schedule.&lt;/p&gt;

&lt;p&gt;Pricing is kept dead simple: $5, $10, or $15. Three tiers, no tricks, no “Pro” upsells, no subscriptions. Small utilities are $5, mid-size apps are $10, and the most capable or niche apps are $15. A couple are free, and there are some bundles to save money.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;fits-on-a-floppy&quot;&gt;Fits On A Floppy&lt;/h2&gt;

&lt;p&gt;Many of these apps are small enough to fit on a 1.44 MB floppy disk. Not as a gimmick but as a discipline. I use only native frameworks, avoid dependency bloat, and keep features focused. Writing small software is harder than writing big software because every line of code has to earn its place. But the result is apps that download in a blink, launch instantly, and respect your system resources. I don’t miss floppy disks, but I miss the mindset they demanded: that every byte matters, that constraints breed creativity, and that software should be light on its footprint.&lt;/p&gt;

&lt;p&gt;Read the full manifesto at &lt;a href=&quot;https://fitsonafloppy.com&quot;&gt;fitsonafloppy.com&lt;/a&gt; and &lt;em&gt;spread the word!&lt;/em&gt; ✨💾✨&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-app-list&quot;&gt;The App List&lt;/h2&gt;

&lt;p&gt;These apps were all built to fill personal needs, but friends have encouraged me to release them for others to use. This is the bit where I was set to claim oldest app was about 8 years old, while the newest apps was about 8 days old. But sadly App Store Review got in the way, by rejecting my oldest app that I’ve had installed on my own phone since 2019 for being “too derivative” (I’ll revisit that and launch it another time), and they took longer to review the newest app than it did for me to develop it. Other fun rejections along the way: inappropriate use of the word “Safari” in an app for managing Safari browser tabs and bookmarks, and use of web server entitlements in a web server app, sending a video of me ejecting a physical CD-ROM drive, the list goes on. But you get the idea: these apps have taken a while to arrive.&lt;/p&gt;

&lt;p&gt;Anyway, here they are, have fun with them:&lt;/p&gt;

&lt;h3 id=&quot;barfly-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/barfly/&quot;&gt;Barfly&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Analytics for your itch.io games, right in the menu bar. Track views, downloads, and purchases with delta tracking across configurable time periods from one hour to one week. Refresh on a schedule and keep historic data.&lt;/p&gt;

&lt;h3 id=&quot;brutify-macos-testflight&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/brutify/&quot;&gt;Brutify&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;chip chip-testflight&quot;&gt;TestFlight&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Lossless image optimisation using 14 engines running in parallel. Drop in images or folders and get perfectly optimised JPEG, PNG, GIF, and WebP assets without any quality loss. Fast, Slow, or Lossy modes to suit any workflow. A modern ImageOptim.&lt;/p&gt;

&lt;h3 id=&quot;ditto-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/ditto/&quot;&gt;Ditto&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Translate text without switching apps. Copy text twice with CMD+C and your clipboard is translated using Apple Translate or DeepL. Includes a floating window for real-time translation as you type. A DeepL alternative.&lt;/p&gt;

&lt;h3 id=&quot;dottie-macos-testflight&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/dottie/&quot;&gt;Dottie&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;chip chip-testflight&quot;&gt;TestFlight&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;A native Mac pixel art editor built from scratch. Drawing tools, layers with onion skinning, frame-based animation, custom palettes with Lospec import, and export to PNG, GIF, SVG, and sprite sheets. A native macOS alternative to Aseprite, Piskel, Pixen, etc.&lt;/p&gt;

&lt;h3 id=&quot;driveaway-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/driveaway/&quot;&gt;Driveaway&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Removes macOS metadata junk from external drives before you eject them. No more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.DS_Store&lt;/code&gt; files, Spotlight indexes, and AppleDouble resource forks cluttering up your USB drives and SD cards.&lt;/p&gt;

&lt;h3 id=&quot;eqer-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/eqer/&quot;&gt;EQer&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;A system-wide 10-band equaliser that processes every app on your Mac in real time. Pick a preset, drag the curve, or paste your Audiogram and let it calculate the right listening curve for your hearing. Includes per-app control and dynamic range compression.&lt;/p&gt;

&lt;h3 id=&quot;feedit-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/feedit/&quot;&gt;Feedit&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;An editor for creating, managing, and publishing RSS 2.0 feeds. Import existing feeds, edit every field, validate against the spec, and publish to a local folder or WebDAV server. Drafts, iCloud sync, and Find &amp;amp; Replace built in. An alternative to Feeder.&lt;/p&gt;

&lt;h3 id=&quot;hubble-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/hubble/&quot;&gt;Hubble&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Interactive USB topology viewer for macOS. See your complete device tree as a zoomable canvas with speed-coded cables, throttle warnings, power diagnostics, and hot-plug animations—all updating in real time.&lt;/p&gt;

&lt;h3 id=&quot;last-dance-macos-download&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/last-dance/&quot;&gt;Last Dance&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;chip chip-direct&quot;&gt;download&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Solves a persistent macOS bug where SMB file sharing becomes unresponsive after restart. It automatically disables file sharing before shutdown and re-enables it at login. No worries.&lt;/p&gt;

&lt;h3 id=&quot;localmost-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/localmost/&quot;&gt;Localmost&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Spin up a local HTTP server by dragging a folder onto your menu bar. Multiple servers, automatic port assignment, access logs, and directory listings—all managed from a clean native interface.&lt;/p&gt;

&lt;h3 id=&quot;mojibaker-ios-free&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/mojibaker/&quot;&gt;Mojibaker&lt;/a&gt; &lt;span class=&quot;chip chip-ios&quot;&gt;iOS&lt;/span&gt; &lt;span class=&quot;chip chip-free&quot;&gt;free&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Create profile pictures with your choice of colours, gradients, images, then overlay emoji, genomji, memoji or stickers. Type a mood to get colour suggestions, compose your design, save presets, and export at high resolution.&lt;/p&gt;

&lt;h3 id=&quot;octoping-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/octoping/&quot;&gt;Octoping&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;GitHub notifications in your Mac menu bar. See what needs attention, grouped by repository with colour-coded icons matching github.com. Mark items as read or open them in your browser.&lt;/p&gt;

&lt;h3 id=&quot;papertrail-ios&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/papertrail/&quot;&gt;PaperTrail&lt;/a&gt; &lt;span class=&quot;chip chip-ios&quot;&gt;iOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Full TaskPaper editing on iOS. Projects, tasks, notes, and tags with a powerful search query language, collapsible projects, widgets, and more. Syncs via iCloud and works with TaskPaper on Mac.&lt;/p&gt;

&lt;h3 id=&quot;seeports-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/seeports/&quot;&gt;Seeports&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Shows every TCP port listening on your Mac from the menu bar. Ports are automatically grouped by service—Docker, OrbStack, Kubernetes, and more. Copy a localhost URL, open in browser, search for info on unknown ports, or terminate a rogue process.&lt;/p&gt;

&lt;h3 id=&quot;spindle-macos-testflight&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/spindle/&quot;&gt;Spindle&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;chip chip-testflight&quot;&gt;TestFlight&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Raw device access for byte-accurate CD and DVD extraction, with support for hybrid ISO 9660/HFS discs and triple hashing for verification. Rip from multiple drives simultaneously.&lt;/p&gt;

&lt;h3 id=&quot;stapler-macos-free&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/stapler/&quot;&gt;Stapler&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;chip chip-free&quot;&gt;free&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Bundle related files into logical projects without moving anything. Opening a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.stapled&lt;/code&gt; document opens your artwork, code, todo list, and your reference docs—all at once.&lt;/p&gt;

&lt;h3 id=&quot;tabulator-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/tabulator/&quot;&gt;Tabulator&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;A bird’s-eye view of every Safari tab across local windows, tab groups, cloud tabs, and bookmarks. Search, sort, tag, export, and manage tabs from one place. Includes a vertical tabs mode, Safari extension to show open tab count, and automatic daily backup with easy restore.&lt;/p&gt;

&lt;h3 id=&quot;tsundoku-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/tsundoku/&quot;&gt;Tsundoku&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Quick access bookmarking for self-hosted services. A global hotkey captures the URL and title from your browser, suggests tags, and posts to Linkding, Raindrop, Shaarli, LinkAce, Karakeep, or Wallabag.&lt;/p&gt;

&lt;h3 id=&quot;vanishing-point-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/vanishing-point/&quot;&gt;Vanishing Point&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Perspective correction for photos and images. Four correction tools with Apple Vision auto-detection, 256+ aspect ratio presets, background removal via Vision framework, colour sampling, plus image adjustments. Export as PNG, JPG, or TIFF. QuickLook and Finder Thumbnails are supported.&lt;/p&gt;

&lt;h3 id=&quot;wavelet-macos&quot;&gt;&lt;a href=&quot;https://www.gingerbeardman.com/apps/wavelet/&quot;&gt;Wavelet&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Sound effect generator powered by synthesis, based on classic sfxr. One-click presets for laser zaps, coin pickups, explosions, and more. Layer multiple sounds with the built-in mixer and export as WAV, or as code in Lua, or Swift.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;fits-on-a-floppy-screensaver-macos-download-free&quot;&gt;&lt;a href=&quot;https://fitsonafloppy.com&quot;&gt;“Fits on a Floppy” Screensaver&lt;/a&gt; &lt;span class=&quot;chip chip-macos&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;chip chip-direct&quot;&gt;download&lt;/span&gt; &lt;span class=&quot;chip chip-free&quot;&gt;free&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;And finally, technically, the 21st release: a macOS screen saver inspired by &lt;em&gt;After Dark&lt;/em&gt; and its classic &lt;em&gt;Flying Toasters&lt;/em&gt;. It features flying disks that do barrel rolls and spins as they drift across your screen. Thanks to my friend &lt;a href=&quot;https://jbmorley.co.uk/about/&quot;&gt;Jason Morley&lt;/a&gt; for the idea, which I built while waiting for App Store Review to finish processing the rest. To make this easier, I upgraded the old test harness I built for my &lt;a href=&quot;/2009/04/09/today-screensaver/&quot;&gt;&lt;em&gt;Today&lt;/em&gt; screensaver&lt;/a&gt; back in 2009.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 17 Apr 2026 18:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/04/17/today-i-shipped-twenty-apps-and-a-screensaver/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/04/17/today-i-shipped-twenty-apps-and-a-screensaver/</guid>
        </item>
      
    
      
        <item>
          <title>Fresh Files Extension for Nova editor</title>
          <description>&lt;p&gt;I spotted &lt;a href=&quot;https://github.com/FreHu/vscode-fresh-file-explorer&quot;&gt;Fresh File Explorer&lt;/a&gt;, a VS Code extension, &lt;a href=&quot;https://github.com/FreHu/vscode-fresh-file-explorer&quot;&gt;on Hacker News&lt;/a&gt; and loved the idea—a file sidebar that only shows recently modified files.&lt;/p&gt;

&lt;p&gt;The default file sidebar in an editor shows &lt;em&gt;everything&lt;/em&gt;, which in a large project is mostly noise. I thought it would be fun to have something like this for &lt;a href=&quot;https://nova.app&quot;&gt;Nova&lt;/a&gt;, so I reimplemented the concept from scratch using only the OG repo readme as a reference. No code is shared, it’s a completely new extension built against Nova’s API. That means I can’t do as much as the VS Code extension, such as colour coding the files like a heatmap, but it’s still quite useful as it is.&lt;/p&gt;

&lt;p&gt;The result is a sort of hybrid filesystem/git/bookmarks sidebar which I’m finding quite pleasant to use.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/fresh-files-extension-for-nova.png&quot; alt=&quot;PNG&quot; title=&quot;Fresh Files showing pinned files and recent changes&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;In Git repositories, Fresh Files uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git log&lt;/code&gt; to figure out what’s changed. In non-Git workspaces it falls back to filesystem modification times, so it works in any folder. It shares some functionality with the built-in Git Sidebar but I’ve been careful to not duplicate too much.&lt;/p&gt;

&lt;p&gt;There are two modes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Pending Changes&lt;/strong&gt; — shows uncommitted files (or files modified in the last day if there’s no Git)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Historical&lt;/strong&gt; — shows files modified within a configurable time window, from 1 hour to 360 days&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can toggle the time window from the sidebar header, the command palette, or project settings.&lt;/p&gt;

&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;

&lt;p&gt;The sidebar supports both a flat file list and a directory tree view, with sorting by recency or name. Files show relative timestamps like “2h ago” or “3d ago” so you can see at a glance what’s freshest.&lt;/p&gt;

&lt;p&gt;Other things worth mentioning:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Pinned files&lt;/strong&gt; — pin files to keep them visible regardless of time window&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;File history&lt;/strong&gt; — right-click any file to see its commit history with diffs&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Diff Search (Pickaxe)&lt;/strong&gt; — find commits where a string was added or removed, file-scoped from the sidebar or repo-wide from the command palette&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Line History&lt;/strong&gt; — view git history for the current line or selection&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Exhume&lt;/strong&gt; — view deleted file contents with syntax highlighting&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resurrect&lt;/strong&gt; — restore deleted files to their original location&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Show All Files&lt;/strong&gt; — temporarily show all tracked files, overriding the time window&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Search Fresh Files&lt;/strong&gt; — full-text search across fresh files from the command palette&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;New File&lt;/strong&gt; — create a new file from the sidebar context menu&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Move to Trash&lt;/strong&gt; — delete files from the sidebar context menu&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quick Open&lt;/strong&gt; — fuzzy-open from just the fresh files via the command palette&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deleted file indicators&lt;/strong&gt; — deleted files show up with a distinct icon&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Auto-refresh&lt;/strong&gt; — the sidebar updates after saves, git commits, checkouts, and merges&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;context-menu&quot;&gt;Context menu&lt;/h2&gt;

&lt;p&gt;Right-click gives you New File, Show in Finder, Copy Path, Copy Relative Path, Move to Trash, Pin/Unpin, Show File History, Diff Search, and Exhume/Resurrect for deleted files.&lt;/p&gt;

&lt;h2 id=&quot;get-it-now&quot;&gt;Get it now&lt;/h2&gt;

&lt;p&gt;You can install it at: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FreshFiles/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FreshFiles/&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 24 Feb 2026 13:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/02/24/fresh-files-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/02/24/fresh-files-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Controlling local web servers using xbar</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve released a native macOS app that does all this script can do &lt;em&gt;and much more!&lt;/em&gt;&lt;br /&gt;Read all about it: &lt;a href=&quot;https://www.gingerbeardman.com/apps/localmost/&quot;&gt;gingerbeardman.com/apps/localmost/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes I want to run local web servers for projects I’m working on. Usually more than one at a time, or at least over a short space of time.&lt;/p&gt;

&lt;p&gt;So I thought it would be cool to have a controller for those local servers in my menu bar. Sounded like the perfect job for a little scripting and &lt;a href=&quot;https://github.com/matryer/xbar&quot;&gt;xbar&lt;/a&gt;, which is a great way to prove a menubar app idea quickly.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;The plugin allows you to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;toggle servers on and off&lt;/li&gt;
  &lt;li&gt;open in browser&lt;/li&gt;
  &lt;li&gt;view ports&lt;/li&gt;
  &lt;li&gt;view paths&lt;/li&gt;
  &lt;li&gt;view log sizes&lt;/li&gt;
  &lt;li&gt;clear logs&lt;/li&gt;
  &lt;li&gt;edit config&lt;/li&gt;
  &lt;li&gt;and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xbar-localhost-dark.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;example-config&quot;&gt;Example config&lt;/h2&gt;

&lt;p&gt;In the config file we set the starting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_PORT&lt;/code&gt;, followed by one or more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_DIR&lt;/code&gt; for as many projects as you might want servers. We can temporarily comment out those server lines to prevent projects from appearing in the menu.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# .xbar_httpd_config&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;8000
&lt;span class=&quot;c&quot;&gt;#SERVER_DIR=~/Projects/starchasers/&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/serenity/
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/point-cloud/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;source-code&quot;&gt;Source code&lt;/h2&gt;

&lt;p&gt;Python source code is available in the following gist:&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 12 Jan 2026 20:57:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</guid>
        </item>
      
    
      
        <item>
          <title>WebGL CRT Shader</title>
          <description>&lt;p&gt;I’m releasing my WebGL CRT Shader as open source.&lt;/p&gt;

&lt;p&gt;It creates a CRT/vintage TV effect so could be used in games, emulators, apps, demos, visuals, etc. It’s not a simulator just something that gives the same general old school vibe.&lt;/p&gt;

&lt;p&gt;As a WebGL shader, it runs on the device’s hardware GPU in the browser and renders to a &amp;lt;canvas&amp;gt; (or offscreen canvas). I currently use it with Three.js, and the demo below uses just a 2D canvas.&lt;/p&gt;

&lt;p&gt;It’s optimised to run well on low power devices as far back as iPhone XS, though I’m certain it can be optimised even further. &lt;a href=&quot;https://github.com/gingerbeardman/webgl-crt-shader/pulls&quot;&gt;PRs welcome&lt;/a&gt;! The easiest personal optimisation you might make would be removing processing of unused parameters.&lt;/p&gt;

&lt;p&gt;Is it scientifically correct? &lt;em&gt;Hell no.&lt;/em&gt; Does it look good? &lt;em&gt;Yes.&lt;/em&gt; Does it give you the feels? &lt;em&gt;Absolutely.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/gingerbeardman/webgl-crt-shader/&quot;&gt;github.com/gingerbeardman/webgl-crt-shader/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;history&quot;&gt;History&lt;/h2&gt;

&lt;p&gt;It began as a &lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3lk2arnt4rc2o&quot;&gt;shader for Love2D&lt;/a&gt; at the beginning of 2025 (&lt;a href=&quot;https://gist.github.com/gingerbeardman/7392ee84fdb2e405d7437b5b12e4c12d&quot;&gt;here’s a gist of that one&lt;/a&gt;) and by the end of 2025 I had ported it to GLSL for a personal work-in-progress web game.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;live-demo&quot;&gt;Live Demo&lt;/h2&gt;

&lt;p&gt;Reach for the sliders!&lt;/p&gt;

&lt;p&gt;(You can scroll the demo horizontally on mobile)&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;

  &lt;iframe src=&quot;https://gingerbeardman.github.io/webgl-crt-shader/&quot; width=&quot;740&quot; height=&quot;740&quot;&gt;
![screenshot](https://cdn.gingerbeardman.com/images/posts/glsl-web-crt-shader.png)
&lt;/iframe&gt;

&lt;/div&gt;

&lt;p&gt;^ Screenshot is Pico-8 classic &lt;a href=&quot;https://www.lexaloffle.com/bbs/?tid=3547&quot;&gt;Worm Nom Nom&lt;/a&gt; by Tic Tac Toad (kometbomb + iLKke) 🌸🍏🎩🐛💩&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 04 Jan 2026 17:44:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/01/04/webgl-crt-shader/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/01/04/webgl-crt-shader/</guid>
        </item>
      
    
      
        <item>
          <title>Remove Comments Extension for Nova editor</title>
          <description>&lt;p&gt;I’ve released a new extension for the Nova editor.&lt;/p&gt;

&lt;p&gt;It’s called &lt;em&gt;Remove Comments&lt;/em&gt; and it …removes comments from the current line, or selected lines, in your code. That’s it!&lt;/p&gt;

&lt;p&gt;Oh, I’ve tried to support as many comment/syntax formats as I can think of. Sadly it’s not possible to get the current comment formatting from the Nova API, so I had to roll my own logic and heuristics.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.remove-comments/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.remove-comments/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript (C-style comments):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This comment will be removed&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This trailing comment will be removed&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* This block comment will be removed */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Python (hash-style comments):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# This comment will be removed
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This trailing comment will be removed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;HTML:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- This comment will be removed --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- This trailing comment will be removed --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 08 Dec 2025 15:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/12/08/remove-comments-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/12/08/remove-comments-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>How to tame a user interface using a spreadsheet</title>
          <description>&lt;p&gt;Many years ago, while working at Apple and running a lab at WWDC, I met a guy who was using a piece of &lt;a href=&quot;https://en.wikipedia.org/wiki/IAd_Producer&quot;&gt;Apple software designed for creating interactive ads&lt;/a&gt; to design a Car Play user interface for a popular US car manufacturer. I was impressed by his ability to think outside the box and told him so. I mentioned to him how human interface designers at Apple &lt;a href=&quot;https://www.youtube.com/watch?v=DGn7BcFGigc&amp;amp;t=474s&quot;&gt;were using Keynote to rapidly prototype user interfaces and animations&lt;/a&gt;. The discussion then took a strange turn onto spreadsheets.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;spreadsheets&quot;&gt;Spreadsheets?&lt;/h2&gt;

&lt;p&gt;In my opinion, spreadsheets are one of the greatest user interface design tools ever created. I believe that the most effective tool for a job is one that gets out of the way as much as possible. Even better if it’s a tool that you already have access to, or at the very least, can use at no cost.&lt;/p&gt;

&lt;p&gt;Before you have time to load up Figma, Sketch, Photoshop, or any other design software, I’ll have already typed in my content placeholders in a spreadsheet and be most of my way towards a solution. I’ll be merging and moving cells at lightning speed—rationalising layouts, determining the optimal use of space, and creating order out of chaos.&lt;/p&gt;

&lt;p&gt;The purpose of using a tool that may seem ill-suited to the task at hand—designing user interfaces—is to liberate yourself from the clutter or baggage of content and style. This allows you to focus solely on layout, positioning, and hierarchy. You don’t have to worry about type sizes, or get bogged down with software choices. You can quickly adjust the size or structure of the screen by adding/removing rows/columns. With a spreadsheet, it’s just the keyboard between you and the perfect layout.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;lets-take-your-brain-to-another-dimension&quot;&gt;Let’s take your brain to another dimension&lt;/h2&gt;

&lt;p&gt;One frequent criticism of spreadsheets is that they are “only 2D” or that a grid isn’t versatile enough to solve your problem. But if you pay close attention, you’ll realise that spreadsheets can occupy as many dimensions as you require by using multiple sheets. Like the stack of ledger paper that was used before spreadsheets were invented. An episode of The Computer Chronicles called “The Spreadsheet Wars” aired in 1988 and has a great &lt;a href=&quot;https://youtu.be/eAiZBUYNUBA?si=TMiXXLj1gtFwZQkp&amp;amp;t=1154&quot;&gt;demo of spreadsheet software Lotus 1-2-3 doing 3D spreadsheet manipulation&lt;/a&gt; in ways that are still impressive several decades later.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://archive.org/details/GridSystemsInGraphicDesignJosefMullerBrockmann&quot;&gt;Grid systems are well known across many fields of design&lt;/a&gt; for being a huge assist when solving layouts and rationalising problems. The grid enforces structure invisibly during the design process. A spreadsheet is a highly configurable grid system that is ready to be put to work on your problem!&lt;/p&gt;

&lt;p&gt;You might also consider the use of plain paper, graph paper, and a bitmap editor. Which might be best suited for sketching out a quick icon idea? It’s easy to understand that graph paper is more suitable than plain paper for drawing images made up of pixels, and in many situations it may be quicker and more suitable than a bitmap editor. &lt;a href=&quot;https://www.moma.org/collection/works/188382?artist_id=38483&amp;amp;page=1&amp;amp;sov_referrer=artist&quot;&gt;Susan Kare famously used a book of grid/graph paper to draw many early Macintosh icons&lt;/a&gt; because icon creation software had yet to be invented.&lt;/p&gt;

&lt;h2 id=&quot;theres-an-app-for-that&quot;&gt;There’s an app for that&lt;/h2&gt;

&lt;p&gt;I use Apple Numbers to do most of my spreadsheet work, because it’s the one that I have easiest access to most of the time—it’s installed on both my Mac and iPhone. You might want to use Google Sheets, Microsoft Excel, or even a simpler table tool like &lt;a href=&quot;https://smoothcsv.com&quot;&gt;SmoothCSV&lt;/a&gt;. There is no wrong choice.&lt;/p&gt;

&lt;p&gt;Here are some examples of layouts I’ve solved using a spreadsheet.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;yoyozo&quot;&gt;YOYOZO&lt;/h2&gt;

&lt;p&gt;Perhaps my most famous example is the stats screen of &lt;a href=&quot;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;my hit game &lt;em&gt;YOYOZO&lt;/em&gt;&lt;/a&gt;. With this one I already had a TTF font file that I’d created for the manual, so I went the extra mile and planned a pixel perfect layout in the right colours and everything. A fun fact you might not know: &lt;em&gt;YOYOZO&lt;/em&gt; was featured in Ars Technica’s “Best Games of 2023” list, alongside Mario and Zelda. This spreadsheet thing is great!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-yoyozo.png&quot; alt=&quot;IMG&quot; title=&quot;Pixel perfect precision using the exact font&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;enhanced-sfxr&quot;&gt;Enhanced sfxr&lt;/h2&gt;

&lt;p&gt;This year I’ve made two enhanced versions of sfxr—&lt;a href=&quot;https://gingerbeardman.itch.io/enhanced-sfxr-for-love2d&quot;&gt;one for Love2D&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/search?q=from%3Agingerbeardman%20sfxr&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;one for macOS&lt;/a&gt;—and I can’t remember which one this was done for. I usually get rid of the header rows and columns but in this instance they acted as sidebar and title bar. Regardless, whilst it might not look like much this sort of pre-planning and organisation saved a ton of time when I was working on the app. It involved no code and was done in a minute or two!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-sfxr.png&quot; alt=&quot;IMG&quot; title=&quot;Organising the large components that make up the window&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;spindle&quot;&gt;Spindle&lt;/h2&gt;

&lt;p&gt;Another of my “coming soon” apps is &lt;a href=&quot;https://twitter.com/search?q=from:gingerbeardman%20spindle&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;an optical media dumper whose USP is its ability to dump from multiple drives at the same time&lt;/a&gt;. I wanted a UI that would stay mostly the same across both the setup and dumping states, and also that was easily repeatable when additional drives duplicate the app UI. The benefit of working with a grid here is that it was 1:1 transferrable to SwiftUI grid system!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-spindle.png&quot; alt=&quot;IMG&quot; title=&quot;Two states of the same interface showing what changes and what remains the same&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;driving-game&quot;&gt;Driving Game&lt;/h2&gt;

&lt;p&gt;This final one is an example of a racing game. Something that you might already be more familiar with and so be able to figure out how you can apply the technique to your own work. Something like Mario Kart, F-Zero, Colin McRae Rally, or WipEout. Let’s go!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-racer.png&quot; alt=&quot;IMG&quot; title=&quot;A fairly standard driving/racing game head up display&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 11 Oct 2025 00:03:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/10/11/how-to-tame-a-user-interface-using-a-spreadsheet/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/10/11/how-to-tame-a-user-interface-using-a-spreadsheet/</guid>
        </item>
      
    
      
        <item>
          <title>Three.js Completions Extension for Nova editor</title>
          <description>&lt;p&gt;I’ve released an extension to add &lt;a href=&quot;https://threejs.org&quot;&gt;Three.js&lt;/a&gt; completions support to the Nova editor.&lt;/p&gt;

&lt;p&gt;The coolest thing about this extension is that most of the code is automatically generated straight from the Three.js TypeScript files. This means there’s the potential for less errors in the data, and I can easily update the extension whenever a new release of Three.js comes out just by running a single command. Why work &lt;em&gt;harder&lt;/em&gt; when you can work &lt;em&gt;cleverer&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.ThreeJS/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.ThreeJS/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might be quick to deduce that—yes—I’m making a 3D game. 😘&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 04 Oct 2025 15:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/10/04/three-js-completions-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/10/04/three-js-completions-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Updates to Extensions for Nova editor</title>
          <description>&lt;p&gt;In late 2024, I spent some time improving my tools by &lt;a href=&quot;/2024/10/17/extensions-for-nova-editor/&quot;&gt;building a set of extensions for Nova editor&lt;/a&gt; to streamline some time-consuming tasks I encounter during blogging and game development.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-extensions.png&quot; alt=&quot;IMG&quot; title=&quot;My current list of extensions for Nova editor&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This week, I released major updates to many of them with enhancements and new features:&lt;/p&gt;

&lt;h2 id=&quot;bookmarks&quot;&gt;Bookmarks&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Adds sorting, the ability to bookmark folders, marking of missing items, and other improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;markdown-file-linker&quot;&gt;Markdown File Linker&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Results in the choice palette are now sorted by default using dates found in filenames.&lt;/li&gt;
  &lt;li&gt;Along with other filtering improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;unwrap-paragraph&quot;&gt;Unwrap Paragraph&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can now unwrap the text surrounding cursor, which removes some friction.&lt;/li&gt;
  &lt;li&gt;In selected text multiple paragraphs will be unwrapped individually.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;word-counter&quot;&gt;Word Counter&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.wordcounter/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Watched words can be managed through the sidebar, added from selected text, or via typing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;yaml-tag-picker&quot;&gt;YAML Tag Picker&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can quickly insert or update the creation/modified ISO date in your front matter.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Plus!&lt;/em&gt; I also released a brand new extension:&lt;/p&gt;

&lt;h2 id=&quot;csv-to-md-table&quot;&gt;CSV to MD table&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.csv2md/&quot;&gt;Version 1.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Convert tabular data between CSV/TSV and Markdown formats.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 30 Aug 2025 15:03:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/08/30/updates-to-my-extensions-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/08/30/updates-to-my-extensions-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>iOS Low Data Mode reveals true app update sizes</title>
          <description>&lt;p&gt;An interesting side-effect of iOS Low Data Mode is that it shows you the exact size of an app update for your device, often a tiny fraction of the listed app size. This is because (delta) updates contain only the files that have changed. Even a first-time install is smaller due to app slicing/thinning containing only what is needed for that device.&lt;/p&gt;

&lt;p&gt;The larger size is the universal app size containing assets for all devices. It’s simpler and less confusing for everybody to see the same size.&lt;/p&gt;

&lt;p&gt;There are more screenshots at the comment links.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/low-data-mode.png&quot; alt=&quot;IMG&quot; title=&quot;eBay 6.220.0 update size for an iPhone XS running iOS 17&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 06 Aug 2025 19:43:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/08/06/ios-low-data-mode-reveals-true-app-update-sizes/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/08/06/ios-low-data-mode-reveals-true-app-update-sizes/</guid>
        </item>
      
    
      
        <item>
          <title>Automatic build number incrementing in Xcode</title>
          <description>&lt;p&gt;Note: these days I mostly let App Store Connect manage version numbers.&lt;/p&gt;

&lt;p&gt;There are many ways to do automatic build version number incrementing in Xcode. I don’t know of any really easy universal way of doing it, so this is simply my preferred way of doing it. I just added it to a new project so thought I’d document if only for my own future reference!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-create-a-xcconfig&quot;&gt;1. Create a .xcconfig&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Add a file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildNumber.xcconfig&lt;/code&gt; to your project&lt;/li&gt;
  &lt;li&gt;Add a &lt;em&gt;Pre-Action Script&lt;/em&gt; to your &lt;em&gt;Scheme&lt;/em&gt;:
    &lt;ul&gt;
      &lt;li&gt;In Xcode, edit your scheme (&lt;em&gt;Product &amp;gt; Scheme &amp;gt; Edit Scheme&lt;/em&gt;).&lt;/li&gt;
      &lt;li&gt;For both “Run” and “Archive” actions, add a &lt;em&gt;Pre-action&lt;/em&gt; with a script like the one below&lt;/li&gt;
      &lt;li&gt;Here I’m using a sort of reverse notation timestamp format, &lt;em&gt;you can use whatever works for you&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;Make sure to set “Provide build settings from” to your project&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;BUILD_NUMBER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;+%y%m%d.%H%M&apos;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PROJECT_FILE_PATH&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; .xcodeproj&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CURRENT_PROJECT_VERSION = &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BUILD_NUMBER&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/BuildNumber.xcconfig&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Updated build number to: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BUILD_NUMBER&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xcode-automatic-build-number-01.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-reference-the-xcconfig-in-your-project&quot;&gt;2. Reference the .xcconfig in your project&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;In your project set the base configuration file to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildNumber.xcconfig&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;In the main editor, you’ll see two sections: &lt;em&gt;PROJECT&lt;/em&gt; and &lt;em&gt;TARGETS&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;Click the name of your project and make sure &lt;em&gt;Info&lt;/em&gt; tab is selected&lt;/li&gt;
      &lt;li&gt;You can set the base configuration at the project level (as here) or for each target individually&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xcode-automatic-build-number-02.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-set-the-version-to-use-the-config-variable&quot;&gt;3. Set the version to use the config variable&lt;/h2&gt;

&lt;p&gt;In the target build settings, set: Current Project Version to: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(CURRENT_PROJECT_VERSION)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It will show the version number in place&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xcode-automatic-build-number-03.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-confirm-by-displaying-the-value-in-your-app&quot;&gt;4. Confirm by displaying the value in your app&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFBundleVersion&lt;/code&gt; for the build version number&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xcode-automatic-build-number-04.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 28 Jun 2025 16:01:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/06/28/automatic-build-number-incrementing-in-xcode/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/06/28/automatic-build-number-incrementing-in-xcode/</guid>
        </item>
      
    
      
        <item>
          <title>Intelligent Agent Technology: Open Sesame! (1993)</title>
          <description>&lt;p&gt;After years of unsuccessful attempts to find the name of a long-forgotten app through Google, I tried pasting my query into Gemini:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Back in my teens I saw a software demo for a Mac/Windows app that tracked what you were doing and offered to complete repetitive tasks for you. Like renaming all files in a folder it would interrupt after a few and offer to do the rest. Maybe late 80s or early 90s.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gemini instantly responded with the app by name and with references. (out of all the services I tried, ChatGPT was the only other one to identify the app correctly.)&lt;/p&gt;

&lt;p&gt;Open Sesame! was “the world’s first intelligent software assistant for the Macintosh. It observes how you work, learns your repetitive patterns, and then offers to do them for you. Automatically.”&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/open-sesame.jpg&quot; alt=&quot;IMG&quot; title=&quot;Open Sesame! software reduces computer-use complexity&amp;lt;br&amp;gt;by relieving users of many routine tasks.&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;one-thousand-and-one-nights&quot;&gt;One Thousand and One Nights&lt;/h2&gt;

&lt;p&gt;I remember seeing a demo of Open Sesame! in the early/mid-1990s. Although the demo failed—the Macintosh was supposed to prompt the user to continue renaming a series of files in sequence—the concept left a lasting impression on me.&lt;/p&gt;

&lt;p&gt;Over the years I’ve tried to find the event I saw the app at, the app itself, or any other information. I asked other classic Macintosh enthusiasts. Time after time I failed to find the answer.&lt;/p&gt;

&lt;p&gt;So I find it delightfully circular that in 2025 I am using the “ai” benefits of machine learning to find out the name of an app built around machine learning in 1993.&lt;/p&gt;

&lt;hr /&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/open-sesame-ad-1993.jpg&quot; alt=&quot;IMG&quot; title=&quot;Advertisement in MacWorld (December 1993)&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/open-sesame-11&quot;&gt;Open Sesame! 1.1 at Macintosh Garden&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9308_August_1993/page/n26/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: MacBulletin&lt;/a&gt; (August 1993)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9309_September_1993/page/n37/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: News&lt;/a&gt; (September 1993)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9312_December_1993/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: advertisement&lt;/a&gt; (December 1993)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9405_May_1994/page/n85/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: review of version 1.02&lt;/a&gt; (May 1994)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9406_June_1994/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: advertisement&lt;/a&gt; (June 1994)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9412_December_1994/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: price of version 1.1&lt;/a&gt; (December 1994)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9508_August_1995/page/n203/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: get Open Sesame! free when buying a keyboard&lt;/a&gt; (August 1995)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://spinoff.nasa.gov/spinoff1996/56.html&quot;&gt;NASA Spinoff: Intelligent Agent Technology&lt;/a&gt; (&lt;a href=&quot;https://archive.org/details/1991_20210720_202107/1996/page/n96/mode/1up&quot;&gt;scan&lt;/a&gt;) (1996)&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 31 May 2025 15:33:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/05/31/intelligent-agent-technology-open-sesame-1993/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/05/31/intelligent-agent-technology-open-sesame-1993/</guid>
        </item>
      
    
      
        <item>
          <title>Monokai Pro Vibrant theme for Nova editor</title>
          <description>&lt;p&gt;I neglected to blog about this in October 2024, so here it is now.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/gingerbeardman/Monokai-Pro-Vibrant.novaextension/releases/&quot;&gt;Monokai Pro Vibrant&lt;/a&gt; is my fork of the &lt;a href=&quot;https://github.com/keisto/Monokai-Pro.novaextension&quot;&gt;unofficial Monokai Pro theme&lt;/a&gt; for &lt;a href=&quot;https://nova.app&quot;&gt;Nova&lt;/a&gt; editor, by Tony Keiser.&lt;/p&gt;

&lt;p&gt;My fork has a few quality of life changes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Theme Vibrancy is enabled, making focused and unfocused windows more distinguishable&lt;/li&gt;
  &lt;li&gt;Colour tweaks improve contrast when highlighting found instances of search terms&lt;/li&gt;
  &lt;li&gt;Comments are now &lt;em&gt;italicised&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use the Spectrum variation of the theme with the &lt;a href=&quot;https://github.com/IBM/plex/releases/tag/%40ibm%2Fplex-mono%401.1.0&quot;&gt;IBM Plex Mono&lt;/a&gt; font to great effect.&lt;/p&gt;

&lt;p&gt;Since late-2025 I’m now using font &lt;a href=&quot;https://mass-driver.com/trial-fonts&quot;&gt;MD IO Trial&lt;/a&gt; (free for personal use).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-monokai-pro-vibrant.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;monokai-pro&quot;&gt;Monokai Pro&lt;/h2&gt;

&lt;p&gt;The original &lt;a href=&quot;https://monokai.pro&quot;&gt;Monokai Pro&lt;/a&gt; theme by Wimer Hazenberg is not free, so I would encourage you to buy a licence even if you’re using an unofficial port or modification of the theme. It’s only right to support software developers. You can get a licence to use Monokai Pro from the original creator: &lt;a href=&quot;https://monokai.pro&quot;&gt;monokai.pro&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;p&gt;I filed a &lt;a href=&quot;https://github.com/keisto/Monokai-Pro.novaextension/pull/9&quot;&gt;PR for these changes&lt;/a&gt;, but it has not yet been accepted. I’m not comfortable distributing this on the official extension gallery at this time. So grab it from GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/gingerbeardman/Monokai-Pro-Vibrant.novaextension/releases/&quot;&gt;github.com/gingerbeardman/Monokai-Pro-Vibrant.novaextension/releases/&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 14 May 2025 19:50:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/05/14/monokai-pro-vibrant-theme-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/05/14/monokai-pro-vibrant-theme-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>The menu bar items I use on macOS</title>
          <description>&lt;p&gt;This is an expanded version of &lt;a href=&quot;https://talk.macpowerusers.com/t/787-revisiting-our-mac-menu-bars/40090/44?u=gingerbeardman&quot;&gt;a post I made to the MPU Talk forum&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;visible&quot;&gt;Visible&lt;/h2&gt;

&lt;p&gt;Those I often need to interact with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.macbartender.com&quot;&gt;Bartender&lt;/a&gt; (menu bar item manager; old version)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sindresorhus.com/dato&quot;&gt;Dato&lt;/a&gt; (alt clock and calendar with timezones; old version)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://bjango.com/mac/istatmenus/&quot;&gt;iStat Menus&lt;/a&gt; (network speed; old version)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://xbarapp.com&quot;&gt;xBar&lt;/a&gt; (custom menu items powered by my own shell scripts)
    &lt;ul&gt;
      &lt;li&gt;Proxy Status&lt;/li&gt;
      &lt;li&gt;Software Sales Stats&lt;/li&gt;
      &lt;li&gt;GitHub Issues&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/2025/02/08/automating-the-cleaning-of-macos-specific-files-on-eject/&quot;&gt;Eject Volumes&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.caffeine-app.net&quot;&gt;Caffeine&lt;/a&gt; (used to set Mac not to sleep)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;hidden&quot;&gt;Hidden&lt;/h2&gt;

&lt;p&gt;Those I infrequently need to interact with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Bookmark manager hotkey app (my own, unreleased; supports &lt;a href=&quot;https://github.com/sissbruecker/linkding&quot;&gt;linkding&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://apps.apple.com/us/app/otp-auth/id1471867429&quot;&gt;OTP Auth&lt;/a&gt; (2FA codes)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://c-command.com/toothfairy/&quot;&gt;ToothFairy&lt;/a&gt; (Bluetooth/AirPods helper)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Pyroh/Fluor&quot;&gt;Fluor&lt;/a&gt; (specify function key behaviour per-app)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;always-hidden&quot;&gt;Always Hidden&lt;/h2&gt;

&lt;p&gt;Those I rarely need to interact with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/odlp/bluesnooze&quot;&gt;Bluesnooze&lt;/a&gt; (turns off Bluetooth when Mac sleeps)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://micropixels.software/apps/neptunes&quot;&gt;NepTunes&lt;/a&gt; (audio scrobbler and track notifications)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rogueamoeba.com/soundsource/&quot;&gt;SoundSource&lt;/a&gt; (custom audio equaliser with my hearing profile audiogram)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.deepl.com/en/macos-app&quot;&gt;DeepL&lt;/a&gt; (instant translation of copied text when doing Cmd+C twice)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.alfredapp.com&quot;&gt;Alfred&lt;/a&gt; (multi-tool with clipboard history and custom workflows)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/apexskier/DefaultBrowser&quot;&gt;Default Browser&lt;/a&gt; (open links in your most recently used/active browser)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://freemacsoft.net/tiles/&quot;&gt;Tiles&lt;/a&gt; (window manager)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hammerspoon.org&quot;&gt;Hammerspoon&lt;/a&gt; (my own window and workspace manager functions written in Lua)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🤓&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 21 Mar 2025 18:08:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/03/21/the-menu-bar-items-i-use-on-macos/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/03/21/the-menu-bar-items-i-use-on-macos/</guid>
        </item>
      
    
      
        <item>
          <title>Digging up the past with DiscMaster</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://discmaster.textfiles.com&quot;&gt;DiscMaster&lt;/a&gt; is an alternative interface for collections of software that have been uploaded to Internet Archive. It allows you to drill into disk images at the file level and preview the contents in-place, download individual folders, zips, images, or whatever you like. It’s been around for a while but has become bigger and better with the introduction of DiscMaster 2, which is ingesting huge amounts of new files daily. But how to keep up with this firehose!?&lt;/p&gt;

&lt;h2 id=&quot;automated-searching&quot;&gt;Automated Searching&lt;/h2&gt;

&lt;p&gt;One of the great things about DiscMaster is that you can subscribe to an RSS feed of any search result, which means you’ll be notified of new matches as they are indexed without the need to manually search on the website.&lt;/p&gt;

&lt;p&gt;I have a bunch of searches including &lt;a href=&quot;/2021/10/31/hypercard-hanafuda/&quot;&gt;Hanafuda&lt;/a&gt;, various computer artists and developers (&lt;a href=&quot;/2023/12/16/see-the-sky-thoru-yamamoto-christmas-story-for-playdate/&quot;&gt;Thoru Yamamoto&lt;/a&gt;), particular apps, specific file types (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PICT/SaMe&lt;/code&gt; for &lt;a href=&quot;/2023/05/04/macigame-user-created-graphics/&quot;&gt;MaciGame tilesets&lt;/a&gt;), and for purely selfish reasons… searches for my own name. It’s fun to see where my early software was distributed decades after the it happened.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;class-of-93&quot;&gt;Class of ’93&lt;/h2&gt;

&lt;p&gt;So far the oldest thing of mine is &lt;em&gt;Chaos Calc 2&lt;/em&gt;, my fractal explorer, this version from May 1993 when I was 16 years young! I have a pretty good memory and can tell you that I received zero registrations for that &lt;a href=&quot;https://en.wikipedia.org/wiki/Shareware&quot;&gt;shareware&lt;/a&gt; app, but I did get one call from somebody who wanted to be certain that their £5 GBP registration fee would absolutely definitely receive a floppy disk in return. I guess they were unconvinced because they never sent me any money!&lt;/p&gt;

&lt;p&gt;There are references to programs (this is what apps were called on the Atari ST) that I had forgotten about but after reading their names can now recall completely (palette manager, boot tools), some I can recall partially (menu system), and others I have very little recollection of (a front end for a ray tracing program). Curiously I see no mention of the apps I was most fond of at that time: a text file viewer, kids paint program, and my take on &lt;a href=&quot;https://en.wikipedia.org/wiki/Gorillas_(video_game)&quot;&gt;Gorillas&lt;/a&gt;. Perhaps they came later?&lt;/p&gt;

&lt;h2 id=&quot;chaos-calc-2&quot;&gt;Chaos Calc 2&lt;/h2&gt;

&lt;p&gt;It’s been 32 years but it only took a moment for me to recall how to use the app and what all the different menu options did, even without consulting the readme. This is running at 32MHz with maths co-processor (compared to stock 8MHz with no co-processor) as your time is valuable!&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 8/5;&quot; videoid=&quot;fizIhUlmVXo&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;wire-hang-redux&quot;&gt;Wire Hang Redux&lt;/h2&gt;

&lt;p&gt;Also fun to see my game &lt;a href=&quot;https://gingerbeardman.itch.io/wire-hang-redux&quot;&gt;&lt;em&gt;Wire Hang Redux&lt;/em&gt;&lt;/a&gt; was featured on the cover media of Retro Gamer magazine issues 7 &amp;amp; 13, which totally passed me by in 2004/2005! No surprise as that game was featured in dozens of magazines around the world: Japan, USA, Sweden, Finland, Czech Republic, Germany. It being &lt;a href=&quot;http://www.forest.impress.co.jp/article/2004/07/06/wirehangredux.html&quot;&gt;Big in Japan&lt;/a&gt; is extra fun because it is my remake of a Japanese game by “d2ac”, &lt;a href=&quot;https://www.mobygames.com/person/510170/masaki-kobayashi/&quot;&gt;Masaki Kobayashi&lt;/a&gt;.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/wire-hang-redux-japanese-feature.jpg&quot; alt=&quot;IMG&quot; title=&quot;I can&apos;t remember which Japanese Macintosh magazine this was from&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;faqs&quot;&gt;FAQs&lt;/h2&gt;

&lt;p&gt;The game guides I wrote and &lt;a href=&quot;https://gamefaqs.gamespot.com/community/msephton/contributions&quot;&gt;published on GameFAQs&lt;/a&gt; ended up on a number of different CD-ROMs. I love the idea of somebody using a disc full of text files as a resource when playing games in the mid-late 1990s.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 14 Mar 2025 22:58:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/03/14/digging-up-the-past-with-discmaster/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/03/14/digging-up-the-past-with-discmaster/</guid>
        </item>
      
    
      
        <item>
          <title>Making games on iPhone/iPad using Lua &amp; Love2D</title>
          <description>&lt;p&gt;I wondered how feasible it is to make Love2D games “on the go” (on a train, in bed, etc). Often I want to try a quick sketch, work up a prototype, or simply verify an algorithm. I’m happy to say it’s possible!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;runners&quot;&gt;Runners&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Love2D Studio&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Available since Jan 2024&lt;/li&gt;
  &lt;li&gt;Free &lt;a href=&quot;https://apps.apple.com/gb/app/love2d-studio/id6474188075&quot;&gt;apps.apple.com/gb/app/love2d-studio/id6474188075&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;this allows you to manage love files and run them on iPhone&lt;/li&gt;
  &lt;li&gt;Love2D errors can even be copied to the clipboard!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Love2D Game Maker&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Available since March 2024&lt;/li&gt;
  &lt;li&gt;Paid &lt;a href=&quot;https://apps.apple.com/gb/app/love2d-game-maker/id6476174098&quot;&gt;apps.apple.com/gb/app/love2d-game-maker/id6476174098&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;TestFlight &lt;a href=&quot;https://testflight.apple.com/join/bCLmQKfQ&quot;&gt;testflight.apple.com/join/bCLmQKfQ&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;from the creator of LuaLu (see below)&lt;/li&gt;
  &lt;li&gt;comes bundled with a handful of demo games&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.iamadman.com/products/love2d-gamemaker/love2d-game-maker-learn-to-create-the-classic-game-of-pong-in-10mins/&quot;&gt;Pong tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;editors&quot;&gt;Editors&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Textastic&lt;/strong&gt; (since 2010)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Paid &lt;a href=&quot;https://apps.apple.com/gb/app/textastic-code-editor/id1049254261&quot;&gt;apps.apple.com/gb/app/textastic-code-editor/id1049254261&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;text editor with autocomplete and function picker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Kodex&lt;/strong&gt; (since 2017)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Free &lt;a href=&quot;https://apps.apple.com/gb/app/kodex/id1038574481&quot;&gt;apps.apple.com/gb/app/kodex/id1038574481&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;text editor with minimap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GoCoEdit&lt;/strong&gt; (since 2016)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Paid &lt;a href=&quot;https://apps.apple.com/gb/app/gocoedit-code-text-editor/id869346854&quot;&gt;apps.apple.com/gb/app/gocoedit-code-text-editor/id869346854&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;text editor with autocomplete&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Runestone&lt;/strong&gt; (since 2022)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Free, limited &lt;a href=&quot;https://apps.apple.com/gb/app/runestone-text-editor/id1548193893&quot;&gt;apps.apple.com/gb/app/runestone-text-editor/id1548193893&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;text editor with clean user interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lua IDE&lt;/strong&gt; (since Feb 2021)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Free &lt;a href=&quot;https://apps.apple.com/gb/app/lua-ide/id1549382090&quot;&gt;apps.apple.com/gb/app/lua-ide/id1549382090&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;this app provides QuickLook support for Lua files&lt;/li&gt;
  &lt;li&gt;at some point you’ll need to check the contents of a Lua file in Files app&lt;/li&gt;
  &lt;li&gt;it also features the full Lua docs (though I use a self-built version of Dash doc viewer)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;LuaLu&lt;/strong&gt; (since Apr 2013)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Free &lt;a href=&quot;https://apps.apple.com/gb/app/lualu-repl-learn-lua-coding/id638219114&quot;&gt;apps.apple.com/gb/app/lualu-repl-learn-lua-coding/id638219114&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;sometimes you just need a Lua &lt;a href=&quot;https://en.wikipedia.org/wiki/Read–eval–print_loop&quot;&gt;REPL&lt;/a&gt; prompt to figure out a formula or do a quick experiment&lt;/li&gt;
  &lt;li&gt;features solid debugging support&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;other-lua-ide&quot;&gt;Other Lua IDE&lt;/h2&gt;

&lt;p&gt;If you’re into Lua but not into Love2D then you might try &lt;a href=&quot;https://codea.io&quot;&gt;Codea for iPad&lt;/a&gt;.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 20 Feb 2025 07:37:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/02/20/making-games-on-iphone-ipad-using-lua-and-love2d/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/02/20/making-games-on-iphone-ipad-using-lua-and-love2d/</guid>
        </item>
      
    
      
        <item>
          <title>Automating the cleaning of macOS-specific files on Eject</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve released a native macOS app that does all this script can do &lt;em&gt;and much more!&lt;/em&gt;&lt;br /&gt;Read all about it: &lt;a href=&quot;https://www.gingerbeardman.com/apps/driveaway/&quot;&gt;gingerbeardman.com/apps/driveaway/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dot underscore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;._&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.DS_Store&lt;/code&gt; files are macOS-specific metadata cruft generated for foreign filesystems (like FAT32 or exFAT) that are not usually needed for disks that are mainly used on other platforms. Digital cameras, music players, e-book readers, and handheld gaming devices can get confused when they encounter these odd files during file system parsing and directory listing. The problem is compounded if the devices naïvely process files by looking only at the file extension as they will then see the dot underscore version of a file as a duplicate and try to preview/play/open it.&lt;/p&gt;

&lt;p&gt;For years I’ve used an app called &lt;a href=&quot;https://web.archive.org/web/20250208072547/https://macpaw.com/cleanmymac&quot;&gt;CleanMyDrive&lt;/a&gt; to remove such files, but &lt;a href=&quot;https://macpaw.com/news/cleanmydrive-no-longer-developed&quot;&gt;it was discontinued in October 2023&lt;/a&gt;. I continued to use it until it recently stopped working completely …so I needed to find an alternative solution. There are some apps on the Mac App Store that look like they’ll do the trick, but I don’t really want to spend the time buying and trialling multiple apps to find one that fits my usage habits. I can make one!&lt;/p&gt;

&lt;p&gt;I already use an app called &lt;a href=&quot;https://xbarapp.com&quot;&gt;xbar&lt;/a&gt; for keeping track of my GitHub issues, itch.io sales, network/server status, and more. So I decided to flex my shell script muscles and put together an xbar script to do it.&lt;/p&gt;

&lt;p&gt;The script adds a menu bar item that allows you to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Eject (click)&lt;/li&gt;
  &lt;li&gt;Unmount (option-click)&lt;/li&gt;
  &lt;li&gt;Eject All (without cleaning, useful when you want to disconnect all drives from your computer)&lt;/li&gt;
  &lt;li&gt;All with a handy notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! Straight to the point, no frills, functional software.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xbar-volumes.png&quot; alt=&quot;IMG&quot; title=&quot; &quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/610f22180117ad20465d7c529cc5faa0&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/610f22180117ad20465d7c529cc5faa0.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 08 Feb 2025 20:45:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/02/08/automating-the-cleaning-of-macos-specific-files-on-eject/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/02/08/automating-the-cleaning-of-macos-specific-files-on-eject/</guid>
        </item>
      
    
      
        <item>
          <title>TETRIS: 1984 is the date of conception (1989)</title>
          <description>&lt;p&gt;Given the ongoing discussion about &lt;a href=&quot;https://www.timeextension.com/news/2024/06/anniversary-is-tetris-really-40-this-year&quot;&gt;whether or not TETRIS turns 40 years old in 2024 or 2025&lt;/a&gt;, I keep my eye out for early mentions of the game. As a bonus, this is an item that at the time of me &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1885581359655039019&quot;&gt;posting it to social media a couple of days ago&lt;/a&gt; had not been covered in English anywhere else on the internet. As far as I’m aware this is the earliest printed reference mentioning the conception date of Tetris.&lt;/p&gt;

&lt;p&gt;It’s a &lt;a href=&quot;https://archive.org/details/OhX_1989-08/page/n91/mode/1up&quot;&gt;short interview with Alexey Pajitnov&lt;/a&gt; in Japanese PC magazine, &lt;strong&gt;Oh! &lt;em&gt;X&lt;/em&gt;&lt;/strong&gt;, conducted mid-1989 around the time of the release of the seminal Game Boy version of TETRIS, the first year the game was available outside of the USSR. It contains this beautiful question and answer:&lt;/p&gt;

&lt;p&gt;Japanese text:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Q: TETRIS誕生のきっかけを教えてください。&lt;/p&gt;

  &lt;p&gt;——いまから5年前の1984年に、IQテスト用プログラムを作ろうとしたのが最初です。そのとき、ペントミノというゲームをベースに5～8個からなるブロックを落下させ、それを回転させて組み合わせるということを考えて作ったのがこのゲームなのです。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;English translation:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Q: Please tell us about how TETRIS came to be created.&lt;/p&gt;

  &lt;p&gt;A: It started 5 years ago in 1984, when I was trying to create a program for IQ testing. At that time, I created this game based on a game called Pentomino, where I came up with the idea of having blocks made up of 5–8 pieces fall down and rotate them to combine them together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; pentomino are comprised of 5 pieces whilst tetromino are comprised of 4 pieces, so the version referred to is certainly an early prototype. I’m not aware of a surviving version of &lt;em&gt;whatever TETRIS was first known as&lt;/em&gt; that has blocks of 5–8 pieces.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-date-of-conception-1984.jpg&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;bibliography&quot;&gt;Bibliography&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Publication: Oh! &lt;em&gt;X&lt;/em&gt;, Vol. 8, No. 8 (August 1989)&lt;/li&gt;
  &lt;li&gt;ISSN: 0910-7614&lt;/li&gt;
  &lt;li&gt;Section: TETRISの原作者パジトノフ氏来日 (TETRIS creator Pajitnov visits Japan)&lt;/li&gt;
  &lt;li&gt;Pages: 89–90&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/OhX_1989-08/page/n91/mode/1up&quot;&gt;Source scan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 03 Feb 2025 17:20:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/02/03/tetris-date-of-conception-is-1984/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/02/03/tetris-date-of-conception-is-1984/</guid>
        </item>
      
    
      
        <item>
          <title>Word Counter extension for Nova editor</title>
          <description>&lt;p&gt;So the &lt;a href=&quot;/2024/10/24/macro-extension-for-nova-editor/&quot;&gt;Macro extension&lt;/a&gt; and &lt;a href=&quot;/2024/10/17/extensions-for-nova-editor/&quot;&gt;a bunch of smaller extensions&lt;/a&gt; were supposed to be it, but it’s so much fun to create these that whenever I get an idea for an extension I can’t help making it.&lt;/p&gt;

&lt;p&gt;This time I wanted to keep track of how many times I’m calling certain functions in my code. That’s quite a niche requirement so I made a more general purpose word counter with sidebar, thresholds and coloured blobs!&lt;/p&gt;

&lt;p&gt;You can configure a list of words to count, and the thesholds that will trigger the grey/green/yellow/red coloured blobs, on a per-project basis using the workspace &lt;em&gt;Project Settings…&lt;/em&gt; panel.&lt;/p&gt;

&lt;p&gt;And you can double-click on a word in the sidebar to search the current document for it. This feature uses AppleScript so you’ll need to give Nova permissions, though it should prompt you to do that if it doesn’t have them already.&lt;/p&gt;

&lt;p&gt;Note: the screenshot below is slightly old as I’ve since refined the images in the extension.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-word-counter.png&quot; alt=&quot;PNG&quot; title=&quot;Counting some Playdate SDK specific function names&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;downloads&quot;&gt;Downloads&lt;/h2&gt;

&lt;p&gt;You can download it at: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.wordcounter/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.wordcounter/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the &lt;a href=&quot;https://github.com/gingerbeardman/Word-Counter&quot;&gt;source code is available on GitHub&lt;/a&gt; and PRs are more than welcome!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 27 Oct 2024 17:12:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/27/word-counter-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/27/word-counter-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Macro extension for Nova editor</title>
          <description>&lt;p&gt;After creating &lt;a href=&quot;/2024/10/17/extensions-for-nova-editor/&quot;&gt;a bunch of smaller Nova Extensions quite quickly&lt;/a&gt; I wondered how far I could push things, just as a personal challenge. I had the idea of implementing a Macro text recording and playback system. There was something similar in TextMate editor, and whilst the Nova API doesn’t currently allow as sophisticated a system I thought it was something still worth exploring.&lt;/p&gt;

&lt;p&gt;Design of the system was incremental, with improvements and simplifications that came about through using it. It took a few aborted attempts to land on a good mechanism of recording and playing back “actions”, which currently encompass:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;insertions&lt;/li&gt;
  &lt;li&gt;deletions&lt;/li&gt;
  &lt;li&gt;cursor position changes&lt;/li&gt;
  &lt;li&gt;selection changes&lt;/li&gt;
  &lt;li&gt;selection replacements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I encode these changes with some simple JSON markup. Insertions are represented as text, and replacements as pairs of text, whilst deletions, cursor position changes, and selection changes are represented as numbers where positive means “to the right” and negative means “to the left”. It’s a simple system but should prove to be expandable if the Nova API is expanded to include extra capabilities, such as hooking into the &lt;em&gt;Find&lt;/em&gt;, &lt;em&gt;Files&lt;/em&gt; sidebar, or the &lt;em&gt;Command Palette&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Macro data can end up quite large, as we’re recording single character or position changes, so I added the ability to compress a macro by coalescing actions of the same type into one. A sort of naïve huffman encoding scheme. This makes a huge difference to both size and playback speed.&lt;/p&gt;

&lt;p&gt;You can download the extension at: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Macro/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Macro/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;recording&quot;&gt;Recording&lt;/h2&gt;

&lt;p&gt;Recording is done on-demand, only when you want it. A notification will signal recording has begun. When you’ve finished you click the Stop button in the notification and the macro will be automatically saved to the sidebar with a sequential name. You can rename it afterwards if you’d like.&lt;/p&gt;

&lt;p&gt;You can also start the recording using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; sidebar icon, or using the command via the Editor menu or &lt;em&gt;Command Palette&lt;/em&gt;, but recording is always stopped through the notification. It took a while to arrive at this mechanism, and for a long time the extension was a lot more complicated and confusing to use.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-macro-extension-record.png&quot; alt=&quot;IMG&quot; title=&quot;Macro recording notification&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;example-macro&quot;&gt;Example Macro&lt;/h2&gt;

&lt;p&gt;Let’s type:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Nova!&amp;lt;move left 1&amp;gt;&amp;lt;select -3&amp;gt;&amp;lt;replace &quot;ova&quot; → &quot;O&quot;&amp;gt;VA&amp;lt;move left 3&amp;gt;&amp;lt;delete 3&amp;gt;ova&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The end result of which is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Nova!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were to record this, the results would be…&lt;/p&gt;

&lt;p&gt;Raw macro (17 actions):&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Macro 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;actions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;N&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;o&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;old&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ova&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;new&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;O&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;V&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;o&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isExpanded&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compressed macro (6 actions):&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Nova!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;old&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ova&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;new&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;O&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;VA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ova&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compression also helps with readability though I added a function to copy a human-readable version of the macro,which is how the &lt;a href=&quot;#example-macro&quot;&gt;text at the start of this example&lt;/a&gt; was generated.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-macro-extension-anim.gif&quot; alt=&quot;IMG&quot; title=&quot;Playback of the above macro&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;sidebar&quot;&gt;Sidebar&lt;/h2&gt;

&lt;p&gt;Recorded macros live in a bespoke sidebar, and can be expanded or collapsed on demand. This allows us to see each action in the macro and exactly how the compressed version differs. The context menu provides functions to manipulate and manage the macros.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-macro-extension-sidebar.png&quot; alt=&quot;IMG&quot; title=&quot;Macro sidebar with raw and compressed versions, expanded to show all actions&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;context-menu&quot;&gt;Context Menu&lt;/h3&gt;

&lt;p&gt;After a macro is recorded you can manipulate it using the context menu of items in the Macro sidebar:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Replay&lt;/li&gt;
  &lt;li&gt;Compress&lt;/li&gt;
  &lt;li&gt;Duplicate&lt;/li&gt;
  &lt;li&gt;Rename&lt;/li&gt;
  &lt;li&gt;Copy (Compressed)&lt;/li&gt;
  &lt;li&gt;Copy (Raw)&lt;/li&gt;
  &lt;li&gt;Copy (Readable)&lt;/li&gt;
  &lt;li&gt;Delete&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;configuration&quot;&gt;Configuration&lt;/h2&gt;

&lt;p&gt;Recording selections adds extra weight and slowness to a macro, so whilst the extension defaults to recording text selection changes I added the option to switch it off. If you’re only interested in the smallest size macros you can enable automatic compression of every macro which happens as the macro is saved.&lt;/p&gt;

&lt;p&gt;Playback is very quick but in some situations, such as playing back for a screen recording, you might want the text to appear more slowly, so I added a playback speed option.&lt;/p&gt;

&lt;p&gt;Note: if you want to see the smoothest character-by-character playback, you should make sure compression and record selection changes are both switched off.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;improvements&quot;&gt;Improvements&lt;/h2&gt;

&lt;p&gt;There are still some improvements I’d like to make in the future: to be able to more easily see the cursor during playback, and to highlight the current action in the sidebar so you can keep track of progress. We’ll see if/when I get around to those.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/gingerbeardman/Macro&quot;&gt;source code is available on GitHub&lt;/a&gt; and PRs are more than welcome!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 24 Oct 2024 14:02:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/24/macro-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/24/macro-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Extensions for Nova editor</title>
          <description>&lt;p&gt;I’m a big believer in solving problems yourself if it’s possible rather than waiting for app updates that might never arrive. Making extensions for the &lt;a href=&quot;https://nova.app&quot;&gt;Nova editor&lt;/a&gt; that I do most of my programming and blogging in is so much fun! So, here are some of my own creation:&lt;/p&gt;

&lt;p&gt;View all: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;yaml-tag-picker&quot;&gt;YAML Tag Picker&lt;/h2&gt;

&lt;p&gt;Allows you to easily select tags for the front matter in your blog posts. It scans your existing posts for tags and presents them in a Choice Palette, making it easy to maintain consistent tagging across your blog. Nice!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yaml-tag-picker.png&quot; alt=&quot;IMG&quot; title=&quot;Searching existing tags for the word “play”&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;markdown-file-linker&quot;&gt;Markdown File Linker&lt;/h2&gt;

&lt;p&gt;Allows you to insert links to local files as Markdown, perfect for linking between articles in your Jekyll blog! Also includes the ability to wrap the current text in a link, and if there’s a URL on the clipboard that’ll be used as the link destination.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You choose a local file using the file selector, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users/matt/Projects/blog/_posts/2023/2023-11-21-yoyozo-how-i-made-a-playdate-game-in-39kb.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it will be inserted as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve selected some text before invoking the extension, you’ll get:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[YOYOZO](/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an image you might end up with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;![IMG](∕images/posts/yoyozo-teaser.gif)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;reindent-o-matic&quot;&gt;Reindent-o-matic&lt;/h2&gt;

&lt;p&gt;Allows you to apply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.editorconfig&lt;/code&gt; indent rules to the current file, or all files matching specific extensions. Important: changes are applied but not saved, giving you the opportunity to review.&lt;/p&gt;

&lt;p&gt;Useful when reusing code from one project that used space indentation to a new one that uses tab indentation. Ideally Nova should do this automatically, and sometimes it does, but not every time for reasons I can’t figure out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Reindent-o-matic/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Reindent-o-matic/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;toggle-scroll-bars&quot;&gt;Toggle Scroll Bars&lt;/h2&gt;

&lt;p&gt;Allows you to see the scrollbar at all times. This is useful because the scrollbar also contains source control change markers. With scrollbars always visible you can more easily locate changes across the entire length of your document.&lt;/p&gt;

&lt;p&gt;This has already saved me so much time and effort and I’ve only been using the editor this way for a week. So I thought I’d create an extension to make it easier for other people to enjoy this usability improvement.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.scrollbars/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.scrollbars/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scroll Bars on/off&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/scrollbars-on-minimap-off.png&quot; alt=&quot;on-off&quot; /&gt; &lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/scrollbars-off-minimap-off.png&quot; alt=&quot;off-off&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;unwrap-paragraph&quot;&gt;Unwrap Paragraph&lt;/h2&gt;

&lt;p&gt;OK, I got carried away. This one adds the ability to merge multiple lines of selected text into one.&lt;/p&gt;

&lt;p&gt;This is useful for combining multiple lines of data into one, or reflowing a paragraph of text that has had manual line breaks applied such as something pasted from an old email. It also colalesces areas of white space into a single space. So, it’s not just “Join Lines” but something more specific.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;TextMate&lt;/em&gt; editor this command is called Unwrap Paragraph, so I’ve kept the same name.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;Text, before:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	What 
exactly does  
this   extension	
do with the
text?  🤔
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Text, after:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;What exactly does this extension do with the text? 🤔
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source code, before:&lt;/p&gt;
&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x202&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x451&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x505&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x088&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x272&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source code, after:&lt;/p&gt;
&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x202&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x451&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x505&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x088&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x272&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;filter-through-command&quot;&gt;Filter Through Command&lt;/h2&gt;

&lt;p&gt;Run terminal commands on selected text. When you need to use a terminal command but you just want the results. The possibilities are endless as command line is your oyster! Comes with a library of useful commands including sort, base64 decode/encode, extract URLs, remove blank lines, count loc, remove HTML tags.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FilterThroughCommand/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FilterThroughCommand/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type your own command&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-filter-through-custom-command.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick from a library of commands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-filter-through-command.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;bookmarks&quot;&gt;Bookmarks&lt;/h2&gt;

&lt;p&gt;Bookmark files in the Sidebar for easier management. Ideal if the Files Sidebar is too much, or you want a simpler view of the files you are working on.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;tag-sync&quot;&gt;Tag Sync&lt;/h2&gt;

&lt;p&gt;Automatic synchronisation of closing tag when editing opening tag. Improve your coding efficiency by ensuring tag pairs stay synchronized while editing markup languages!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.tagsync/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.tagsync/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;openscad&quot;&gt;OpenSCAD&lt;/h2&gt;

&lt;p&gt;Adds language support for &lt;a href=&quot;https://openscad.org&quot;&gt;OpenSCAD&lt;/a&gt;: &lt;em&gt;The Programmers Solid 3D CAD Modeller&lt;/em&gt;, including both syntaxes (with highlighting) and completions (with alternate forms).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.openscad/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.openscad/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;macro&quot;&gt;Macro&lt;/h2&gt;

&lt;p&gt;Text editing recording and playback system. This one &lt;a href=&quot;/2024/10/24/macro-extension-for-nova-editor/&quot;&gt;has its own blog post&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;word-counter&quot;&gt;Word Counter&lt;/h2&gt;

&lt;p&gt;Count multiple words and have their tally displayed in the sidebar. This one also &lt;a href=&quot;/2024/10/27/word-counter-extension-for-nova-editor/&quot;&gt;has its own blog post&lt;/a&gt;.&lt;/p&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 17 Oct 2024 20:47:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/17/extensions-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/17/extensions-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Automating the most annoying aspects of blogging</title>
          <description>&lt;p&gt;Back in 2021 I had a look around and decided to base this incarnation of my blog on an open-source &lt;a href=&quot;https://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt; theme called “&lt;a href=&quot;https://github.com/ahmadajmi/type&quot;&gt;Type&lt;/a&gt;”, though I’ve changed and added so much it’s quite some distance from the original as it stands today. As I added blog posts the performance became much worse. Initially I blamed Jekyll for this, until I took a closer look. What I learned was that the blog theme did some things in sub-optimal ways, so over the course of 2024 I have corrected as many of them as I can. Build time dropped from ~12 seconds to ~1 second.&lt;/p&gt;

&lt;p&gt;My fixes and optimisations included:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Optimised SASS to compile once in plugin rather than header of every page (23 Feb)&lt;/li&gt;
  &lt;li&gt;Optimised all includes and templates to reduce build time (19 Aug)&lt;/li&gt;
  &lt;li&gt;Improved “noun” replacement, which are automatically-emphasised words (3 Sep)&lt;/li&gt;
  &lt;li&gt;Better YouTube embeds using lite-yt-embed (11 Sep)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And my additions along the way:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Multi-carousel support (14 Jul)&lt;/li&gt;
  &lt;li&gt;Automatic transformation of local images urls to CDN urls (19 Aug)&lt;/li&gt;
  &lt;li&gt;Automatic smart quotes in post titles (19 Aug)&lt;/li&gt;
  &lt;li&gt;Added OpenSearch to allow searching blog from address bar (15 Sep)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might ask… what’s left to do? Not much I reckon. So I took a closer look at what is involved in me creating a blog post and made a list of the most repetitive, awkward or error prone tasks:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Entering links, partcularly links to other blog posts&lt;/li&gt;
  &lt;li&gt;Entering tags, going from memory or using search across project&lt;/li&gt;
  &lt;li&gt;Getting images onto my CDN server, currently copy by SFTP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s solve all these annoyances!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;entering-links-and-tags&quot;&gt;Entering links and tags&lt;/h2&gt;

&lt;p&gt;You can read about these two in previous blog posts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Created &lt;a href=&quot;/2024/10/08/markdown-file-linker/&quot;&gt;Markdown File Linker&lt;/a&gt; to make linking easier (8 Oct)&lt;/li&gt;
  &lt;li&gt;Created &lt;a href=&quot;/2024/10/08/yaml-tag-picker/&quot;&gt;YAML Tag Picker&lt;/a&gt; to make tagging easier (8 Oct)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;getting-images-onto-my-cdn-server&quot;&gt;Getting images onto my CDN server&lt;/h2&gt;

&lt;p&gt;I was still having to copy my images to my CDN server manually, which was a pain. But the software stack on the server was intimidating. I kept putting it off for a rainy day, but I knew I would eventually get around to automating it.&lt;/p&gt;

&lt;p&gt;The idea was to not upload the images at all, but rather download them to the server directly. I’d use a GitHub Webhook to trigger a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; of the latest files onto my server. It took a few hours and a few attempts, but I finally arrived at a fairly elegant system I’m happy with:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;GitHub Webhook that runs on push event&lt;/li&gt;
  &lt;li&gt;PHP script in web server docker container receives, validates, and creates a trigger file&lt;/li&gt;
  &lt;li&gt;Service on server outside of docker looks for trigger file and does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: in January 2025, since this post was originally written, I have simplified the following files after migrating away from web servers in Docker containers to &lt;a href=&quot;https://caddyserver.com&quot;&gt;Caddy server&lt;/a&gt;. Highly recommended!&lt;/p&gt;

&lt;h3 id=&quot;github-webhook&quot;&gt;GitHub Webhook&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Payload URL: https://www.example.com/webhook.php&lt;/li&gt;
  &lt;li&gt;Content type: application/json&lt;/li&gt;
  &lt;li&gt;SSL verification: enabled&lt;/li&gt;
  &lt;li&gt;Which events would you like to trigger this webhook: Just the push event&lt;/li&gt;
  &lt;li&gt;Active: on&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;webhook-receiver-php-script&quot;&gt;Webhook receiver (PHP script)&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/63e4dc0bce459ad6609c2701963eb61f&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/63e4dc0bce459ad6609c2701963eb61f.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;webhook-git-pull-watcher-shell-script&quot;&gt;Webhook git pull watcher (Shell script)&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/e1c513c69b9e9d41aa91155893ae7334&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/e1c513c69b9e9d41aa91155893ae7334.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;trigger-system-service&quot;&gt;Trigger (System service)&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/1ff95ce64a6a255919b8262dd4a21bc7&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/1ff95ce64a6a255919b8262dd4a21bc7.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;access-control&quot;&gt;Access control&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/23e0da9949a8d41ebd190d60b0bd033b&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/23e0da9949a8d41ebd190d60b0bd033b.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;setup&quot;&gt;Setup&lt;/h3&gt;

&lt;p&gt;You can set up the service using:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl start git-pull-watcher&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl enable git-pull-watcher&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other commands&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl restart git-pull-watcher&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl status git-pull-watcher&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 11 Oct 2024 16:42:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/11/automating-the-most-annoying-aspects-of-blogging/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/11/automating-the-most-annoying-aspects-of-blogging/</guid>
        </item>
      
    
      
        <item>
          <title>This behavior is by design</title>
          <description>&lt;p&gt;Back in the mid-1990s I was using Windows 95/98 and running up against bugs, problems, driver issues, unexpected things happening. I would often end up on the Microsoft Knowledge Base support web pages, where the issue would often be accompanied by the line “&lt;a href=&quot;https://support.microsoft.com/en-gb/topic/you-can-change-the-desktop-wallpaper-setting-after-administrator-selects-prevent-changing-wallpaper-option-in-group-policy-e3af8a03-69f5-e320-42dc-15702ba5375c&quot;&gt;This behavior is by design&lt;/a&gt;”, which remains in use to this very day.&lt;/p&gt;

&lt;p&gt;This mantra has stuck with me over the years, and it came to light in a recent discussion about the design of &lt;a href=&quot;https://en.wikipedia.org/wiki/Lucas_Pope&quot;&gt;Lucas Pope&lt;/a&gt;’s &lt;a href=&quot;https://play.date/games/mars-after-midnight/&quot;&gt;Mars After Midnight&lt;/a&gt; for the &lt;a href=&quot;https://play.date&quot;&gt;Playdate&lt;/a&gt; handheld game console. It has a black interstitial screen between some scenes, which was enough to get me thinking about the intent behind the design of a screen that contains …nothing at all.&lt;/p&gt;

&lt;h2 id=&quot;to-be-or-not-to-be&quot;&gt;To be or not to be&lt;/h2&gt;

&lt;p&gt;I’m as guilty as anybody in wondering why certain things are how they are in the software, apps, or games I’m using. It’s a fundamental truth about software development that often goes unnoticed by end users: every aspect of computer software is the result of deliberate human decisions, from the broadest feature sets right down to the placement of individual pixels.&lt;/p&gt;

&lt;p&gt;This intentionality in software design has profound implications. It means that the user experience—whether frustrating or delightful—stems from choices made by developers, designers, product managers, and perhaps even the users themselves. How enjoyable or not the software is to use, the accessibility (or lack thereof) of user interfaces, and even the bugs and glitches we encounter are all products of the human decision-making process. This perspective challenges the notion that technology is impersonal, as there are human minds and motivations behind every aspect.&lt;/p&gt;

&lt;p&gt;However, it’s worth noting that while every element is designed, not all design decisions are made with equal deliberation or foresight. Some choices are carefully considered and tested, while others might be rushed, overlooked, or based on flawed assumptions. Additionally, the complexity of modern software means that interactions between different design choices can sometimes lead to unintended consequences. More on that later.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;

&lt;p&gt;Given that we could go on forever with examples, here are 10 off the top of my head:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;whether or not there’s a black screen between scenes in a game&lt;/li&gt;
  &lt;li&gt;size of padding or border around the edge of a dialog box&lt;/li&gt;
  &lt;li&gt;radius of a rounded rectangle&lt;/li&gt;
  &lt;li&gt;spacing or kerning between specific letters of a font&lt;/li&gt;
  &lt;li&gt;aliasing or anti-aliasing around the edge of a circle&lt;/li&gt;
  &lt;li&gt;speed at which an object moves across the screen&lt;/li&gt;
  &lt;li&gt;what happens when you press a button or do an action&lt;/li&gt;
  &lt;li&gt;wording on a menu item or dialog box&lt;/li&gt;
  &lt;li&gt;order of two competing buttons that mean cancel or proceed&lt;/li&gt;
  &lt;li&gt;the number of digits used to represent a high score&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Making a game involves deciding on thousands upon thousands of tiny things like this. I love it.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;happy-accidents&quot;&gt;Happy accidents&lt;/h2&gt;

&lt;p&gt;While most aspects of software are meticulously planned, sometimes unintended behaviors or glitches lead to serendipitous discoveries. These “happy accidents” might result in a breakthrough in design and can become beloved features if developers choose to keep them.&lt;/p&gt;

&lt;p&gt;A famous example is the “creeper” in Minecraft, which resulted from a coding error while creating pigs. The game’s creator, &lt;a href=&quot;https://en.wikipedia.org/wiki/Markus_Persson&quot;&gt;Markus Persson&lt;/a&gt;, liked the mistaken entity so much that he decided to keep and refine it, turning it into one of the game’s most iconic elements. Similarly, the &lt;a href=&quot;https://www.ssbwiki.com/Wavedash&quot;&gt;wavedash technique in Super Smash Bros. Melee&lt;/a&gt; was an unintended consequence of the game’s physics engine, but it became a crucial part of high-level play after creator &lt;a href=&quot;https://www.ssbwiki.com/Masahiro_Sakurai&quot;&gt;Masahiro Sakurai&lt;/a&gt; opted to leave it in. These instances remind us that even in a world of intentional design, there’s room for the unexpected—and the human decision to embrace and incorporate these accidents can lead to some of the most innovative and engaging aspects of software.&lt;/p&gt;

&lt;h2 id=&quot;deciding-by-not-deciding&quot;&gt;Deciding by not deciding&lt;/h2&gt;

&lt;p&gt;Some might argue that you don’t make a choice when you accept the default settings, like the white background and font in a Word document. Or you might make the same argument about using an existing framework, package, template, or SDK in your software. However, I would argue that by using the defaults you are implicitly agreeing with the designers’ choices. As a user you become complicit in the design decisions simply by not changing them.&lt;/p&gt;

&lt;p&gt;This tacit agreement with default settings is another aspect of design that often goes unnoticed, yet it plays a significant role in shaping the end product. Somebody at Microsoft decided that with Office 2007 the default font would become Calibri, replacing Times New Roman which had been the default since forever. There’s no better example of the implications and reach of a single human decision.&lt;/p&gt;

&lt;h2 id=&quot;thinking-about-the-future&quot;&gt;Thinking about the future&lt;/h2&gt;

&lt;p&gt;At the end of the day, computers do only what we direct them to do, so every aspect of software has been touched by the hand of a human. With the rise of machine learning and “AI” coding assistants, that human touch may become increasingly abstracted from view. However, rest assured it is still there. Even in systems that utilize artificial intelligence, humans are designing the algorithms, curating and creating the training data, and making decisions about how to implement and apply these tools. The fundamental truth remains: behind every line of code, every pixel, and every feature, there are human decisions shaping our experiences.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 28 Aug 2024 21:46:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/08/28/this-behavior-is-by-design/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/08/28/this-behavior-is-by-design/</guid>
        </item>
      
    
      
        <item>
          <title>Stapler: I remade a 32 year old classic Macintosh app</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;The latest version of this app is now available as a free download on the Mac App Store.&lt;br /&gt;
More info on my apps website at: &lt;a href=&quot;https://www.gingerbeardman.com/apps/stapler&quot;&gt;gingerbeardman.com/apps/stapler/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A couple of days ago I was &lt;a href=&quot;https://news.ycombinator.com/item?id=41192547&quot;&gt;reading on Hacker News about a feature in some &lt;em&gt;Linux&lt;/em&gt; window managers where they allow collections of tabs from different apps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This reminded me of &lt;em&gt;BeOS&lt;/em&gt;, but at the same time it reminded me of an app from 1992 for classic Macintosh called &lt;a href=&quot;https://macintoshgarden.org/apps/stapler-11&quot;&gt;&lt;em&gt;Stapler&lt;/em&gt;&lt;/a&gt;, and how &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1590051288951443456&quot;&gt;I’d talked about that&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1596573654674034691&quot;&gt;it’s one-time spiritual successor&lt;/a&gt;, &lt;a href=&quot;http://hasseg.org/launchList/&quot;&gt;&lt;em&gt;LaunchList&lt;/em&gt;&lt;/a&gt;, in the past. These were both similar apps that allowed you to collect and launch all the apps, files, folders, documents, related to a specific task, thus saving time. Or as Ali Rantakari so succinctly put it in 2009: “&lt;a href=&quot;http://hasseg.org/blog/post/249/launching-lots-of-stuff-at-once-on-your-mac/&quot;&gt;Opening a Bunch of Stuff at Once on Your Mac&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;So over the past day or so I built my own take on this concept for modern macOS!&lt;/p&gt;

&lt;p&gt;Whilst my app is inspired by both Stapler (right down to the name) and LaunchList, my app adds improvements and features of its own and is more of a hybrid of both. For example, Stapler didn’t have drag and drop and LaunchList didn’t have zero-click auto-launch. My goal was to keep it as simple as possible, but no simpler. To that end, there are no preferences or settings in the app.&lt;/p&gt;

&lt;hr /&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/stapler.png&quot; alt=&quot;PNG&quot; title=&quot;Stapler, for modern macOS (12.0 or newer)&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;tell-me-more&quot;&gt;Tell me more&lt;/h2&gt;

&lt;p&gt;The idea is you set up a Stapler Document per project containing related apps, files, folders, etc. Then you can open them all at once by launching the single document. Each document contains a list of aliases that can be managed, inspected, launched using the app. The key time-saver is that if you launch a Stapler Document directly, all the items in its list will automatically be launched. Cool!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Work: text editor, run current game, pixel art editor, bitmap font app, todo list&lt;/li&gt;
  &lt;li&gt;Play: Music app, Hacker News app, Twitter app, script to position windows&lt;/li&gt;
  &lt;li&gt;Movie: run Caffeine to keep your computer on, shortcut to put displays to sleep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s an odd way of thinking about working on a computer—it’s task-based rather than app-based or document-based. Indeed, some might say it’s an outdated way of approaching things. But I’m always &lt;a href=&quot;https://www.youtube.com/playlist?list=PLfF-zlMNYMd_ZioGb0BKdqwWnx0wCDSgU&quot;&gt;banging the drum&lt;/a&gt; about there being so much good stuff that was prematurely left behind along with the memories of System 7 (&lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh/&quot;&gt;though I still use that&lt;/a&gt;), BeOS, OS/2, Amiga Workbench, GEM and the many alternative desktops of Atari ST, etc. So I thought I’d see if I could walk the walk as well as talk the talk.&lt;/p&gt;

&lt;h2 id=&quot;details&quot;&gt;Details&lt;/h2&gt;

&lt;p&gt;It’s written in Swift and SwiftUI and weighs in at 640KB, about one third of which is a multitude of icon files at many different sizes and resolutions. By creating a Document-Based App you get a ton of functionality for free, such as document/tab/window management, undo/redo (though I still needed to watch for it and refresh the app window), and much more.&lt;/p&gt;

&lt;p&gt;The editor is a native macOS window that’s kind of like list view in a file manager, or a spreadsheet, or a little folder…depending on your point of view. Plus some menu commands and keyboard equivalents. You can have multiple windows open at once, for example one window each for all your common tasks, giving you the ability to launch full tasks or parts of them with minimal effort.&lt;/p&gt;

&lt;p&gt;The items in each list are macOS &lt;a href=&quot;https://eclecticlight.co/2020/05/21/bookmarks-a-type-of-alias-their-access-and-use/&quot;&gt;bookmarks&lt;/a&gt; (sometimes: security scoped bookmarks) which are a type of authorised/verified/secure alias (in fact, they’re still called aliases in the code) that have been around for about 10–15 years. They contain the path plus a bunch more info. As macOS becomes more locked-down the recommended way of accessing files is to retrieve these bookmarks through the normal layers of system permissions and security. Without the bookmarks, for example just using plain text paths, I would not be able to show the full images in Quick Look or easily launch the list items. A key benefit is that the bookmark will still resolve if the file is moved somewhere else on the same disk, or even to a different volume!&lt;/p&gt;

&lt;p&gt;I store the items as JSON in the saved file, simply because I prefer it to XML (which is the main/default option). I wanted the files to still be human readable and editable to a degree.&lt;/p&gt;

&lt;p&gt;The files are launched using the default app specified by that file, so it can be changed on a per-file basis. Individual images might open in an image editor, image viewer, app to run OCR, script to run OCR on it, etc.&lt;/p&gt;

&lt;p&gt;Dealing with files was both cool and annoying, cool that you can get the bookmarks to files so easily, but annoying that you have to jump through so many hoops to work around the security and sandbox protections and end up having to do file requests in a long-winded way, and then there’s having to tweak plist entries to give the app just the right permissions. I’m using some features of SwiftUI that mean the app can’t run on anything before macOS 14 Sonoma. All-in-all I’d say modern macOS development is a bit of a mixed bag. Take it or leave it.&lt;/p&gt;

&lt;h2 id=&quot;bonus-tip&quot;&gt;Bonus tip&lt;/h2&gt;

&lt;p&gt;Make sure to &lt;em&gt;uncheck&lt;/em&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System Settings&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Desktop &amp;amp; Dock&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Close windows when quitting an application&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then leave the windows of an app open as you quit it. When you next launch the app its windows will restore to their previous size and position. If you close the windows first, then the app will restore to having no windows open.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;thanks&quot;&gt;Thanks&lt;/h2&gt;

  &lt;p&gt;My good friend Dave Roberts (Serendipity App Company) for brainstorming why it was so damned difficult to get the app to react differently when a document was opened from Finder. Rip it up and start again! To Dustin Mierau for kickstarting this trend of remaking forgotten old apps for modern macOS. And eternal thanks to &lt;a href=&quot;https://twitter.com/chrispatterson/status/1822396663425532259&quot;&gt;Chris Patterson&lt;/a&gt; and Ali Rantakari for their apps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;since-launch&quot;&gt;Since launch&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;support added for macOS 12 and newer (was originally macOS 14 and newer)&lt;/li&gt;
  &lt;li&gt;new features added&lt;/li&gt;
  &lt;li&gt;omissions addressed&lt;/li&gt;
  &lt;li&gt;bug fixes&lt;/li&gt;
  &lt;li&gt;new icon on the way&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;elsewhere&quot;&gt;Elsewhere&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=41212193&quot;&gt;Hacker News&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tidbits.com/2024/09/02/appbits-stapler-opens-multiple-apps-documents-and-folders/&quot;&gt;TidBits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 10 Aug 2024 21:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/08/10/stapler-i-remade-a-32-year-old-classic-macintosh-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/08/10/stapler-i-remade-a-32-year-old-classic-macintosh-app/</guid>
        </item>
      
    
      
        <item>
          <title>TETRIS for Sharp Electronic Notebook (1989)</title>
          <description>&lt;p&gt;Sharp’s Denshi Techō (電子手帳) were a range of electronic notebooks or organisers, marketed as &lt;a href=&quot;https://www.g-mark.org/gallery/winners/9cad4b2a-803d-11ed-862b-0242ac130002&quot;&gt;Bware&lt;/a&gt; in Japan and &lt;a href=&quot;https://en.wikipedia.org/wiki/Sharp_Wizard&quot;&gt;Wizard or IQ&lt;/a&gt; elsewhere. In Japan they won a prestigious &lt;a href=&quot;https://www.g-mark.org/gallery/winners/9cad4b2a-803d-11ed-862b-0242ac130002&quot;&gt;G-mark Good Design Award in 1988&lt;/a&gt; and in the USA &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Wizard_(Seinfeld)&quot;&gt;an episode of Seinfeld&lt;/a&gt; was the prize.&lt;/p&gt;

&lt;p&gt;As well as being &lt;a href=&quot;/2024/05/10/emoji-history-the-missing-years/&quot;&gt;home to some of the earliest emoji&lt;/a&gt; these devices could run software from IC cards (solid state storage). There were about 30 games released in this format, everything from classics like &lt;em&gt;Sokoban&lt;/em&gt;, &lt;em&gt;Shanghai&lt;/em&gt;, &lt;em&gt;Lode Runner&lt;/em&gt;, &lt;em&gt;Puzznic&lt;/em&gt;, &lt;em&gt;Fortress&lt;/em&gt;, to everygreen games like &lt;em&gt;Golf&lt;/em&gt;, &lt;em&gt;Baseball&lt;/em&gt;, &lt;em&gt;Mahjong&lt;/em&gt;, &lt;em&gt;Shogi&lt;/em&gt;, &lt;em&gt;Othello&lt;/em&gt;, &lt;em&gt;Solitaire&lt;/em&gt;, &lt;em&gt;President&lt;/em&gt; (card game), exclusive games like &lt;em&gt;Yamamura Misa Suspense – The Kyoto Zai-tech Murder Case&lt;/em&gt;, and some contemporary titles like &lt;em&gt;Hatris&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And perhaps most importantly: a version of &lt;em&gt;Tetris&lt;/em&gt;, released in December 1989. &lt;a href=&quot;/2024/06/18/tetris-heavenly-scrolls/&quot;&gt;Tetris-mania was in full effect&lt;/a&gt; and the &lt;em&gt;Game Boy&lt;/em&gt; had recently received its version of &lt;em&gt;Tetris&lt;/em&gt; in April 1989.&lt;/p&gt;

&lt;p&gt;There’s not much about these devices online, particularly in English, but two notable places are: &lt;a href=&quot;https://www.hpmuseum.org/forum/thread-16524.html&quot;&gt;HP Museum forum&lt;/a&gt; (for devices and IC cards) and &lt;a href=&quot;https://forums.insertcredit.com/t/stay-sharp-with-sc-denshi-system-techo-games/2326&quot;&gt;Insert Credit forum&lt;/a&gt; (for games). The devices are common on Japanese auction and selling websites, but the games are becoming more difficult to find.&lt;/p&gt;

&lt;p&gt;This was, as far as I am aware, the second ever handheld version of Tetris. At the time of its release &lt;a href=&quot;https://en.wikipedia.org/wiki/Japanese_asset_price_bubble&quot;&gt;Japan was in the “bubble era” period of economic boom&lt;/a&gt;, which lasted 1986–1991. There was so much money floating around they didn’t know what to do with it, so much of it was ploughed into research and development of crazy things in the fields of consumer electronics, computers, engine technology, vehicle design, book design, software distribution, etc.&lt;/p&gt;

&lt;p&gt;Without this device &lt;a href=&quot;/2024/05/10/emoji-history-the-missing-years/#1994&quot;&gt;there would be no Apple Newton&lt;/a&gt; (which was co-produced by Sharp), without Newton there would be no iPhone, and without iPhone there would be no Android. Evolution is necessary for technological progress.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Thanks to &lt;a href=&quot;https://www.reddit.com/r/OldHandhelds/comments/sr51ze/may_i_present_you_the_whole_family_of_sharp/&quot;&gt;Akuji&lt;/a&gt;, the collector from whom I bought my Sharp PA-8500 and manual, who is a fountain of knowledge regarding IC-capable devices from a range of manufacturers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;tetris-ic-card&quot;&gt;TETRIS IC card&lt;/h2&gt;

&lt;p&gt;Here it is running on my PA-8500:&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 1/1;&quot; videoid=&quot;Rzm5STvVY08&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;analysis&quot;&gt;Analysis&lt;/h2&gt;

&lt;p&gt;This version of the game is documented only briefly on &lt;a href=&quot;https://tetris.wiki/Tetris_(Sharp_Wizard)&quot;&gt;Tetris.wiki&lt;/a&gt; and even less on &lt;a href=&quot;https://harddrop.com/wiki/Tetris_(Sharp_Wizard)&quot;&gt;Hard Drop Tetris Wiki&lt;/a&gt;, so let’s see what we can do to fill in a bit more information.&lt;/p&gt;

&lt;p&gt;The device boots straight into the game, but you can switch to calendar, memo, calculator, or other organiser functions whilst it is running. Switching back to the game causes it to reboot, so games cannot be resumed. You cannot skip the BPS logo, but you can skip the credits.&lt;/p&gt;

&lt;p&gt;The credits and game area are in the left (or top) half of the display because it can also run on devices with a smaller screen of half the size of the one shown.&lt;/p&gt;

&lt;p&gt;There is a touch screen surface on top of where the card slots in and the print on the IC card itself shows the user where to press for each button or function. We can see that the shape of each buttons lines up with the grid of dots of the touch surface. We see the following buttons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;left&lt;/li&gt;
  &lt;li&gt;right&lt;/li&gt;
  &lt;li&gt;rotate (only counter-clockwise)&lt;/li&gt;
  &lt;li&gt;soft drop&lt;/li&gt;
  &lt;li&gt;start (hi-speed)&lt;/li&gt;
  &lt;li&gt;start (low-speed)&lt;/li&gt;
  &lt;li&gt;pause on/off&lt;/li&gt;
  &lt;li&gt;sound on/off&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scoring, line-clearing, leveling-up are somewhat odd and non-standard. &lt;a href=&quot;https://tetris.wiki/Tetris_(Sharp_Wizard)&quot;&gt;Tetris.wiki&lt;/a&gt; has those covered. Notably, the play area reduces in size by the bottom lines filling in as you level-up, gradually becoming less tall and more cramped.&lt;/p&gt;

&lt;p&gt;The game seems to have been programmed by BPS (Bullet-Proof Software), though I’m yet to see the manual, but there are no separate programming credits. &lt;!--Perhaps the game code or chips would contain more information.--&gt;&lt;/p&gt;

&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;/h2&gt;
&lt;p&gt;As seen in the video:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;™ and © 1987 Elorg.&lt;br /&gt;
TETRIS licensed to B.P.S.&lt;br /&gt;
© 1989 B.P.S. All Rights Reserved.&lt;br /&gt;
Original concept by Alexey Pazhitnov.&lt;br /&gt;
Original design and program by Vadim Gerasimov.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 04 Aug 2024 16:47:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/08/04/tetris-for-sharp-electronic-notebook-1989/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/08/04/tetris-for-sharp-electronic-notebook-1989/</guid>
        </item>
      
    
      
        <item>
          <title>Search Moby Games using Alfred app</title>
          <description>&lt;p&gt;Earlier this year I made a workflow for &lt;a href=&quot;https://alfred.app&quot;&gt;Alfred app&lt;/a&gt; to allow easy searching of &lt;a href=&quot;https://www.mobygames.com&quot;&gt;Moby Games&lt;/a&gt;. Earlier this week it was released on &lt;a href=&quot;https://alfred.app/workflows/gingerbeardman/moby-games/&quot;&gt;Alfred Gallery&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/alfred-moby-games.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://alfred.app/workflows/gingerbeardman/moby-games/&quot;&gt;alfred.app/workflows/gingerbeardman/moby-games/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow &lt;a href=&quot;https://www.mobygames.com/info/api/&quot;&gt;requires an API key that you can get instantly&lt;/a&gt; via your Moby Games profile page.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you’d like access to the free API (not-for-profit use), create a MobyGames account and then visit your profile page. Click the ‘API’ link under your user name to sign up for an API key. If you wish to use the API key with an existing game launcher or plugin please specify which launcher and plugin you will be using.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enter “Alfred app Moby Games workflow”. Then paste your API into the Workflow configuration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;open-source&quot;&gt;Open Source&lt;/h2&gt;
&lt;p&gt;Suggestions and PRs are welcome at: &lt;a href=&quot;https://github.com/gingerbeardman/alfred-moby-games/&quot;&gt;github.com/gingerbeardman/alfred-moby-games/&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 04 Aug 2024 12:57:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/08/04/search-moby-games-using-alfred-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/08/04/search-moby-games-using-alfred-app/</guid>
        </item>
      
    
      
        <item>
          <title>Taking command of the Context Menu in macOS</title>
          <description>&lt;p&gt;Yesterday on Twitter the inimitable &lt;a href=&quot;https://twitter.com/mortenjust/status/1817991110544744764&quot;&gt;Morten Just posted a preview of a tool he’s created&lt;/a&gt; that wrap ffmpeg to allow movies, such screen recordings but pretty much anything, to be re-encoded to a smaller filesize.&lt;/p&gt;

&lt;p&gt;I responded with a trick I use to do the same on “right-click” context menu using a macOS app called ContextMenu, and others said it was possible to do it using Automator (with some caveats). In this blog post I’ll compare the two.&lt;/p&gt;

&lt;p&gt;But first… let’s talk about how we will make this work.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;processing-files-through-shell-scripts&quot;&gt;Processing files through shell scripts&lt;/h1&gt;

&lt;p&gt;We’ll be making use of command line tools to do the heavy lifting, but don’t worry I’ll show that this can be as simple as a single-line command to process a single file, or a shell scripts of a few lines to process multiple files.&lt;/p&gt;

&lt;h3 id=&quot;single-files&quot;&gt;Single files&lt;/h3&gt;

&lt;p&gt;The one-line command to convert a video or audio file can be as simple as the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are some assumptions here:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt; has been installed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;my Mac is Apple silicon, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; installs commands to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/homebrew/bin/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;(older Intel Macs will have them installed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin/&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;the script is called with parameters passed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;argv&lt;/code&gt; so the file is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$@&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;the new file will have the same name as the original, but with a new .mp4 file extension&lt;/li&gt;
  &lt;li&gt;it only works on single files (read on)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; add an extra parameter to the command line to use hardware encoding (hevc_videotoolbox) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c:v hevc_videotoolbox&lt;/code&gt;, which would be something like twice as fast, but this will not reduce the file size. So we stick with ffmpeg default settings.&lt;/p&gt;

&lt;p&gt;You can take the exact same approach with any destination format as it is decided by the file extension. Cool. You could convert any of ffmpeg’s supported file types to any other, such as WAV, OGG, MKV, etc.&lt;/p&gt;

&lt;p&gt;And as we’ll see later we can take this simple command and change it to use other command line tools to compress GIFs, with or without upscaling, and many other timesaving tasks. Powerful stuff!&lt;/p&gt;

&lt;h3 id=&quot;multiple-files&quot;&gt;Multiple files&lt;/h3&gt;

&lt;p&gt;To extend this approach for multiple files you can use the following for loop:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    /opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;multiple-files-in-parallel&quot;&gt;Multiple files in parallel&lt;/h3&gt;

&lt;p&gt;And to process those multiple files in parallel (be careful not to overwhelm your computer!) we can background each execution using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; symbol.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    /opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt; &amp;amp;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;automator&quot;&gt;Automator&lt;/h2&gt;

&lt;p&gt;This has been a built-in macOS app since 2005. Setup is relatively straightforward:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a new &lt;em&gt;Quick Action&lt;/em&gt; workflow&lt;/li&gt;
  &lt;li&gt;Workflow receives current: &lt;em&gt;movie files&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;in: &lt;em&gt;Finder&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Add a new action of type action &lt;em&gt;Run Shell Script&lt;/em&gt; (search for it in the sidebar)&lt;/li&gt;
  &lt;li&gt;Pass input: &lt;em&gt;as arguments&lt;/em&gt; (this will give us a template command)&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Replace the echo line with your command&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Save&lt;/em&gt;, name it “Duplicate as MP4”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-automator-setup.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The menu item is now available on the context menu, inside the &lt;em&gt;Quick Actions&lt;/em&gt; submenu. After repeated use I find this submenu too annoying, but you may fare better.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-automator-finder.png&quot; alt=&quot;PNG&quot; title=&quot;Automator Quick Action in Finder Context Menu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, pretty easy! But, there are some caveats or limitations that may, or may not, annoy you. Perhaps we don’t want the menu item to appear for such a broad range of files (all “movie files”), or maybe we want it to appear for multiple types of files (both “audio files” and “movie files”). Sadly this is not so easy with Automator.&lt;/p&gt;

&lt;p&gt;Automator stores these files at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Services/&lt;/code&gt; and they will migrate to a new Mac.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;contextmenu&quot;&gt;ContextMenu&lt;/h2&gt;

&lt;p&gt;Many years ago I found &lt;a href=&quot;https://apps.apple.com/us/app/context-menu/id1236813619?mt=12&quot;&gt;ContextMenu&lt;/a&gt; ($4.99) which solves all of the issues I have with the Automator approach. There’s also a free version, &lt;a href=&quot;https://apps.apple.com/gb/app/context-menu-lite/id1261373706?mt=12&quot;&gt;ContextMenu Lite&lt;/a&gt;, that supports up to 3 actions to give you a taste of the good stuff.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Submenu is optional&lt;/li&gt;
  &lt;li&gt;Apply to multiple types (files or directories; can be as granular as file extension)&lt;/li&gt;
  &lt;li&gt;Show output (sometimes you want to see the results of the command)&lt;/li&gt;
  &lt;li&gt;Confirmation before running (if it’s a potentially dangerous operation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-context-menu-setup.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The menu item displays in Finder as follows:&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-context-menu-finder.png&quot; alt=&quot;PNG&quot; title=&quot;ContextMenu Action in Finder Context Menu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ContextMenu stores these files in its own group folder, right click an action and choose &lt;em&gt;Show in Finder&lt;/em&gt;, but you can specify your own folder in a location of your choice. Both will migrate to a new Mac, but the later might give you more control.&lt;/p&gt;

&lt;p&gt;Edit: we can limit actions to running on files with specific extensions or, since update 1.4.4 &lt;a href=&quot;https://gist.github.com/RhetTbull/7221ef3cfd9d746f34b2550d4419a8c2&quot;&gt;specific UTI types&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;compressing-a-gif&quot;&gt;Compressing a GIF&lt;/h2&gt;

&lt;p&gt;We can use the &lt;a href=&quot;https://www.lcdf.org/gifsicle/&quot;&gt;gifsicle&lt;/a&gt; command line tool to optimise (compress) an Animated GIF. The a parameters I use here are explained in the &lt;a href=&quot;https://www.lcdf.org/gifsicle/man.html&quot;&gt;gifsicle man page&lt;/a&gt;, and I arrived at them with &lt;a href=&quot;/2016/06/16/post-processing-animated-gifs/&quot;&gt;years of experimentation&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/gifsicle &lt;span class=&quot;nt&quot;&gt;-O1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Okeep-empty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--careful&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.gif&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.o.gif&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can also do the same thing whilst scaling it up by a factor of 2:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/gifsicle &lt;span class=&quot;nt&quot;&gt;-O1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Okeep-empty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--careful&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scale&lt;/span&gt; 2 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.gif&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.o.gif&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;repository-of-contextmenu-actions&quot;&gt;Repository of ContextMenu Actions&lt;/h2&gt;

&lt;p&gt;Here’s my repo of shared actions. Inside each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cmaction&lt;/code&gt; file is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.sh&lt;/code&gt; file which contains the command that you can use in Automator if you’d like.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gingerbeardman/contextmenu-actions&quot;&gt;github.com/gingerbeardman/contextmenu-actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These include many useful commands such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Convert BIN+CUE to ISO&lt;/li&gt;
  &lt;li&gt;Change sample rate of audio&lt;/li&gt;
  &lt;li&gt;Convert image to 1-bit&lt;/li&gt;
  &lt;li&gt;Convert image format&lt;/li&gt;
  &lt;li&gt;Create Clean ZIP&lt;/li&gt;
  &lt;li&gt;Duplicate audio as alternative format&lt;/li&gt;
  &lt;li&gt;Duplicate image as alternative format&lt;/li&gt;
  &lt;li&gt;Duplicate movie as alternative format&lt;/li&gt;
  &lt;li&gt;File information&lt;/li&gt;
  &lt;li&gt;Generate images for web&lt;/li&gt;
  &lt;li&gt;Make script executable&lt;/li&gt;
  &lt;li&gt;Move file (includes helper app)&lt;/li&gt;
  &lt;li&gt;New file with clipboard&lt;/li&gt;
  &lt;li&gt;Open file with specific apps&lt;/li&gt;
  &lt;li&gt;Optimize GIF&lt;/li&gt;
  &lt;li&gt;Set GIF to loop infinitely&lt;/li&gt;
  &lt;li&gt;Show GIF info&lt;/li&gt;
  &lt;li&gt;Playdate Colorize IMG&lt;/li&gt;
  &lt;li&gt;Replace Existing App (warning)&lt;/li&gt;
  &lt;li&gt;Scale image to certain percentage&lt;/li&gt;
  &lt;li&gt;Set as MacJapanese encoding&lt;/li&gt;
  &lt;li&gt;Show hashes (includes helper app)&lt;/li&gt;
  &lt;li&gt;Stub out file (warning)&lt;/li&gt;
  &lt;li&gt;Touch file&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 30 Jul 2024 16:14:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/07/30/taking-command-of-the-context-menu-in-macos/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/07/30/taking-command-of-the-context-menu-in-macos/</guid>
        </item>
      
    
      
        <item>
          <title>Shibuya Pixel Art Contest 2024</title>
          <description>&lt;p&gt;For June and July of 2024 the &lt;a href=&quot;https://pixel-art.jp/contesten&quot;&gt;Shibuya Pixel Art Contest&lt;/a&gt; has been running, a welcome return after it not happening in 2023. Entries are open all over the world, but can only be submitted to Twitter using the hashtag &lt;a href=&quot;https://twitter.com/hashtag/shibuyapixelart2024?f=live&quot;&gt;#shibuyapixelart2024&lt;/a&gt;. You have to include a name for the artwork and its original pixel dimensions (though it’s OK to rescale small artwork so it can be seen more easily). There are special categories for 16×16px and 32×32px artwork, and then a category for anything bigger up to the maximum of 512×512px. Full rules at &lt;a href=&quot;https://pixel-art.jp/contesten&quot;&gt;pixel-art.jp/contesten&lt;/a&gt; and there’s still time to enter! Selected works will go on display around the Shibuya district of Tokyo in August and September.&lt;/p&gt;

&lt;p&gt;For my entries I thought it would be cool to use different, unexpected software to produce my artwork.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;pico-8&quot;&gt;PICO-8&lt;/h2&gt;

&lt;p&gt;These three entries are based on graphics I created for a game I started making when my wife was pregnant with our first child. We were house bound for a while, waiting for the birth, and I drew these cards using &lt;a href=&quot;https://www.lexaloffle.com/pico-8.php&quot;&gt;PICO-8&lt;/a&gt; for a game which remains unfinished. At least I finished the graphics!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;16×16px = 「花見」hanami card, (&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1808102421077295350&quot;&gt;link&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;32×32px = 「花見酒」hanami-sake yaku, (&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1808102225857720725&quot;&gt;link&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;128×128px = 「花札」hanafuda koi-koi game layout, (&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1806905446805938219&quot;&gt;link&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smaller cards shown in the full game layout are just the regular sized cards downscaled to 8×11px in code using nearest neighbour resizing. For all cards the border is drawn seperately. One or the other size of card are most likely the smallest Hanafuda ever pixelled!?&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div id=&quot;carousel0&quot; class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel0&quot; id=&quot;0a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel0&quot; id=&quot;0b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel0&quot; id=&quot;0c&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;0c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;0b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;0a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;0c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;0b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;0a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-pico8-hanami.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-pico8-hanami.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-pico8-hanami-sake.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-pico8-hanami-sake.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-pico8-hanafuda.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-pico8-hanafuda.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;0a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;0b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;0c&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 100%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1 {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2 {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3 {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;#carousel0 .carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;rorschach-for-playdate&quot;&gt;Rorschach for Playdate&lt;/h2&gt;

&lt;p&gt;This entry was created with &lt;a href=&quot;https://gingerbeardman.itch.io/rorschach&quot;&gt;Rorschach&lt;/a&gt; a creative toy/game I made for the &lt;a href=&quot;https://play.date&quot;&gt;Playdate&lt;/a&gt; handheld gaming system. This piece was created by moving the “pen” using the accelerometer and relying on the dynamic ink colour which is relative to the movement. It’s a fullscreen grab at 400×240px, titled 「キクぞく」”Kikuzoku” or “Chrysanthemum”.&lt;/p&gt;

&lt;p&gt;It would be tremendously complicated and time-consuming to create artwork like this pixel-by-pixel, or even with dither brushes, unless you had some sort of pressure sensitive stylus. But Rorschach and the Playdate accelerometer makes light work of it.&lt;/p&gt;

&lt;p&gt;The final image was my 12th attempt at getting a convincing flower! With many of my earlier attempts I was slow to hide the cursor and beautiful flowers were ruined by rogue strokes of ink after I’d technically finished. Perhaps I should have added the ability to record/playback or some sort of undo to the app. (&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1810481363046318410&quot;&gt;link&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-rorschach.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;deneba-artworks-for-classic-macintosh&quot;&gt;Deneba artWORKS for Classic Macintosh&lt;/h2&gt;

&lt;p&gt;I thought it would be fun to use my favourite classic Macintosh drawing app—Deneba &lt;a href=&quot;/tag/deneba/&quot;&gt;artWORKS&lt;/a&gt;—to create pixel art using vector shapes, its bundled external tools (plugins), and the infinite fills best known from their appearance in MacPaint. There are only 20 objects in this drawing, which is 200×300px in size and titled 「黄金比」 or “the golden ratio”. (&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1812491599789576474&quot;&gt;link&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Spiral: flower heads (2)&lt;/li&gt;
  &lt;li&gt;Resistor: hairs (3)&lt;/li&gt;
  &lt;li&gt;Bezier: stems, branch, leaves (5)&lt;/li&gt;
  &lt;li&gt;Arc: shoots, stems, midrib (7)&lt;/li&gt;
  &lt;li&gt;Round rect: planter (2)&lt;/li&gt;
  &lt;li&gt;Polygon: planter inner shadow (1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Calligraphic pen nibs to get variable line thickness&lt;/li&gt;
  &lt;li&gt;Infinite fills to get dithered patterns and textures&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div id=&quot;carousel1&quot; class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel1&quot; id=&quot;1a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel1&quot; id=&quot;1b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel1&quot; id=&quot;1c&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;1c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;1b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;1a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;1c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;1b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;1a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks1.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks1.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks3.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;1a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;1b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;1c&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 75%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1 {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2 {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3 {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;#carousel1 .carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;bonus&quot;&gt;Bonus!&lt;/h2&gt;

&lt;p&gt;I was having too much fun with dither patterns, so I drew one more piece just for kicks. This one is called 「生け花」”Ikebana” (“flower arranging”) and was exported at 200% because I miscalculated my document size at the beginning. I drew the shears and then transformed them with Free Rotate and Scale. There was minor pixel touch-up after export, and the final thing measures 400×512px. Otherwise I used the same techniques as above. (&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1812840849211851109&quot;&gt;link&lt;/a&gt;)&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div id=&quot;carousel2&quot; class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel2&quot; id=&quot;2a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel2&quot; id=&quot;2b&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;2b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;2b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;2a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;2a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks-ikebana.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks-ikebana.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks-ikebana2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks-ikebana2.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;2a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;2b&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 128%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1 {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2 {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;#carousel2 .carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;extras-bonus&quot;&gt;Extras Bonus!&lt;/h2&gt;

&lt;p&gt;And I did one more just before the deadline. This is titled 「ラペルピン」”lapel pin” and is 512×512px and was created using the same vectors and fills technique. I decided to work in Canvas 3.0 rather than artWORKS, simply because it has a way of locking objects. The only new thing I did here is that I created two diagonal line fill patterns of my own for the collars. (&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1817609688210690392&quot;&gt;link&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks-lapel-pin.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And here’s a screenshot of my working area, Macintosh System 7.5.5 running in 1-bit colour at 1025x768 in the BasiliskII emulator. This time I created it on my MacBook Pro with external Magic Trackpad, but &lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh/&quot;&gt;sometimes I use my iPad Pro with Apple Pencil&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/shibuya-pixel-art-contest-2024-artworks-lapel-pin-working.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 14 Jul 2024 16:42:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/07/14/shibuya-pixel-art-contest-2024/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/07/14/shibuya-pixel-art-contest-2024/</guid>
        </item>
      
    
      
        <item>
          <title>TETRIS: Heavenly Scrolls (1989)</title>
          <description>&lt;p&gt;Given the ongoing discussion about &lt;a href=&quot;https://www.timeextension.com/news/2024/06/anniversary-is-tetris-really-40-this-year&quot;&gt;whether or not TETRIS is 40 years old in 2024&lt;/a&gt; (I say not), I thought it would be cool to add something interesting and relevant to the conversation. As a bonus, this is an item that at the time of writing is not covered in English anywhere else on the internet.&lt;/p&gt;

&lt;p&gt;A Japanese book about TETRIS, written in the first year the game was available outside of the USSR:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;テトリス10万点への解法 (天の巻)&lt;br /&gt;
TETRIS: The Solution to 100,000 Points (Heavenly Scrolls)&lt;/strong&gt;&lt;br /&gt;
“HOW TO WIN TETRIS FOR 100,000,000 TETRISTS”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Edited and translated by 石原恒和 Tsunekazu Ishihara, published 15th March 1989.&lt;/p&gt;

&lt;p&gt;Yes, that’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Tsunekazu_Ishihara&quot;&gt;the current CEO of The Pokémon Company&lt;/a&gt;!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The book is equal parts strategy guide, love letter to the game, and historical document. It mostly features &lt;em&gt;Spectrum Holobyte&lt;/em&gt;’s &lt;a href=&quot;https://www.mobygames.com/game/1630/tetris/screenshots/macintosh/&quot;&gt;&lt;em&gt;Macintosh&lt;/em&gt; version of TETRIS&lt;/a&gt;, but fondly refers to versions on the &lt;em&gt;Amiga&lt;/em&gt; and other home computers. At the point this book was published, March 1989, the &lt;em&gt;Game Boy&lt;/em&gt; was just about to release, in April, and its version of TETRIS would not arrive for a few months more, in June.&lt;/p&gt;

&lt;p&gt;It features contributions from the team at &lt;a href=&quot;https://www.mobygames.com/company/20165/sedic/&quot;&gt;&lt;em&gt;SEDIC&lt;/em&gt;&lt;/a&gt; (most famous for &lt;a href=&quot;https://www.mobygames.com/game/61185/otocky/&quot;&gt;&lt;em&gt;Otocky&lt;/em&gt;&lt;/a&gt;) in the form of essays, stories, and illustrations about their experience with the game and how it had changed their world view. More on them in a forthcoming blog post. Towards the end of the book there are interviews with the TETRIS creators: &lt;a href=&quot;https://www.mobygames.com/person/62651/vadim-gerasimov/&quot;&gt;Vadim Gerasimov&lt;/a&gt; (programmer) and &lt;a href=&quot;https://www.mobygames.com/person/533681/alexey-l-pajitnov/&quot;&gt;Alexey Pajitnov&lt;/a&gt; (designer). &lt;a href=&quot;https://www.mobygames.com/person/3523/henk-b-rogers/&quot;&gt;Henk Rogers&lt;/a&gt; does not seem to be mentioned, though his company &lt;a href=&quot;https://www.mobygames.com/company/2186/bullet-proof-software-inc/&quot;&gt;BPS&lt;/a&gt; is briefly mentioned in passing.&lt;/p&gt;

&lt;p&gt;I can’t see any specific mention of the creation date of TETRIS in the text, but there’s a copyright line, &lt;a href=&quot;https://youtu.be/2wnhPS5Q78E?t=24&quot;&gt;most likely taken from the &lt;em&gt;Spectrum Holobyte&lt;/em&gt; &lt;em&gt;Macintosh&lt;/em&gt; version&lt;/a&gt;, that says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Copyright © 1987 Academy Soft-ELORG.&lt;br /&gt;
All rights reserved. TETRIS is a trademark of Academy Soft-ELORG.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;e&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;f&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;g&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;h&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-01-front-cover.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-01-front-cover.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-02-title-page.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-02-title-page.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-03-contents-left.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-03-contents-left.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-04-contents-right.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-04-contents-right.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-05-copyright.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-05-copyright.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-06-sample.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-06-sample.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-07-interview-alexey-pajitnov.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-07-interview-alexey-pajitnov.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-08-rear-cover.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/tetris-heavenly-scrolls-1989-img-08-rear-cover.jpg&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 133%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}

.carousel__activator:nth-of-type(5):checked ~ .carousel__track {
  -webkit-transform: translateX(-400%);
          transform: translateX(-400%);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__slide:nth-of-type(5) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__controls:nth-of-type(5) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(5) {
  opacity: 1;
}

.carousel__activator:nth-of-type(6):checked ~ .carousel__track {
  -webkit-transform: translateX(-500%);
          transform: translateX(-500%);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__slide:nth-of-type(6) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__controls:nth-of-type(6) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(6) {
  opacity: 1;
}

.carousel__activator:nth-of-type(7):checked ~ .carousel__track {
  -webkit-transform: translateX(-600%);
          transform: translateX(-600%);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__slide:nth-of-type(7) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__controls:nth-of-type(7) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(7) {
  opacity: 1;
}

.carousel__activator:nth-of-type(8):checked ~ .carousel__track {
  -webkit-transform: translateX(-700%);
          transform: translateX(-700%);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__slide:nth-of-type(8) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__controls:nth-of-type(8) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(8) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(5) {
  -webkit-transform: translateX(400%) translateZ(0);
          transform: translateX(400%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(6) {
  -webkit-transform: translateX(500%) translateZ(0);
          transform: translateX(500%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(7) {
  -webkit-transform: translateX(600%) translateZ(0);
          transform: translateX(600%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(8) {
  -webkit-transform: translateX(700%) translateZ(0);
          transform: translateX(700%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(5),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(5) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(6),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(6) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(7),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(7) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(8),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(8) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 18 Jun 2024 14:47:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/06/18/tetris-heavenly-scrolls/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/06/18/tetris-heavenly-scrolls/</guid>
        </item>
      
    
      
        <item>
          <title>Adding your own multi-channel audio to Music app</title>
          <description>&lt;p&gt;I’m really enjoying Dolby Atmos, “spatial audio”, or “surround sound” as we used to call it. There is so much music in this format on streaming platforms, both new albums and classic albums. I wanted to listen to Björk’s first four albums in “spatial audio”, but they don’t currently exist in that format on streaming platforms. But I was reminded about DVD-Audio versions of these from back in the day, and I could find most of mine, so I figured I’d have a go of converting them myself whilst I’m waiting for Dolby Atmos versions to hit streaming.&lt;/p&gt;

&lt;p&gt;This blog post lists the three main steps needed to add your own multi-channel audio to Music.app (you might know it by its old name: iTunes) and have them be accessible across all of your devices thanks to Apple Music’s Cloud Library (you might know it by its old name: iCloud Music Library).&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;dvd-audiovideo&quot;&gt;DVD Audio/Video&lt;/h2&gt;

&lt;p&gt;Three albums I have were released on DVD and contain multi-channel audio in both DTS and Dolby Digital as DVD-Video, these are Debut, Post, and Homogenic. Vespertine is better mastered and was released as a disc that included DVD-Audio. Regardless, we can take the same approach for these two disc types: use &lt;a href=&quot;https://www.dvdae.com&quot;&gt;DVD Audio Extractor&lt;/a&gt; (the 30-day trial is OK) to extract the tracks to FLAC files:&lt;/p&gt;

&lt;p&gt;Settings:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sample rate: Same as input&lt;/li&gt;
  &lt;li&gt;Channels: All 6 Channels&lt;/li&gt;
  &lt;li&gt;Bits per sample: 24-bit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’ll take a minute or two for the files to be generated.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: many multi-channel FLAC files&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;aside-cloud-library&quot;&gt;Aside: Cloud Library&lt;/h2&gt;

&lt;p&gt;Music.app (formerly iTunes) doesn’t really go out of its way to support multi-channel audio users might add themselves. We won’t be creating Dolby Atmos tracks, but rather we’ll take advantage of a loop hole in Apple Music’s Cloud Library feature.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://www.quadraphonicquad.com/forums/threads/ripping-in-surround-for-apple-tv.33931/post-699285&quot;&gt;a post on the QuadrophonicQuad forum&lt;/a&gt; we know that we need to limit the bitrate to 256kb/s for it to be uploaded unmodified to our Cloud Library and sync to all our devices. Well, it turns out that trick also works with 768kb/s bitrate files, which is the same bitrate as Dolby Atmos releases on Apple Music. That’s good enough for me.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;conversionencoding&quot;&gt;Conversion/Encoding&lt;/h2&gt;

&lt;p&gt;There are many GUI tools that you might use to do this but I’ve chose to use ffmpeg on the command line. Feel free to adapt these settings to your tool of choice. The most important property is the 768kb/s bitrate, as mentioned earlier.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for n in *.flac; do ffmpeg -i &quot;$n&quot; -vn -c:a aac_at -b:a 768k -sample_fmt s16 -ar 48000 &quot;$n.m4a&quot;; done&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Breaking down the command above:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for n in *.flac; do&lt;/code&gt; = loop through all *.flac files&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i &quot;$n&quot;&lt;/code&gt; = input, using filename from for loop&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-vn&lt;/code&gt; = remove the video track&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c:a aac_at&lt;/code&gt; = use Apple’s AAC encoder&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-b:a 768k&lt;/code&gt; = target 768kb/s bitrate&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-sample_fmt s16&lt;/code&gt; = 16-bit sample format&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ar 48000&lt;/code&gt; = 48KHz sample rate&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;$n.m4a&quot;&lt;/code&gt; = output, using filename from for loop, adding .m4a&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;; done&lt;/code&gt; = end for loop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully that makes sense.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: many multi-channel M4A files&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;chapters&quot;&gt;Chapters&lt;/h2&gt;

&lt;p&gt;If the M4A audio file contains chapters they will need to be removed or the file will not be playable on iPhone. We can load it into &lt;a href=&quot;https://subler.org&quot;&gt;Subler&lt;/a&gt;, then select and delete the “Text Track”.&lt;/p&gt;

&lt;p&gt;The process above should not introduce them, but I’m mentioning it as I was repeatedly caught out by this on my journey to this solution. If you fail to remove it the track will either either upload but not be playable, error and not upload, or be matched rather than uploaded.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: multi-channel M4A file (modified)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;final-steps&quot;&gt;Final steps&lt;/h2&gt;

&lt;p&gt;Add the final files to Music.app on your desktop. At this point you can add artwork and other meta data, such as tagging them as “multi-channel” or similar.&lt;/p&gt;

&lt;p&gt;When you’re done make sure to choose &lt;em&gt;File &amp;gt; Library &amp;gt; Update Cloud Library&lt;/em&gt;. After they finish uploading, open the Music app on your iPhone and you’ll see the multi-channel files ready to play.&lt;/p&gt;

&lt;p&gt;These multi-channel files work wonderfully with AirPods Pro spatial audio head tracking!&lt;/p&gt;

&lt;hr /&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/music-multi-channel.heic&quot; alt=&quot;WEBP&quot; title=&quot;My own multi-channel audio in Music app on my iPhone&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 09 May 2024 20:19:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/05/09/adding-your-own-multi-channel-audio-to-music-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/05/09/adding-your-own-multi-channel-audio-to-music-app/</guid>
        </item>
      
    
      
        <item>
          <title>Running modern macOS on non-retina displays</title>
          <description>&lt;p&gt;It’s 2024 and for some years now Apple have stopped supporting non-retina displays. From their perspective it makes sense given that all of their devices run retina displays, but from the user’s perspective it’s annoying given that so many of us are still using non-retina displays as either an external main display or as a secondary display. Not all of us are capable or comfortable buying multi-thousand dollar 5K displays.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;My personal situation is that I run a portrait display so that I can display a long page of code. Hey, if portrait was good enough for the &lt;a href=&quot;https://en.wikipedia.org/wiki/Xerox_Alto&quot;&gt;Xerox Alto&lt;/a&gt; and is good enough for the &lt;a href=&quot;https://en.wikipedia.org/wiki/IPhone&quot;&gt;iPhone&lt;/a&gt; then it’s good enough for me. I have shortcut keys to arrange windows into halves, thirds or quarters and I’m super productive with it. I’ve been using this setup for a handful of years now, with my current setup consisting of a 1920×1200 display rotated 90-degrees giving me a desktop of 1200×1920.&lt;/p&gt;

  &lt;p&gt;You might be thinking, “why not use a rotated 1080p display?” and the answer to that question is that a lot of software won’t display correctly or even fit on a desktop of width 1080px, but is fine at 1200px. You might also be thinking “why not buy a 4K display?” and that’s because none of them are wide enough in portrait. Finally, if you’re asking “why not buy a 5K display?” then, yes, that’s a very good point. But until I do that, there’s a problem waiting to be solved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;problem&quot;&gt;Problem&lt;/h2&gt;

&lt;p&gt;There are a number of issues running modern macOS on non-retina displays that Apple seems to not want to fix, and in fact they are introducing new problems with every new version of macOS. Apple’s stance is that they don’t make resolution-specific adjustments, but I find that to not be quite true. Of course, abandoning non-retina elements or not paying attention to how they display on non-retina displays has the affect of being a resolution-specific adjustment. Plus, one of the most recent issues proves that there was a specific change made for non-retina displays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Text cursor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since macOS 14, Sonoma, there’s a new redesign text cursor. It changes colour and flashes in a different way than the normal cursor, but more importantly it is 50% wider (1.5x) on non-retina displays! On retina displays the cursor is equivalent to 2px wide and on non-retina displays it is equivalent to 3px side. You might think that a single pixel might not make much of a difference, but when there’s only 2px between characters it means &lt;a href=&quot;https://forums.macrumors.com/attachments/cursor-non-retina-png.2338640/&quot;&gt;the neighbouring text is obscured&lt;/a&gt;. So it’s difficult, perhaps even impossible, to tell whether a c character to the left of the cursor is a c, d, o, q, etc. In some situations, depending on your choice of font or text size the new wider cursor will completely obscure the letters i, I, l, 1 that are next to the cursor. Reported to Apple as FB13536508.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abandoned icons&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Across macOS from Finder to Safari &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1539963156570820608&quot;&gt;icons are stretched and badly positioned&lt;/a&gt; on non-retina displays, which may or may not be limited to icons made from SF Symbols. As recent as Monterey, System Preferences app would &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1539963168654589954&quot;&gt;transition using retina icons and then after the transition they would be replaced with non-retina icons&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;workaround&quot;&gt;Workaround&lt;/h2&gt;

&lt;p&gt;The most useful workaround to these problems is to trick macOS into thinking that your non-retina display is retina. This is done by changing the scale value of your display.&lt;/p&gt;

&lt;p&gt;An alternative way of doing this is to use &lt;a href=&quot;https://github.com/waydabber/BetterDisplay&quot;&gt;BetterDisplay&lt;/a&gt; where you can simply click the High Resolution (HiDPI) toggle for your non-retina display.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you want to do it by hand you can open up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.apple.windowserver.displays...plist&lt;/code&gt; at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Preferences/ByHost/&lt;/code&gt;, in something like PlistEdit Pro, and search for “Scale” to change each instance of a value 1 into 2. Thanks to &lt;a href=&quot;https://www.getvladimir.com&quot;&gt;Vladimir Kochkovski&lt;/a&gt; for sharing this solution!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;improves cursor width issue (it’s less wide so neighbouring text is not obscured quite as much)&lt;/li&gt;
  &lt;li&gt;fixes many other user interface elements that are bad on non-retina displays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;screenshots are @2x, which might be unexpected or undesirable&lt;/li&gt;
  &lt;li&gt;websites that do not provide retina-ready images may appear slightly blurry&lt;/li&gt;
  &lt;li&gt;(in other words: the same things as a real retina display)&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 25 Jan 2024 16:25:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/01/25/running-modern-macos-on-non-retina-displays/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/01/25/running-modern-macos-on-non-retina-displays/</guid>
        </item>
      
    
      
        <item>
          <title>JINZO Paint: vintage mobile drawing app</title>
          <description>&lt;p&gt;At my core I’m a software guy. I don’t really get attached to hardware: in my mind it exists only as a conduit to software. I use emulation whenever I can to benefit from the increased convenience and reliability. But when I can’t… I buy old devices, and with old devices come old problems.&lt;/p&gt;

&lt;p&gt;I’m fascinated by &lt;a href=&quot;/2023/10/21/list-of-vintage-japanese-pixel-dot-art-software/&quot;&gt;vintage digital art software&lt;/a&gt;, from my beginnings on Atari ST, though classic Macintosh and vintage Japanese PCs, to handhelds like Palm devices or, in this case, a PocketPC running Windows CE. As with all software, many great ideas have been abandoned in the name of “progress”.&lt;/p&gt;

&lt;h2 id=&quot;zen-and-the-art-of-software-design&quot;&gt;Zen and the Art of Software Design&lt;/h2&gt;

&lt;p&gt;Japanese art software is notable in a number of ways because their drawing tools evolved differently to those in the West, at least until Photoshop took hold. In Japan the first breakthrough digital art software was the PC-98’s &lt;a href=&quot;https://www.youtube.com/watch?v=nIdFor2WOnw&quot;&gt;Multi Paint System&lt;/a&gt; (MPS, マルチペイントシステム) released by C-Lab in 1992, though an earlier version was released in 1991 as Maguro Paint System, or Tuna Paint System (鮪ペイントシステム) both of which were programmed by Woody_RINN.&lt;/p&gt;

&lt;p&gt;MPS introduced &lt;a href=&quot;https://twitter.com/_blubot_/status/1727397680895476153&quot;&gt;the ability to lock colours and prevent them from being drawn over&lt;/a&gt;. You might think of it as a mask featuring every instance of that particular colour. You could also replace colours in a similar way. This was done through an intuitive mechanism of a toolbar button or key press to lock one or more specific colours on the palette, which is quite different to the selection or mask approaches popularised by Photoshop and common today.&lt;/p&gt;

&lt;p&gt;Regardless of whether or not the method of handling colours in MPS is novel or unique, it was adopted as a standard by most Japanese art software for many years. JINZO Paint (JZP), a digital art app for PocketPC (Windows CE) created by t-ueno (&lt;a href=&quot;http://www.tomozon.sakura.ne.jp/wince/&quot;&gt;Tomohiro Ueno&lt;/a&gt;), was one such app that adopted the MPS way of doing things and I’ve been using it a bunch recently. It offers first class support for drawing in dither patterns, and you can load in custom patterns. If you like HyperCard, NewtPaint, TealPaint, you’ll love it. &lt;em&gt;Jinzo&lt;/em&gt; is the Japanese word for kidney, which is also the icon of the app.&lt;/p&gt;

&lt;p&gt;If you’re interested in reading about the interface and functions of JINZO Paint &lt;a href=&quot;https://www.gingerbeardman.com/jzpaint/&quot;&gt;I’ve mirrored the manuals for two early versions on my website&lt;/a&gt; as the original location is no longer available. A later version supports full 24-bit colour, but removes some useful functions. Regardless, all versions use a similar interface so they’re easy to use once you’re familiar with the general operation. One thing I would say is that the toolbars can be opened and selected with a single tap-drag-release action, which a huge win for usability and one that reminds me of the original Apple Macintosh and Palm OS. If you’re lucky your operating system today will support such fluidity.&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;e&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;f&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;g&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;h&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-1.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-1.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-3.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-4.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-4.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-5.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-5.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-6.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-6.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-7.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-7.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-8.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-8.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 133%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}

.carousel__activator:nth-of-type(5):checked ~ .carousel__track {
  -webkit-transform: translateX(-400%);
          transform: translateX(-400%);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__slide:nth-of-type(5) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__controls:nth-of-type(5) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(5) {
  opacity: 1;
}

.carousel__activator:nth-of-type(6):checked ~ .carousel__track {
  -webkit-transform: translateX(-500%);
          transform: translateX(-500%);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__slide:nth-of-type(6) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__controls:nth-of-type(6) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(6) {
  opacity: 1;
}

.carousel__activator:nth-of-type(7):checked ~ .carousel__track {
  -webkit-transform: translateX(-600%);
          transform: translateX(-600%);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__slide:nth-of-type(7) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__controls:nth-of-type(7) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(7) {
  opacity: 1;
}

.carousel__activator:nth-of-type(8):checked ~ .carousel__track {
  -webkit-transform: translateX(-700%);
          transform: translateX(-700%);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__slide:nth-of-type(8) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__controls:nth-of-type(8) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(8) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(5) {
  -webkit-transform: translateX(400%) translateZ(0);
          transform: translateX(400%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(6) {
  -webkit-transform: translateX(500%) translateZ(0);
          transform: translateX(500%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(7) {
  -webkit-transform: translateX(600%) translateZ(0);
          transform: translateX(600%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(8) {
  -webkit-transform: translateX(700%) translateZ(0);
          transform: translateX(700%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(5),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(5) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(6),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(6) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(7),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(7) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(8),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(8) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },10000);
  
&lt;/script&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;for-my-next-trick-i-will-run-it-on-a-dictionary&quot;&gt;For my next trick I will run it on a Dictionary&lt;/h2&gt;

&lt;p&gt;My Windows CE device is a bit odd because it’s a Brain. These are a range of electronic dictionaries made by Sharp and sold only in Japan. With a bit of gentle coaxing it can be used as a little computer running Windows CE. My particular model is the PW-SH1 which is a 3rd generation device with a high resolution screen whose hinge can rotate 360° so it’s back-to-back against the keyboard, effectively becoming a tablet computer. Some years ago it was figured out that you could sideload apps and even force these devices to open the Windows CE desktop and do all manner of crazy things. Japanese hackers and modders seem to love using these devices to run emulators for old computers like Sharp MZ-series and NEC PC-series. If you want to know more check out the &lt;a href=&quot;https://brain.fandom.com/ja/wiki/Brain_Wiki&quot;&gt;Brain Wiki&lt;/a&gt; and if you want to pick up a device &lt;a href=&quot;https://brain.fandom.com/ja/wiki/Brain機種別解説&quot;&gt;here’s a list of them all&lt;/a&gt; (browser translation required for those links).&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint.jpg&quot; alt=&quot;JPG&quot; title=&quot;JINZO Paint, 4-colour version&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;a-problem-with-file-selection&quot;&gt;A problem with file selection&lt;/h2&gt;

&lt;p&gt;Anyway, I noticed whilst using JZP that the file selector would often fail to open properly. It worked the first time but subsequent attempts saw it open and then immediately vanish. The source code to the first two versions is available, and with the help of Brain Hackers’ &lt;a href=&quot;https://twitter.com/watamario15&quot;&gt;@watamario15&lt;/a&gt; the reason for the problem was traced. He found that the value of the parameter being passed to the file selector was invalid, and provided proof by modifying the source code to those versions resulting in new working binaries. But with the final full-colour version has no source code, so how do we fix that?&lt;/p&gt;

&lt;h2 id=&quot;multiple-solutions&quot;&gt;Multiple solutions&lt;/h2&gt;

&lt;p&gt;The binaries for my device are ARM so there’s good support for debugging that type of code. I fired up Ghidra and started poking around in the earliest, smallest version of JZP. I quickly found the section of code that defined the parameters, helped by the placement of the setup of the strings used in the file selector. &lt;a href=&quot;https://www.coalfire.com/the-coalfire-blog/reverse-engineering-and-patching-with-ghidra&quot;&gt;A quick tutorial later&lt;/a&gt; and I knew how to use Ghidra to find references to data, patch instructions, and save a new binary. The workaround was to set the parameter to NULL, which works nicely but removes the ability for the app to remember the most recently used directory.&lt;/p&gt;

&lt;p&gt;However the source is available for the two earlier versions, which I prefer using, so more complete changes could be implemented. Rather than simply nulling out the parameter we could set it to the correct initial value: the root directory. Additional changes were needed to make sure the program would cope with saving and loading from the root directory of the device directly. And finally, just for good measure, the 16-colour version’s extremely slow bitmap saving has been optimised!&lt;/p&gt;

&lt;p&gt;Many thanks to &lt;a href=&quot;https://twitter.com/watamario15&quot;&gt;@watamario15&lt;/a&gt; for his invaluable help with debugging and his generous source code wrangling. And to &lt;a href=&quot;https://brain.fandom.com/ja/wiki/&quot;&gt;Brain Wiki&lt;/a&gt; for being such a valuable resource for crazy old software nerds like me!&lt;/p&gt;

&lt;h2 id=&quot;keyboard-controls&quot;&gt;Keyboard controls&lt;/h2&gt;

&lt;p&gt;I’ve also added keyboard control to JINZO Paint 16, with standard Photoshop keys to switch tools, plus keys for undo, set zoom, quick zoom (hold space bar), and more. If I get permission from the original author I’ll release a patch.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-dev.png&quot; alt=&quot;JPG&quot; title=&quot;JINZO Paint 16, source code modifications&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;repo&quot;&gt;Repo&lt;/h2&gt;

&lt;p&gt;My changes to the 16-color version are now available at the BRAIN hackers GitHub:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/brain-hackers/jinzo-paint/tree/16-color&quot;&gt;github.com/brain-hackers/jinzo-paint/tree/16-color&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://brain.fandom.com/ja/wiki/JINZO_Paint&quot;&gt;JINZO Paint page on Brain Wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://discord.com/channels/759813579120836608/1198349406878060646&quot;&gt;JINZO Paint thread on Brain Hackers Discord Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/jzpaint/&quot;&gt;JINZO Paint documentation mirror&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.tomozon.sakura.ne.jp/wince/JINZO_COLLECTION/DATA_BOOK/JZP_DATA/tel/howtojzp/howtojzp.htm&quot;&gt;JINZO Paint tutorial (4-colours)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.tomozon.sakura.ne.jp/wince/JINZO_COLLECTION/DATA_BOOK/JZP_DATA/emugaro/cgmake.htm&quot;&gt;JINZO Paint tutorial (16-colours)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=nIdFor2WOnw&quot;&gt;Playback of Woody_RINN drawing in Multi Paint System&lt;/a&gt; watch the dithering by blending at &lt;a href=&quot;https://www.youtube.com/watch?v=nIdFor2WOnw&amp;amp;t=434&quot;&gt;07:14&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 22 Jan 2024 20:53:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/01/22/jinzo-paint-vintage-mobile-drawing-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/01/22/jinzo-paint-vintage-mobile-drawing-app/</guid>
        </item>
      
    
      
        <item>
          <title>Going back to the old (pre-X) Twitter iOS app</title>
          <description>&lt;p&gt;There are two main ways to do this. As of 2025 I recommend the first one, using a tweaked app, but I’ll leave the second one up for the sake of history as it still works, albeit more limited in use.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;using-a-tweaked-app&quot;&gt;Using a tweaked app&lt;/h2&gt;

&lt;p&gt;My current recommended way of doing this to sideload a tweaked version of the Twitter/X app.&lt;/p&gt;

&lt;p&gt;You’ll need a tweaked Twitter.ipa and the version I use is &lt;a href=&quot;https://github.com/ghl3m0n/FuckElon&quot;&gt;a version by ghl3m0n&lt;/a&gt; that also replaces all X branding with the bird.&lt;/p&gt;

&lt;p&gt;Tweaked apps have additional plugins and extensions added to them. The most common and useful tweak is BHTwitter, which will block ads and most bots whilst enabling some extra features. The only real downside to tweaked apps is that they don’t support deep linking like the original apps, but you can use the bundled Safari extensions and/or &lt;a href=&quot;https://www.opener.link&quot;&gt;Opener app&lt;/a&gt; to work around that.&lt;/p&gt;

&lt;p&gt;Then you will need to pick a method of installing, using &lt;a href=&quot;https://altstore.io&quot;&gt;AltStore&lt;/a&gt;, &lt;a href=&quot;https://appdb.to&quot;&gt;appdb&lt;/a&gt;, &lt;a href=&quot;https://sidestore.io&quot;&gt;SideStore&lt;/a&gt;, &lt;a href=&quot;https://github.com/khcrysalis/Feather&quot;&gt;Feather&lt;/a&gt;, &lt;a href=&quot;https://sideloadly.io&quot;&gt;Sideloadly&lt;/a&gt;, or similar. Depending which method you choose you may have to reinstall or reactivate the app from time to time.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;using-an-old-version-of-the-app&quot;&gt;Using an old version of the app&lt;/h2&gt;

&lt;p&gt;Here’s how you can downgrade to an older version of Twitter (pre-X changes) and install it in a way that means &lt;em&gt;it will not be automatically updated&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;img-with-caption&quot;&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.avif&quot; type=&quot;image/avif&quot; /&gt;
  &lt;source srcset=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.webp&quot; type=&quot;image/webp&quot; /&gt;
  &lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.png&quot; alt=&quot;&quot; title=&quot;&quot; loading=&quot;lazy&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption class=&quot;caption&quot;&gt;Twitter 9.66 (858339189) running on an iPhone Xs just now&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;downloading-the-old-version&quot;&gt;Downloading the old version&lt;/h3&gt;

&lt;p&gt;This is the trickiest part of the process as it requires installing some old software and following a guide. But don’t worry—it’s not that difficult! You’ll need access to an old version of iTunes (so it’s most easily done using Windows) and about 15 minutes to carry out the steps.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/qnblackcat/How-to-Downgrade-apps-on-AppStore-with-iTunes-and-Charles-Proxy&quot;&gt;Follow this great step-by-step tutorial&lt;/a&gt;. It might seem complicated but it is quite easy. A high level summary of what is involved in the guide is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install old iTunes &amp;amp; Charles Proxy&lt;/li&gt;
  &lt;li&gt;Configure intercepting of the latest app download&lt;/li&gt;
  &lt;li&gt;Change the version of the app to be downloaded&lt;/li&gt;
  &lt;li&gt;Download the old version&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id 858339189 (version 9.66) predates the rebrand to X&lt;/li&gt;
  &lt;li&gt;id 848443565 (version 9.7.2) predates Blue&lt;/li&gt;
  &lt;li&gt;id 840768123 (version 8.56) predates Spaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result of this process is a completely legitimate .ipa file, tied to your Apple ID in exactly the same way that apps normally are. As such, the resulting file can be installed on your device and once installed would be no different to an app you’ve downloaded directly from the App Store. This also means that if you share your .ipa with somebody else they would need to log in to your account using your Apple ID to do so, which is undesirable. Best if they download their own! Send them this blog post.&lt;/p&gt;

&lt;h3 id=&quot;avoiding-updates&quot;&gt;Avoiding updates&lt;/h3&gt;

&lt;p&gt;If you install the .ipa file onto your phone using Finder, iTunes, Apple Configurator it will install just fine. But, because of the metadata that is included the .ipa, it will be checked for updates and automatically updated soon after installation. We need to go deeper.&lt;/p&gt;

&lt;p&gt;There is a little-known method of installing apps on an iOS device which will prevent it from being checked for updates. I discovered this method &lt;a href=&quot;/2016/07/19/how-to-prevent-an-individual-ios-app-from-updating-forever/&quot;&gt;back in 2016&lt;/a&gt; when I used it to downgrade the eBay and Gmail Inbox apps.&lt;/p&gt;

&lt;h3 id=&quot;installing-the-app&quot;&gt;Installing the app&lt;/h3&gt;

&lt;p&gt;Before we begin, you don’t need to remove the current app you’re using. But best to make sure to backup any data you need from it regardless! You may, or may not, need to login once the old version of the app is reinstalled.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Copy your backup of .ipa somewhere where you can work on it&lt;/li&gt;
  &lt;li&gt;Open the .ipa with Archive Utility to decompress it&lt;/li&gt;
  &lt;li&gt;Expand the resulting folder until you go into the Payload folder&lt;/li&gt;
  &lt;li&gt;You’ll see Twitter.app (on macOS the icon has a “no entry sign” because it’s an iOS app)&lt;/li&gt;
  &lt;li&gt;Connect your device&lt;/li&gt;
  &lt;li&gt;Open Apple Configurator (you might need to &lt;a href=&quot;https://archive.org/details/apple-configurator&quot;&gt;use an old version&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Double click on your device (missing this will mean you can’t do step 8)&lt;/li&gt;
  &lt;li&gt;Click Add &amp;gt; App &amp;gt; Choose from my Mac…&lt;/li&gt;
  &lt;li&gt;Confirm you want to overwrite the old app&lt;/li&gt;
  &lt;li&gt;Wait for the app to install on your device&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’re done!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-payload.png&quot; alt=&quot;PNG&quot; title=&quot;Locating the Twitter .ipa Payload&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;questions&quot;&gt;Questions&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why can’t I add the app to my phone?&lt;/strong&gt;
    Either you missed step 7, or you need to &lt;a href=&quot;https://archive.org/details/apple-configurator&quot;&gt;use an older version of Apple Configurator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does downloading this way work?&lt;/strong&gt;
    iTunes used to be able to download apps this way, so we’re just persuading it to download a particular version. The app is attached to your Apple ID and totally legitimate, no funny business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does installing this way work?&lt;/strong&gt;
    Installing the payload directly means the App Store app has no record of the app being installed so it does not check for updates to it. You can confirm this by going to the App Store page for the app, where it will show it is yet to be downloaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will the old app stop working at some point?&lt;/strong&gt;
    Eventually, yes. Older versions lack support for modern Twitter features but that can be a desirable feature depending on your point of view. But let’s enjoy the old experience for as long as we can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get help to do this?&lt;/strong&gt;
    No, sorry. You need to do it yourself for various reasons. It will take less than 1 hour to read all the steps, then follow them one-by-one.&lt;/p&gt;

&lt;p&gt;Long live the bird!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 17 Aug 2023 18:39:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/08/17/going-back-to-the-old-pre-x-twitter-ios-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/08/17/going-back-to-the-old-pre-x-twitter-ios-app/</guid>
        </item>
      
    
      
        <item>
          <title>Five interesting facts about the design of the original PlayStation</title>
          <description>&lt;p&gt;Some things you probably didn’t realise about the design of the original &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/PlayStation_(console)&quot;&gt;Sony PlayStation&lt;/a&gt;&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it was inspired by Apple’s &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Macintosh_Plus&quot;&gt;Macintosh Plus&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;the colour is grey with a hint of violet to counteract plastic ageing/yellowing&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Sony&lt;/em&gt; acquired the PlayStation name from Yamaha&lt;/li&gt;
  &lt;li&gt;it led to the &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Vaio&quot;&gt;VAIO&lt;/a&gt;&lt;/em&gt; range of PCs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bonus fact:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Vaio#Etymology&quot;&gt;VAIO&lt;/a&gt;&lt;/em&gt; was originally an acronym for Video Audio Input Output&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;photo-reference&quot;&gt;Photo reference&lt;/h2&gt;

&lt;p&gt;Taken from &lt;em&gt;&lt;a href=&quot;https://www.worldcat.org/title/1050032044&quot;&gt;Digital Dreams: The Work of the Sony Design Center&lt;/a&gt;&lt;/em&gt; (1999, Paul Kunkel)&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-1.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-1.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-2.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-2.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-3.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-3.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-4.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/sony-playstation-facts-4.jpg&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 133%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },60000);
  
&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 12 Jun 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/06/12/five-interesting-facts-about-the-design-of-the-original-playstation/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/06/12/five-interesting-facts-about-the-design-of-the-original-playstation/</guid>
        </item>
      
    
      
        <item>
          <title>Ordering photocopies from Japan’s National Library</title>
          <description>&lt;p&gt;Recently I have been looking for a specific issue of an old 1985 Japanese PC magazine, but there are non currently for sale, nor have there been any sold for some time judging by sold listings. Over the past several years what I would normally do in this scenario is play the waiting game and hope one pops up for sale and that I can win it. But this time I decided to play things a little different, after finding a detailed table of contents for the magazine at Japan’s National Diet Library (NDL) and seeing that they offered a &lt;a href=&quot;https://www.ndl.go.jp/en/copy/remote/overseas.html&quot;&gt;remote duplication service&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;remote-duplication&quot;&gt;Remote duplication?&lt;/h2&gt;

&lt;p&gt;Japan has very strict copyright laws which are obeyed by their citizens and that goes for the library and its employees. Even though the magazine in question had been scanned onto their system the scans can only be viewed in person at NDL. That’s because even though the magazine is from 1985 it’s still in copyright so it’s a breach of that copyright to send the scans digitally: either over the internet to your web browser or by email. But they are totally fine making a photocopy and posting it to you. In this particular scenario, I’m totally fine with that!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: the majority of this process happens &lt;em&gt;in Japanese&lt;/em&gt; so make sure to use the built-in translation features of your operating system (&lt;em&gt;iOS&lt;/em&gt; can translate any selected text), email service (&lt;em&gt;Gmail&lt;/em&gt; will offer to translate foreign language emails), or web browser (&lt;em&gt;Safari&lt;/em&gt; and &lt;em&gt;Chrome&lt;/em&gt; can translate any web page). With that out of the way, let’s go!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;step-by-step&quot;&gt;Step-by-step&lt;/h2&gt;

&lt;p&gt;It turns out this process is pretty old-school, powered by a mix of website and email.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://iss.ndl.go.jp&quot;&gt;Find the item on the NDL website&lt;/a&gt; and &lt;a href=&quot;https://dl.ndl.go.jp&quot;&gt;check if it’s available to view online&lt;/a&gt;, if so there’s no need for you to do this process!&lt;/li&gt;
  &lt;li&gt;If it’s only available for remote duplication you’ll need to &lt;a href=&quot;https://ndlonline.ndl.go.jp/#!/userreg&quot;&gt;set up an account at NDL Online&lt;/a&gt;, a little tricky as I could only find the registration form in Japanese and make sure to confirm your email address&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ndlonline.ndl.go.jp/static/en/help-6a/index.html?lang=en#menu6a-4&quot;&gt;Fill out the remote duplication form&lt;/a&gt; making sure to specify all details and most importantly the page number range. You can request an estimate up-front or wait for the invoice on receipt. That’s right, you don’t pay for this service until after you receive the material!&lt;/li&gt;
  &lt;li&gt;Next, waiting for the item to be picked from the shelves and make it to the front of the duplication queue. For me this took about a week, but it was easy to see it progressing as &lt;a href=&quot;https://ndlonline.ndl.go.jp/#!/status&quot;&gt;the status the request can be checked on the website at all times&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;At this point I received an email - in Japanese - asking me to please check my address and make sure that it contains the country. For whatever reason it didn’t have country, so I made sure to correct it and &lt;em&gt;then replied to the email to let them know I’d done it&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;You’ll receive a final email when the item has been processed and shipped!&lt;/li&gt;
  &lt;li&gt;Wait for it to arrive, a mere 4 days to get to me in the UK&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;costs&quot;&gt;Costs&lt;/h2&gt;

&lt;p&gt;My request was only 3 magazine pages which, when scanned two-at-a-time, fit on two A3 sheets.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Charge&lt;/th&gt;
      &lt;th&gt;Cost&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;A4 paper (¥43 per sheet)&lt;/td&gt;
      &lt;td&gt;¥86&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Packing charge&lt;/td&gt;
      &lt;td&gt;¥350&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Postage charge&lt;/td&gt;
      &lt;td&gt;¥400&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;¥836&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Note: they recommend online payment by card as it’s free. If you really need to you can pay by bank transfer, but it costs an extra ¥4,000! Yikes.&lt;/p&gt;

&lt;h2 id=&quot;paying&quot;&gt;Paying&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier you don’t pay until you receive the material. Included alongside my two photocopies were: a 2-page stapled A4 invoice, a single A4 sheet payment request fax form, and an A5 information slip on how to request an online payment. So, let’s pay online!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Send an email to their email address, with your name and invoice number&lt;/li&gt;
  &lt;li&gt;They reply confirming and asking you to watch for an email from the payment provider, and reminding you that you need to pay within the 3 days during which the link is active&lt;/li&gt;
  &lt;li&gt;The payment website is basic but functional and straightforward&lt;/li&gt;
  &lt;li&gt;It accepted my strange European debit card&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Total cost in GBP £4.89&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;All in all a very useful service, fairly painless process, and one that I will no doubt use again in future!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/ascii-1985-11-chain-shot-intro.jpg&quot; alt=&quot;JPG&quot; title=&quot;Introduction to Chain Shot (by 森辺訓章 Kuniaki &amp;quot;Morisuke&amp;quot; Moribe) for FM-8/7, PC-9801 &amp;amp; PC-8801&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 24 May 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/05/24/ordering-photocopies-from-japans-national-library/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/05/24/ordering-photocopies-from-japans-national-library/</guid>
        </item>
      
    
      
        <item>
          <title>IntelligentPad: component-based drag-and-drop software creator</title>
          <description>&lt;p&gt;IntelligentPad was a drag-and-drop software creator based on the concept of reusable components. Pads could be reused on other pads. There was no programming language so software could be created by anybody, including those without programming experience. It was generally referred to as IP, and often “iPad” which resulted in &lt;a href=&quot;https://twitter.com/search?q=intelligentpad%20ipad&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;some users reminiscing on Twitter&lt;/a&gt; after the launch of Apple’s iPad device.&lt;/p&gt;

&lt;h3 id=&quot;history&quot;&gt;History&lt;/h3&gt;

&lt;p&gt;IntelligentPad was proposed in 1987 by Professor Yuzuru Tanaka 田中譲 of Knowledge Media Laboratory, Faculty of Engineering, Hokkaido University, and implemented using &lt;a href=&quot;https://en.wikipedia.org/wiki/Smalltalk#History&quot;&gt;Smalltalk-80&lt;/a&gt; in 1989. All software resources on a computer are represented in the form of Pads. Pads are standardised so that they can be connected to each other and by combining general pads such as text pads, graph pads, and image pads, a program (called a composite pad) is created.&lt;/p&gt;

&lt;p&gt;With it being a tool for &lt;a href=&quot;https://en.wikipedia.org/wiki/Rapid_application_development&quot;&gt;Rapid Application Development&lt;/a&gt; there are some similarities with Jean-Marie Hullot’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Interface_Builder&quot;&gt;Interface Builder&lt;/a&gt; (1986), Bill Atkinson’s &lt;a href=&quot;https://en.wikipedia.org/wiki/HyperCard&quot;&gt;HyperCard&lt;/a&gt; (1987), Denison Bollay’s &lt;a href=&quot;https://vimeo.com/62618532&quot;&gt;Action! (video)&lt;/a&gt; (1988), Fujitsu’s &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1574018275507412992&quot;&gt;TownsGEAR&lt;/a&gt; (1990), Microsoft’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Visual_Basic_(classic)&quot;&gt;Visual Basic&lt;/a&gt; (1991), Borland’s &lt;a href=&quot;https://en.wikipedia.org/wiki/History_of_Delphi_(software)&quot;&gt;Delphi&lt;/a&gt; (1995), and also Apple Research Labs’ &lt;a href=&quot;https://web.archive.org/web/19970104030734/http://www.research.apple.com/research/proj/Learning_Concepts/squeak/intro.html&quot;&gt;Squeak&lt;/a&gt; (1996, which also happened to be created using Smalltalk-80).&lt;/p&gt;

&lt;p&gt;IntelligentPad could be used to build a variety of software from a working calculators and digital clocks (as shown in the documentation/tutorials), through to fully blown applications such as a &lt;a href=&quot;https://www.jcprg.org/ipad/&quot;&gt;database of nuclear reactors&lt;/a&gt;. Examples quoted in early-1999 included a &lt;a href=&quot;https://drops.dagstuhl.de/opus/volltexte/2021/15137/pdf/DagSemRep-251.pdf&quot; title=&quot;Declarative Data Access on the Web&quot;&gt;Kyoto culture database “THE MIYAKO”&lt;/a&gt; (pdf, p.13) and IntelligentPad’s own “Piazza” project. But, both were still under development at that time.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/intelligentpad-about.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;IntelligentPad for Macintosh (1994, Hitachi)&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;desktop-software&quot;&gt;Desktop software&lt;/h3&gt;

&lt;p&gt;Implementations of the IntelligentPad standard were available for multiple platforms, and all could mutually exchange pads. Hitachi were most active, creating versions for Mac (as both Shareware and limited demo), HP workstations and a version for Windows with Fujitsu. To add to that Fujitsu created a version for Solaris workstations. Elsewhere K-Plex released a commercial version under the name &lt;a href=&quot;http://www.kplex.com/products/plexware.html&quot;&gt;PlexWare&lt;/a&gt; (&lt;a href=&quot;http://www.kplex.co.jp/products/plexware/PlexWare.html&quot;&gt;Japanese&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;From 1993 the standard was overseen by the IntelligentPad Consortium, a non-profit organisation aiming to promote and standardise IntelligentPad. The consortium is made up of 36 corporate members and individual members, including Fujitsu, Hitachi Software Engineering, Fuji Xerox, NTT, and NEC. The same year a live-demo was presented &lt;a href=&quot;https://kobe-cc.jp/kcc/wp-content/uploads/2017/12/139.pdf&quot;&gt;in Kobe at the first TED conference held outside of North America&lt;/a&gt; (pdf). The proliferation of the world wide web at this point meant the beginning of some adjustments to the concept.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/intelligentpad-clock.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;Sample: Digital Clock Pad&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-piazza-project&quot;&gt;The “Piazza” project&lt;/h3&gt;

&lt;p&gt;Given that the core concept meant Pads were freely redistributable components, a problem arose that software made using IntelligentPad was difficult to sell. The software was free, Pads were free, and there was no distribution or billing system available.&lt;/p&gt;

&lt;p&gt;So, the IntelligentPad Consortium proposed a virtual space for content distribution called Piazza, which was presented at the &lt;a href=&quot;https://art-science.org/nicograph/&quot;&gt;NicoGRAPH&lt;/a&gt; conference of art and science in 1998. Users would gain the ability to place their own applications and image data in the Piazza space in the form of pads, and have other users download them. The proposal was complicated by Japan’s copyright laws, which caused the need for a middle-man clearing house to be involved issuing copyright registrations, as well as distributors who would encrypt the content. It makes the single point of contact for modern App Stores appear to be the ultimate in luxury!&lt;/p&gt;

&lt;p&gt;In March 1999 this was all just an idea, with no working prototype available. Piazza version 1.0 was released in November 1999.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/intelligentpad-piazza.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;IntelligentPad Piazza&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;reinventing-the-internet&quot;&gt;Reinventing the internet&lt;/h3&gt;

&lt;p&gt;Further proposals included an “internet sandbox” that used the Piazza to connect elementary schools over long distances, enabling them to exchange content and communicate with each other, and the development of a search engine for content distributed on Piazza. To me this sounds a little like reinventing the internet.&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;p&gt;My interest in IntelligentPad begun when I found Japanese version 2.0.1J in my archive of &lt;a href=&quot;/2021/10/30/macintosh-magazine-media/&quot;&gt;Macintosh Magazine Media&lt;/a&gt; on a Japanese MacUser magazine CD-ROM from 1996. With that knowledge I headed over to &lt;a href=&quot;http://discmaster.textfiles.com&quot;&gt;DiscMaster&lt;/a&gt; and found English version 2.0.1 on a 1996 CD-ROM sold by German Apple reseller GRAVIS that contained their catalogue, software and updates. One world!&lt;/p&gt;

&lt;p&gt;You can download those Macintosh files at &lt;a href=&quot;https://macintoshgarden.org/apps/intelligentpad&quot;&gt;macintoshgarden.org/apps/intelligentpad&lt;/a&gt; and try it in a classic Macintosh emulator such as the Infinite Mac web-based emulators (&lt;a href=&quot;https://system7.app&quot;&gt;System 7&lt;/a&gt;, &lt;a href=&quot;https://KanjiTalk7.app&quot;&gt;KanjiTalk 7&lt;/a&gt;) and do make sure to copy the files to the emulated hard drive before expanding and running IntelligentPad. Documentation is included and there are Tutorials to create a variety of things from a simple calculator, to a more advanced digital clock, and even a full software application in the form of an interactive map with database browser.&lt;/p&gt;

&lt;p&gt;For Windows, IntelligentPad version 4 released in 2000 still works on Windows 8 in XP Mode. There’s also a Java version of IntelligentPad. Both can be &lt;a href=&quot;http://pads.kplex.co.jp/_taiken/dl2.html&quot;&gt;downloaded from the Consortium website&lt;/a&gt; though I am yet to try those specific versions myself.&lt;/p&gt;

&lt;h2 id=&quot;demonstration&quot;&gt;Demonstration&lt;/h2&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 4/3;&quot; videoid=&quot;4an1bzfOlKA&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;aftershock&quot;&gt;Aftershock&lt;/h3&gt;

&lt;p&gt;In 1995 the concept was reimagined as IntelligentBox, which added an extra dimension as it was capable of displaying and manipulating 3D models. An internet-ready version used the phrase Web Pebble (“Webble”) instead of Pad or Box, and yet another version used the phrase “Meme Media” to refer to reusable components comprised of parts of web pages.&lt;/p&gt;

&lt;h3 id=&quot;intelligentpad-today&quot;&gt;IntelligentPad today&lt;/h3&gt;

&lt;p&gt;By now you might think IntelligentPad is long forgotten, but I’m here to blow your mind. The &lt;a href=&quot;https://ipad.live7.jp&quot;&gt;IntelligentPad Museum/Palace&lt;/a&gt; website is still being updated.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;hhttp://pads.kplex.co.jp/index.html&quot;&gt;IntelligentPad Consortium home page&lt;/a&gt; (2005)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ascii.jp/elem/000/000/315/315443/&quot;&gt;Report on the 6th IntelligentPad workshop: marketplace for software and components&lt;/a&gt; (1999)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.kplex.com/products/intelligentpad.html&quot;&gt;K-Plex IntelligentPad Overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;related-reading&quot;&gt;Related reading&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://drops.dagstuhl.de/opus/volltexte/2021/15137/pdf/DagSemRep-251.pdf&quot; title=&quot;Declarative Data Access on the Web&quot;&gt;Declarative Data Access on the Web&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://annas-archive.org/md5/d12a196536c538bc713e8d2175afdce5&quot;&gt;Meme Media and Meme Market Architectures: Knowledge Media for Editing, Distributing, and Managing Intellectual Resources&lt;/a&gt; (book)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://slideplayer.com/slide/4962007/&quot;&gt;Meme Media Architecture for the Re-editing and Re-distribution of Web Resources&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.researchgate.net/publication/37553567_Meme_Media_for_Clipping_and_Combining_Web_Resources&quot;&gt;Meme Media for Clipping and Combining Web Resources&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.researchgate.net/publication/236148025_Advanced_Webble_Application_Development_Directly_in_the_Browser_by_Utilizing_the_Full_Power_of_Meme_Media_Customization_and_Event_Management_Capabilities&quot;&gt;Advanced “Webble” Application Development Directly in the Browser by Utilizing the Full Power of Meme Media Customization and Event Management Capabilities&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://link.springer.com/chapter/10.1007/978-3-642-38836-1_2&quot;&gt;Web Version of IntelligentBox (WebIB) and Its Integration with Webble World&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.researchgate.net/publication/260347336_Media_Multiplicity_at_Your_Fingertips_Direct_Manipulation_Based_on_Webbles&quot;&gt;Media Multiplicity at Your Fingertips: Direct Manipulation Based on Webbles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 17 May 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/05/17/intelligentpad-component-based-drag-and-drop-software-creator/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/05/17/intelligentpad-component-based-drag-and-drop-software-creator/</guid>
        </item>
      
    
      
        <item>
          <title>Fixing bugs using Bird on Palm OS</title>
          <description>&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-pixels.jpg&quot; alt=&quot;JPG&quot; title=&quot;Tenohira Hanafuda for Palm OS, running on a Sony CLIÉ PEG-SJ22&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fudawiki.org/en/hanafuda/video-games/palm/tenohira-hanafuda-kai&quot;&gt;Tenohira Hanafuda Kai&lt;/a&gt; (掌花札 kai) is a koi-koi card game for Palm OS, created in 2001 by Hiroki Takahashi. It’s a fun game with varying difficulty, stats tracking, and lovely high-resolution (for the time!) colour graphics.&lt;/p&gt;

&lt;p&gt;But, it has one serious problem: sometimes it will forfeit the current round when you choose to continue!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-buggy.gif#pixel&quot; alt=&quot;GIF&quot; title=&quot;https://youtube.com/shorts/0fZefFpGd5Y&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;analysis&quot;&gt;Analysis&lt;/h2&gt;

&lt;p&gt;After repeated play I figured out that the game would continue as intended only if I pressed the right half of the こいこい (koi-koi) button.&lt;/p&gt;

&lt;p&gt;But if I pressed the left half of the button it would not behave as expected and forfeit the round. More on that later.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-problem.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;The problem happens when we press the left side of the こいこい button&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-plan&quot;&gt;The Plan&lt;/h2&gt;

&lt;p&gt;One of the beautiful things about Palm OS is that apps and their resources are viewable, and even editable, right there on the device itself! It’s a lot like Classic Macintosh in that regard, which is no surprise as Palm took a lot of inspiration from the original Mac.&lt;/p&gt;

&lt;p&gt;And much like ResEdit on Macintosh, Palm had it’s own equivalent app in &lt;a href=&quot;https://palmdb.net/app/rsrcedit&quot;&gt;RsrcEdit&lt;/a&gt; by Quartus, though I preferred to use an app called &lt;a href=&quot;https://palmdb.net/app/bird&quot;&gt;Bird&lt;/a&gt; by Philippe Guillot. You can view strings, bitmaps, menu bars, and other user interface elements (organised as Forms).&lt;/p&gt;

&lt;h2 id=&quot;lets-do-this&quot;&gt;Let’s do this&lt;/h2&gt;

&lt;p&gt;So I launched Bird and loaded up the contents of Tenohira Hanafuda Kai, and went through all the forms until I found the one that displays the continue prompt.&lt;/p&gt;

&lt;p&gt;Interestingly the form is dual-purpose. It contains the continue/stop buttons (a List of two items) and also a single button (了解; confirm) used on a different prompt. It’s overlaid on the continue button in a close enough position to be suspect. Perhaps it’s moved slightly at run time?&lt;/p&gt;

&lt;p&gt;We can easily change the order of the controls on the form by cutting and pasting, so we do that with the List and it now comes below the button in the order and will be drawn last on the screen. Presumably the things drawn last are the first to capture interactions? Let’s see.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-bird.gif#pixel&quot; alt=&quot;GIF&quot; title=&quot;https://youtube.com/shorts/yRLfHoHkjTY&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

&lt;p&gt;I apply all changes and they are saved directly into the app data. Launching the game again and playing enough to trigger another continue/stop prompt, I tap the left half of the button and… the game continues as expected. There is no strange forfeit. The problem has been solved!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-fixed.gif#pixel&quot; alt=&quot;GIF&quot; title=&quot;https://youtube.com/shorts/3faHHcuSQv4&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;addendum&quot;&gt;Addendum&lt;/h2&gt;

&lt;p&gt;I figured this out back in 2019 and edited the app on my Sony CLIÉ device. Recently I’ve been using &lt;a href=&quot;https://cloudpilot-emu.github.io&quot;&gt;CloudPilot&lt;/a&gt; to run Palm OS apps and games on my iPhone. So to play Tenohira Hanafuda Kai I had to either find the old modified game file, or do it all over again from scratch. I chose to do it again to test my memory and so I could document the process.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 07 Oct 2022 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/10/07/fixing-bugs-using-bird-on-palm-os/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/10/07/fixing-bugs-using-bird-on-palm-os/</guid>
        </item>
      
    
      
        <item>
          <title>Playing Hanafuda on Palm OS in a web browser</title>
          <description>&lt;p&gt;I recently found out that it’s possible to run Palm OS in a web browser, using &lt;a href=&quot;https://cloudpilot-emu.github.io&quot;&gt;cloudpilot-emu.github.io&lt;/a&gt; which is optimised for use on iPhone and Android, so you can use old Palm apps and run old games and apps on your smart phone!&lt;/p&gt;

&lt;h2 id=&quot;koi-koi&quot;&gt;Koi-Koi&lt;/h2&gt;

&lt;p&gt;I play a lot of video game hanafuda and out of all platforms Palm OS has three of the most interesting games. To play these games I carry around either an old Palm OS device (Sony Clié PEG-SL10) or an Android phone (Galaxy Fame Lite S6790) that runs an old Palm OS emulator.&lt;/p&gt;

&lt;p&gt;So it’s great news that Palm OS Hanafuda games can now be played easily on modern devices.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/palmos-hanafuda-real.jpg&quot; alt=&quot;Keisuke Mitani&apos;s Koi-Koi running on Sony Clié PEG-SL10&quot; title=&quot;Keisuke Mitani&apos;s Koi-Koi running on Sony Clié PEG-SL10&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;preparation&quot;&gt;Preparation&lt;/h2&gt;

&lt;p&gt;We’re going to use CloudPilot to create two Palm OS devices, both running in Japanese, and install the games we want to play. You could create English devices, but there will be issues with some text displayed in the games as they expect Japanese text support.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Sony Clié N700C (Colour, Japanese)&lt;/li&gt;
  &lt;li&gt;Palm V (Greyscale, Japanese)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can source alternative Palm OS system files from &lt;a href=&quot;https://palmdb.net/app/palm-roms-complete&quot;&gt;PalmDB&lt;/a&gt;. You might choose to add an English device to run other Palm OS games and apps.&lt;/p&gt;

&lt;h2 id=&quot;support-files&quot;&gt;Support files&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.dropbox.com/s/b2obo97hlbb89n5/Palm%20OS%20Hanafuda%20games.zip?dl=0&quot;&gt;Download Palm OS Hanafuda games and required support files&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;p&gt;Steps to play in Japanese, carry out twice to create one device each of Grey/Colour:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;add new device and pick the respective .rom file&lt;/li&gt;
  &lt;li&gt;go through first launch setup and touch screen calibration&lt;/li&gt;
  &lt;li&gt;top right button to add the .prc files to device&lt;/li&gt;
  &lt;li&gt;top left button to reset, normal boot&lt;/li&gt;
  &lt;li&gt;launch and play the games&lt;/li&gt;
  &lt;li&gt;switch devices using the Sessions button in the website toolbar&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;games&quot;&gt;Games&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Koi-Koi&lt;/strong&gt; (1999, by Keisuke Mitani/Palm Boarderz)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://fudawiki.org/en/hanafuda/video-games/palm/koi-koi&quot;&gt;fudawiki.org/en/hanafuda/video-games/palm/koi-koi&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;my favourite&lt;/li&gt;
      &lt;li&gt;cards can be picked from the field as well as your hand&lt;/li&gt;
      &lt;li&gt;only works on old greyscale devices
&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/palmos-hanafuda-1.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;Koi-Koi&quot; /&gt;&lt;br /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tenohira Hanafuda Kai&lt;/strong&gt; (2001, by Hiroki Takahashi)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://fudawiki.org/en/hanafuda/video-games/palm/tenohira-hanafuda-kai&quot;&gt;fudawiki.org/en/hanafuda/video-games/palm/tenohira-hanafuda-kai&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;low and high resolution support&lt;/li&gt;
      &lt;li&gt;multiple opponents/difficulties&lt;/li&gt;
      &lt;li&gt;tracks play history and win ratios
&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/palmos-hanafuda-2.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;Tenohira Hanafuda Kai&quot; /&gt;&lt;br /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;palm Hanafuda&lt;/strong&gt; (2001, by Oruge/e-frontier)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://fudawiki.org/en/hanafuda/video-games/palm/palm-hanafuda&quot;&gt;fudawiki.org/en/hanafuda/video-games/palm/palm-hanafuda&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;has 5 game types, including Mushi&lt;/li&gt;
      &lt;li&gt;I could swear this displayed in English on my real Palm OS device
&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/palmos-hanafuda-3.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;palm Hanafuda&quot; /&gt;&lt;br /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Catch-A-Bird&lt;/strong&gt; (2003, by Juergen Bermann)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://fudawiki.org/en/hanafuda/video-games/palm/catch-a-bird&quot;&gt;fudawiki.org/en/hanafuda/video-games/palm/catch-a-bird&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;similar to Go-stop&lt;/li&gt;
      &lt;li&gt;no idea how to get it to display in colour&lt;/li&gt;
      &lt;li&gt;doesn’t work on old greyscale devices
&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/palmos-hanafuda-4.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;Catch-a-Bird&quot; /&gt;&lt;br /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 03 Oct 2022 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/10/03/playing-hanafuda-on-palm-os/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/10/03/playing-hanafuda-on-palm-os/</guid>
        </item>
      
    
      
        <item>
          <title>My System 7 software choices</title>
          <description>&lt;p&gt;System 7 is a great operating system. The experience using it today remains very close to modern macOS. It’s surprising how little has changed on our Desktop in the 30 years since. That said, the experience can be improved with the judicious use of additional software.&lt;/p&gt;

&lt;p&gt;When adding things to the system my goal is always to increase quality of life and improve user experience. Functionality is key and nothing is added without careful consideration. If I can install one package to add several functions I’d rather do that than add several individual packages. If I can install software to match modern macOS features or muscle memory then I’ll do so.&lt;/p&gt;

&lt;p&gt;Below are my personal choices that improve my time spent using System 7.&lt;/p&gt;

&lt;h2 id=&quot;control-panels&quot;&gt;Control Panels&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/better-edit-keys&quot;&gt;Better Edit Keys&lt;/a&gt; - adds modern text editing and selection keyboard controls, such as the ability to grow the selection by word&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/capture-402&quot;&gt;Capture&lt;/a&gt; - screen grabbing software&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/capture-ae&quot;&gt;Capture AE&lt;/a&gt; - logger for Apple Events calls&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/aladdin-desktop-tools&quot;&gt;Desktop Makeover&lt;/a&gt; - provides custom Finder features such as menu shortcuts, enhanced diskette icons, allows you to hide Balloon Help&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/draganywindow&quot; title=&quot;https://macintoshgarden.org/apps/draganywindow&quot;&gt;DragAnyWindow&lt;/a&gt; - adds solid dragging of any window, apps can be excluded&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/hidden-finder-features&quot;&gt;Hidden Finder Features&lt;/a&gt; - enables hidden Finder keyboard shortcuts to create aliases by dragging, reveal alias original, and move file to Trash&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/keyquencer&quot;&gt;KeyQuencer&lt;/a&gt; - macro and automation utility, the ultimate tool for power-users&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/popup-folder-201&quot;&gt;PopupFolder&lt;/a&gt; - allows access to folder contents from hierarchical pop-up menu without having to drill down into folders&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/quickeys-353&quot;&gt;QuicKeys&lt;/a&gt; - macro and automation utility, I use it only for composing Apple Event calls&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/smart-scroll&quot;&gt;Smart Scroll&lt;/a&gt; - live scrolling and proportionally sized scrollbar thumb&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/smartlaunch-308&quot;&gt;SmartLaunch&lt;/a&gt; - application launcher, I launch this using Cmd+Return KeyQuencer macro&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/snitch&quot;&gt;Snitch&lt;/a&gt; - Get Info extender, can do batch type/creator changes by summoning whilst holding Shift&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/symbionts-286&quot;&gt;Symbionts&lt;/a&gt; - occasionally used to troubleshoot Control Panels and Extensions&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/windowshade&quot;&gt;WindowShade&lt;/a&gt; - collapse a window into its title bar by double-clicking&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/yesnocancel-121&quot;&gt;YesNoCancel&lt;/a&gt; - adds modern keyboard control to dialog boxes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;extensions&quot;&gt;Extensions&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/2020patch&quot;&gt;2020Patch&lt;/a&gt; - fixes year 2020 date bug&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/about&quot;&gt;About&lt;/a&gt; - replacement for About This Macintosh window, with task manager features&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/apollo&quot;&gt;Apollo&lt;/a&gt; - pop-up menu at any location to select and launch apps&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/copypaste-33&quot;&gt;CopyPaste&lt;/a&gt; - multiple clipboard manager, works everywhere but ResEdit!&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macgui.com/downloads/?file_id=26327&quot;&gt;DirtyDesk&lt;/a&gt; - disables Clean Up Desktop menu item&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/natural-order&quot;&gt;Natural Order&lt;/a&gt; - improves sorting order in Finder&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;text-editors&quot;&gt;Text Editors&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macgui.com/downloads/?file_id=20966&quot;&gt;Alpha&lt;/a&gt; - programmer’s editor, includes TCL scripting&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/anarcho-16&quot;&gt;Anarcho&lt;/a&gt; - has a few unique features&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/bbedit-5&quot;&gt;BBEdit&lt;/a&gt; - packed with features&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macgui.com/downloads/?file_id=21218&quot;&gt;Eddan&lt;/a&gt; - has a way to summon THINK References&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;word-processors&quot;&gt;Word Processors&lt;/h2&gt;

&lt;p&gt;Take your pick of the best:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/macwrite-pro-15&quot;&gt;MacWritePro&lt;/a&gt; 1.5&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/nisus-writer-4&quot;&gt;Nisus Writer&lt;/a&gt; 4&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/microsoft-word&quot;&gt;Word&lt;/a&gt; 5.1&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/wordperfect-35-novell&quot;&gt;WordPerfect&lt;/a&gt; 3.5&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/writenow&quot;&gt;WriteNow&lt;/a&gt; 4&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;other-utilities&quot;&gt;Other Utilities&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/compact-pro&quot;&gt;CompactPro&lt;/a&gt; - file compressor/decompressor&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/graphicconverter-4x&quot;&gt;GraphicConverter&lt;/a&gt; - image file converter&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/disktop-453&quot;&gt;DiskTop&lt;/a&gt; - file manager desk accessory&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/gregs-browser&quot;&gt;Greg’s Browser&lt;/a&gt; - file manager with StuffIt integration&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/masterfinder-13-fat&quot;&gt;MasterFinder&lt;/a&gt; - multi-column file manager&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/mindexpander&quot;&gt;MindExpander&lt;/a&gt; - file decompressor&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/super-resedit-213&quot;&gt;(Super) ResEdit&lt;/a&gt; - resource editor with built-in code viewer&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/resorcerer-125&quot;&gt;Resorcerer&lt;/a&gt; - resource editor with built-in code viewer&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/shrinkwrap-21&quot;&gt;ShrinkWrap&lt;/a&gt; - disk and archiving helper&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.macgui.com/downloads/?file_id=23652&quot;&gt;ShowSizes&lt;/a&gt; - disk usage analyser&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/type-resolve-201&quot;&gt;Type Resolve&lt;/a&gt; - drag-and-drop type/creator changer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;operating-system&quot;&gt;Operating System&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/macintosh-os-755&quot;&gt;System 7.5.3 plus updates&lt;/a&gt; - Apple released this version for free!&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/japanese-language-kit-v12&quot;&gt;Japanese Language Kit&lt;/a&gt; - adds support for Japanese, I use this on a duplicate System folder&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/system-picker&quot;&gt;System Picker&lt;/a&gt; - choose which system folder to boot from&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/applescript-11&quot;&gt;AppleScript 1.1&lt;/a&gt; - to enable Finder Scripting on &lt;em&gt;System 7.1&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;related-posts&quot;&gt;Related posts&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh&quot;&gt;Turning an iPad Pro into the Ultimate Classic Macintosh&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/21/building-basiliskii-for-ios/&quot;&gt;How to install BasiliskII on your iPad&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/19/automating-interactions-using-apple-events/&quot;&gt;Exploring Custom Keyboards and Automation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/03/28/changing-the-size-of-toolbar-items-using-resedit/&quot;&gt;Optimising Macintosh app toolbars for touch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/24/macintosh-drawing-software-compared/&quot;&gt;Macintosh drawing software compared&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/25/mixing-external-tools-across-deneba-software/&quot;&gt;Mixing External Tools across Deneba software&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;My System 7 software choices&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/05/03/interoperability-of-system-7-and-ios/&quot;&gt;About the interoperability of System 7 and iOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 30 Apr 2021 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/04/30/my-system-7-software-choices/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/04/30/my-system-7-software-choices/</guid>
        </item>
      
    
      
        <item>
          <title>Mixing External Tools across Deneba software</title>
          <description>&lt;p&gt;One of the most interesting things about Deneba’s early 1990s Macintosh drawing apps is that they were built in a modular way with additional files adding specific features. These days we call those Plugins but back in 1990 Deneba referred to them as External Tools.&lt;/p&gt;

&lt;p&gt;They are first seen in UltraPaint and later in Canvas 3.0, artWORKS and Canvas 3.5. Tools are categorised as several types: Effect, I/O, Manager, Modifier, Object, Setting and Other. Each adds functionality and/or user interface to the app in its own way.&lt;/p&gt;

&lt;p&gt;Amongst the Deneba material on an AppleLink archive disk from Spring 1993 there are a bunch of “non-shipping” tools labelled for use with Canvas 3.0. Interestingly, they are listed individually and also as a combined set. If we extract the combined set of tools there’s a single additional tool that is somewhat out of place: the Waco Tool (for Wacom pressure sensitive pen) which is marked with UltraPaint’s type and creator information.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/deneba-external-tools-0.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;Spot the odd one out&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I can confirm this that this tool - which I don’t believe was ever included with UltraPaint - allows my Wacom ArtPad II to work. Bonus! But why was this tool meant for UltraPaint included in a download for Canvas. Could it work in Canvas?&lt;/p&gt;

&lt;p&gt;I put the additional files in the Canvas Tools folder and started the app with the Space Bar held down. This starts the Tool Picker allowing you to select which tools you want to use for that session.&lt;/p&gt;

&lt;p&gt;Boom! There it was.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/deneba-external-tools-1.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;A wild Waco Tool appears in the Tool Picker&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;experimentation&quot;&gt;Experimentation&lt;/h2&gt;

&lt;p&gt;I tried copying some tools from Canvas to UltraPaint and artWORKS. They didn’t work. What was the difference? In ResEdit they looked largely the same. On a whim I looked at the type/creator codes using Snitch’s Get Info extension. I noticed that disabled tools had a different type code. If the app is using type to store information like that, maybe it’s only accepting certain type codes?&lt;/p&gt;

&lt;p&gt;So I copied a tool from Canvas over to artWORKS, being careful to pick one that already exists so it was as much like for like as it could be. It didn’t show up. Then I changed the type/creator codes and… it did show up! And not only that, but the tool worked in the app!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/deneba-external-tools-2.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;External Tool type/creator codes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was surprised, and also impressed. Deneba did a really great job with the architecture and API of these apps for things to remain this compatible over the course of several years. All three apps share code and resources. If you take a look at both UltraPaint and artWORKS in ResEdit you’ll see that they share a lot of DNA, artWORKS is basically an enhanced version of UltraPaint. I’d bet all three apps were built from the same code base.&lt;/p&gt;

&lt;h2 id=&quot;how-far-can-we-go&quot;&gt;How far can we go?&lt;/h2&gt;

&lt;p&gt;Some questions remained: how many tools could this be done with? Would they install and function? Could I use tools in artWORKS that were meant only for the more expensive Canvas? There was only one way to find out: try all the tools!&lt;/p&gt;

&lt;p&gt;I set about copying the tools from one app to artWORKS, my favourite app, changing the creator and type codes to match. To make bulk changes to type/creator I used Get Info (with Snitch) on multiple items, but I could have used software such as FileTyper.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Year&lt;/th&gt;
      &lt;th&gt;Software&lt;/th&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Creator&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1990&lt;/td&gt;
      &lt;td&gt;UltraPaint&lt;/td&gt;
      &lt;td&gt;TOOL&lt;/td&gt;
      &lt;td&gt;ULTR&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1992&lt;/td&gt;
      &lt;td&gt;Canvas 3.0&lt;/td&gt;
      &lt;td&gt;TOL2&lt;/td&gt;
      &lt;td&gt;DAD2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1993&lt;/td&gt;
      &lt;td&gt;artWORKS&lt;/td&gt;
      &lt;td&gt;TOL4&lt;/td&gt;
      &lt;td&gt;DAD3&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;very-far&quot;&gt;Very far!&lt;/h2&gt;

&lt;p&gt;The result was quite amazing with artWORKS total tools increasing from 59 to 92—that’s 33 additional tools! This is not counting a handful of tools that install but do not present themselves in the user interface. I’ll need to investigate those further at a later date.&lt;/p&gt;

&lt;p&gt;Doing the same for Canvas 3.0 takes us from 74 to an even higher total of 97 tools! It’s worth noting there are a couple of tools that go by the same internal name: PANTONE™ Colors (Color Manager) and Separations (PostScript) but I’ve chosen not to merge those rows of the table as there are some subtle differences in their capabilities. Also the functionality of the Grids &amp;amp; Rulers tool is built-in.&lt;/p&gt;

&lt;p&gt;The table below shows tools present in each of the apps, each with their own symbol, and then the final combined column shows all tools that are compatible with artWORKS with the symbols showing which app that tool originated from.&lt;/p&gt;

&lt;h1 id=&quot;external-tools&quot;&gt;External Tools&lt;/h1&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table id=&quot;deneba-external-tools&quot;&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Software Name&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;UltraPaint&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;Canvas 3.0&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;artWORKS&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;artWORKS+&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;Canvas 3.0+&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt; Tool Loader&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt; ToolPicker&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Alignment Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Antialias&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Arrowhead Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Auto Trace&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Balloon Help&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Bézier Text&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Bind&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Blend Objects&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Blotter Pen&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Brush Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Canvas 1.0 I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Canvas 2.1 I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;CGM I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Chalk&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Charcoal&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Coil&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Color Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Combine Objects&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Concentric Circles&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Contrast&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Convert To&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Cube&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Cylinder&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Dash Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Dimensioning&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Doughnuts&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Drawing Size&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Dropper&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Duplication Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;DXF I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Filters&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fingertip&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fractals&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fuzz lasso/Soften Edges&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GIF I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Gradient Fill&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Gray Scale Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Grid Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Grid Tool/Maker&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Grids &amp;amp; Rulers&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Hatch Patterns&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;IGES I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Illustrator I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Imported Filters&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Impressionist&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Layer Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Luminance&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;MacDraw I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;MacPaint I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Magic Wand&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Masking&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Mover&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Multigon&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;NameStamp&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Object Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Object Tint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;PANTONE™ Colors&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Parallel Lines&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Pattern Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Pen Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Place&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Point Rotate&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;PostScript&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Preferences&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Pressure Pen&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;QuickTime&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Quill Pen&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Registration Marks&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Resistor&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;RGB Color Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Rubber Stamp&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Ruler Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Scale Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Search &amp;amp; Replace&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Selection Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Separations&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Sharpen and Blur&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Size Other&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Slides&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Smart Mouse&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Sounder&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spacing Other&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spelling&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spiral&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Split&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spray Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;StartupScreen I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;System 7.0 Options&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Text Ruler&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Text Utilities&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Textures&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Three Color Airbrush&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;TIFF I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;TimeStamp&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Tool Info&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;UltraPaint 1.0 I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Undo Killer&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Water Droplet&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;tfoot&gt;
      &lt;tr&gt;
        &lt;td&gt;Total Tools&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;30&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;74&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;58&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;92&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;97&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tfoot&gt;
  &lt;/table&gt;

  &lt;p&gt;Key:&lt;/p&gt;

  &lt;p&gt;▲ UltraPaint tool
  ● Canvas 3.0 tool
  (○ that installs but doesn’t show any interface)
  ■ artWORKS tool&lt;/p&gt;

  &lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

  &lt;p&gt;So after all this I now have an install of artWORKS (let’s call it artWORKS+) that includes additional tools from both UltraPaint and Canvas 3.0 and I had a ton of fun figuring all this out. And I’m only 30 years late!&lt;/p&gt;

  &lt;p&gt;Here’s a download of my artWORKS Tools folder containing all the combined tools (use StuffIt! Expander 5.5 to extract): &lt;a href=&quot;/files/artWORKS-Tools-Combined.sit&quot;&gt;artWORKS-Tools-Combined.sit&lt;/a&gt; [1.1MB]&lt;/p&gt;

  &lt;h3 id=&quot;footnote&quot;&gt;Footnote&lt;/h3&gt;

  &lt;p&gt;A note about later versions of Canvas: version 3.5 provides roughly the same tools as version 3.0. The handful of unique tools in version 3.5 are not compatible with artWORKS. Version 5.0 features similar tools but they’re no longer compatible with the earlier apps.&lt;/p&gt;

  &lt;h3 id=&quot;related-posts&quot;&gt;Related posts&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh&quot;&gt;Turning an iPad Pro into the Ultimate Classic Macintosh&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/21/building-basiliskii-for-ios/&quot;&gt;How to install BasiliskII on your iPad&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/19/automating-interactions-using-apple-events/&quot;&gt;Exploring Custom Keyboards and Automation&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/03/28/changing-the-size-of-toolbar-items-using-resedit/&quot;&gt;Optimising Macintosh app toolbars for touch&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/24/macintosh-drawing-software-compared/&quot;&gt;Macintosh drawing software compared&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Mixing External Tools across Deneba software&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/30/my-system-7-software-choices/&quot;&gt;My System 7 software choices&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/05/03/interoperability-of-system-7-and-ios/&quot;&gt;About the interoperability of System 7 and iOS&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;

&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 25 Apr 2021 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/04/25/mixing-external-tools-across-deneba-software/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/04/25/mixing-external-tools-across-deneba-software/</guid>
        </item>
      
    
      
        <item>
          <title>Macintosh drawing software compared</title>
          <description>&lt;p&gt;Below is a list of various drawing applications that are available to use with System 7 (68K).&lt;/p&gt;

&lt;p&gt;I’m mostly interested in apps that can do both pixel and vector, and that support layers and transparency. The table below might be useful for at-a-glance comparisons. Apps run in both mono or colour, unless stated otherwise.&lt;/p&gt;

&lt;p&gt;Deneba artWORKS can do both pixel and vector, has layers and partial transparency. The bottom layer is opaque and all layers on top are transparent. Collections of pixels are managed as a special object, and there’s a limit of 8 layers. The user interface is brilliant.&lt;/p&gt;

&lt;p&gt;SuperPaint has two layers: pixel and vector with transparency in all but the base layer. Though it allows cut/copy and paste from the pixel layer to the vector layer at which point those collections of pixels are managed as SuperBits objects and can be edited in isolation.&lt;a href=&quot;https://twitter.com/tumult/status/1432279388519763972&quot;&gt;ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Freehand is king of the vector apps. It really was so much better than Illustrator. Such a shame that it’s no longer around. To use it on iPad is quite something!&lt;/p&gt;

&lt;p&gt;Also interesting is how many early versions of successful apps didn’t support layers (Photoshop, Freehand) or full transparency (Photoshop).&lt;/p&gt;

&lt;p&gt;Almost every app has only a single level undo, with only Expression and Freehand offering multi-level undo. This is probably why everybody was using Freehand when I started work in graphic design in the mid-late 1990s.&lt;/p&gt;

&lt;p&gt;One small disappointment: Macromedia Fireworks requires the PPC architecture so won’t run on my iPad System 7 setup. Sad face. (But we can use &lt;a href=&quot;https://infinitemac.org&quot;&gt;infinitemac.org&lt;/a&gt;)&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table id=&quot;macintosh-drawing-software&quot;&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;APP&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;PIXEL&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;VECTOR&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;LAYERS&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;TRANSP.&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;UNDO&lt;/th&gt;
        &lt;th&gt;NOTES&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;artWORKS&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Many layers, many objects&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;BluePaint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;PPC-only&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Canvas&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Many layers, many objects&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;ClarisDraw&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Pixel drawing within specific objects&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Cricket Draw&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Draw It Again Sam&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Drawing Table&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Expression&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td&gt;Colour required&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Freehand 1.0–2.0&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td&gt;Layers not managed visually&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Freehand 3.1–5.5&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Illustrator 88–3.0&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;ImageStudio 0.6&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Custom brushes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;LightningPaint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Polite palettes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;MacDraw Pro&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;MacPaint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;MacroPaint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Transparent patterns&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Photoshop 1.0–2.5&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Photoshop 3.0&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Full alpha channel transparency&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;PixelPaint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Special brush effects&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;ShareDraw&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Studio/1&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Animation, 1-bit&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Studio/8&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Animation, 8-bit&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;SuperPaint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Two layers, many objects&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;UltraPaint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Eight layers, many objects&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Zeus&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td&gt;Animation&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;
&lt;p&gt;Key: ● full support, ○ partial support&lt;/p&gt;

&lt;h3 id=&quot;related-posts&quot;&gt;Related posts&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh&quot;&gt;Turning an iPad Pro into the Ultimate Classic Macintosh&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/21/building-basiliskii-for-ios/&quot;&gt;How to install BasiliskII on your iPad&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/19/automating-interactions-using-apple-events/&quot;&gt;Exploring Custom Keyboards and Automation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/03/28/changing-the-size-of-toolbar-items-using-resedit/&quot;&gt;Optimising Macintosh app toolbars for touch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Macintosh drawing software compared&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/25/mixing-external-tools-across-deneba-software/&quot;&gt;Mixing External Tools across Deneba software&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/30/my-system-7-software-choices/&quot;&gt;My System 7 software choices&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/05/03/interoperability-of-system-7-and-ios/&quot;&gt;About the interoperability of System 7 and iOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 24 Apr 2021 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/04/24/macintosh-drawing-software-compared/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/04/24/macintosh-drawing-software-compared/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: Automation Improvements</title>
          <description>&lt;p&gt;The recent automation was really just help with organisation. As soon as I started looking at running OpenSCAD from the shell/command-line it became obvious that I could do the rendering and organisation in one step without having to use external apps like Hazel.&lt;/p&gt;

&lt;p&gt;So, that’s now done. I render all the frames, with more sensible filenames, to a single folder.&lt;/p&gt;

&lt;p&gt;If I run all the renders one after the other, maxing out a single CPU core (99% CPU usage), time taken:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;~17 seconds 🐢&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But, using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; directive and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait&lt;/code&gt; command, I can run the renders in parallel (well, technically it’s one process each; and batches of 32 works best) using all 6 CPU cores (~485% CPU usage), time taken:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;~10 seconds 🐇&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;…the beauty of the command line!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note to self: don’t publish a post about an automation breakthrough until the dust has settled.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 09 Oct 2020 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2020/10/09/automation-improvements/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2020/10/09/automation-improvements/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: Automation</title>
          <description>&lt;p&gt;In preparation for regenerating my many cars with x3 the number of sprites, I thought I’d try to sort the rendered frames automatically into named folders because this is fiddly manual work I really don’t enjoy, and a bit of a bottleneck in my asset generation. For each pose I have to render the frames then group the new image files into a folder that describes that pose, as these multiple folders are later be used for batch processing.&lt;/p&gt;

&lt;p&gt;I could use macOS Folder Actions for this, but I’ve been using an app called &lt;a href=&quot;https://www.noodlesoft.com/&quot;&gt;Hazel&lt;/a&gt; for many years to do this sort of thing, so that was my first choice.&lt;/p&gt;

&lt;p&gt;The hard work is done with a shell script, as I’m quite comfortable writing those.&lt;/p&gt;

&lt;h2 id=&quot;flow&quot;&gt;Flow&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;get most recently saved .scad file&lt;/li&gt;
  &lt;li&gt;parse filename to capture car name&lt;/li&gt;
  &lt;li&gt;parse file contents for left/right/forward/backward tilt values&lt;/li&gt;
  &lt;li&gt;concatenate all this information as our new folder name&lt;/li&gt;
  &lt;li&gt;create new folder&lt;/li&gt;
  &lt;li&gt;move matching file into new folder&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means that the folder name will change based on the render settings in the file—perfect!&lt;/p&gt;

&lt;p&gt;I still have to make 9 small sets of manual text changes to render each car pose, so that’s the next thing I’ll try to automate by running OpenSCAD from the command line.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-automation.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 07 Oct 2020 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2020/10/07/automation/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2020/10/07/automation/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: Playable Demo</title>
          <description>&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-playable-demo.gif#playdate&quot; alt=&quot;GIF&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A made a demo build of Daily Driver. It is based on code a couple weeks old (dated &lt;strong&gt;30 July 2020&lt;/strong&gt;) so whilst not representative of where I am right now with the game it does show my progress since the last build I shared at the start of June.&lt;/p&gt;

&lt;p&gt;Controls&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;any combination of d-pad, A/B, Crank&lt;/li&gt;
  &lt;li&gt;d-pad U/D, or A/B = accelerate/brake&lt;/li&gt;
  &lt;li&gt;d-pad L/R, or Crank = steer left/right&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notes&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;runs at 60fps on device (50fps in Simulator)&lt;/li&gt;
  &lt;li&gt;pressing Menu twice will reset the current stage layout&lt;/li&gt;
  &lt;li&gt;pressing Menu then Options will load the next stage&lt;/li&gt;
  &lt;li&gt;19 different stages (all cone layouts in this build)&lt;/li&gt;
  &lt;li&gt;includes level editor!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debug Keys for game (simulator only):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;E = open level editor&lt;/li&gt;
  &lt;li&gt;F = toggle fps indicator&lt;/li&gt;
  &lt;li&gt;J = jump!&lt;/li&gt;
  &lt;li&gt;L = list all sprites&lt;/li&gt;
  &lt;li&gt;N = next stage&lt;/li&gt;
  &lt;li&gt;P = play replay data&lt;/li&gt;
  &lt;li&gt;R = print replay data to console&lt;/li&gt;
  &lt;li&gt;T = toggle telemetry&lt;/li&gt;
  &lt;li&gt;U = toggle frame limiter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Controls for editor (simulator only):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;d-pad = move cursor&lt;/li&gt;
  &lt;li&gt;A = place an object or increment object under the cursor&lt;/li&gt;
  &lt;li&gt;B = toggle precision mode
    &lt;ul&gt;
      &lt;li&gt;cursor moves in smaller increments (5px vs 20px)&lt;/li&gt;
      &lt;li&gt;drag/move item under cursor&lt;/li&gt;
      &lt;li&gt;A = delete item under cursor&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debug Keys for editor (simulator only):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;B = new blank stage&lt;/li&gt;
  &lt;li&gt;D = dump stage JSON to console (for copy and pasting)&lt;/li&gt;
  &lt;li&gt;E = exit level editor&lt;/li&gt;
  &lt;li&gt;O = output stage as “m-edited.json” in game Sandbox directory&lt;/li&gt;
  &lt;li&gt;Z = undo (deletes last item placed)&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 15 Aug 2020 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2020/08/15/playable-demo/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2020/08/15/playable-demo/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: Post-processing workflow</title>
          <description>&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-post-processing.gif#playdate&quot; alt=&quot;GIF&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My post-processing to 1-bit is fairly simple. I use a bespoke tool that allows me to have “live” (realtime) manual control over a bunch of image filters so I can see the results immediately.&lt;/p&gt;

&lt;p&gt;But essentially it’s:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;convert to greyscale, using one of many algorithms&lt;/li&gt;
  &lt;li&gt;reduce colours to 1-bit, decide on dithering or threshold level on case-by-case basis&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The grey shades that I applied to my model in OpenSCAD give an element of control during this conversion process. Greys can be pushed either way, towards black or white, depending on my need with the specific model I am working on.&lt;/p&gt;

&lt;p&gt;In this instance I desaturated the greys which blows them out to nearer white. And then I chose to threshold to reduce to b/w.&lt;/p&gt;

&lt;p&gt;I also have the wheels as a separate finished image so I don’t have to worry if their detail is lost during this phase, I can just paste over the accurate/finished wheels.&lt;/p&gt;

&lt;p&gt;Final result, unedited:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-post-processing.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I would later touch up the sprite by hand to reinforce any details I think have been lost. I use Piskel for edited sprites because it has really nice sprite sheet support, drag and drop loading, and quick and versatile exporting.&lt;/p&gt;

&lt;p&gt;Aside: 32 is a number that is a leftover from a different prototype and it’s stuck. I guess it should really be 36? or 24? or 18?. But it’s too late now! Actually it would be relatively easy to change but I have bigger fish to fry.&lt;/p&gt;

&lt;p&gt;The animations alone could run at 99fps—it’s anything that causes more drawing which slow things down. Collisions, not because they are computationally heavy, but because they cause a lot of sprite updates - which means drawing moving things - to happen. I’m working hard to maintain 60fps on device (50fps in simulator for… reasons) and am excited I’ve managed to get here.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 10 Aug 2020 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2020/08/10/post-processing-workflow/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2020/08/10/post-processing-workflow/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: Retrobatch workflow</title>
          <description>
&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-1.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-1.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-3.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-4.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-retrobatch-workflow-4.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 82%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;h2 id=&quot;retrobatch&quot;&gt;Retrobatch&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;process dumped frames x 32
    &lt;ul&gt;
      &lt;li&gt;once each for left, right and straight directions&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;stitch processed frames together as sprite-sheets x 3
    &lt;ul&gt;
      &lt;li&gt;we end up with three long images&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;stitch 3x sprite-sheets together
    &lt;ul&gt;
      &lt;li&gt;we end up with our final image ready for post-processing&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Edit, August 2020: since writing this post I’ve been able to condense this process into 1 single workflow that executes much faster. See image 4. Thanks to Gus, maker of Retrobatch!&lt;/p&gt;

&lt;p&gt;Edit, June 2021: I replaced my all uses of Retrobatch with imagemagick commands.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 09 Aug 2020 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2020/08/09/retrobatch-workflow/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2020/08/09/retrobatch-workflow/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: OpenSCAD workflow</title>
          <description>
&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;e&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-1.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-1.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-3.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-4.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-4.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-5.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-openscad-workflow-5.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 82%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}

.carousel__activator:nth-of-type(5):checked ~ .carousel__track {
  -webkit-transform: translateX(-400%);
          transform: translateX(-400%);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__slide:nth-of-type(5) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__controls:nth-of-type(5) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(5) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(5) {
  -webkit-transform: translateX(400%) translateZ(0);
          transform: translateX(400%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(5),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(5) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;p&gt;I took the plunge and upgraded to a Mac mini and 4K display so had to migrate my setup and then figure out why my workflow was broken (spoiler: retina!) compared to my old rMBP with non-retina display.&lt;/p&gt;

&lt;p&gt;So, my workflow uses the following apps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://openscad.org&quot;&gt;OpenSCAD&lt;/a&gt; “The Programmers Solid 3D CAD Modeller”&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://flyingmeat.com/retrobatch/&quot;&gt;Retrobatch&lt;/a&gt; “a unique application for automating actions on multiple images at the same time”&lt;/li&gt;
  &lt;li&gt;post-processing “greyscale and dithering tool” (I use my own realtime tool, but any image editor would do it to a degree, see this other thread)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is so I can re-run a workflow at any point (maybe in a make file) which I often do during development. These become executable assets, of sorts, in my project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenSCAD&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;define 3D models (I get a feeling like coding CSS in a strange way)&lt;/li&gt;
  &lt;li&gt;animate the model spinning through one 360-degree rotation&lt;/li&gt;
  &lt;li&gt;dump frames out as PNG files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the model definition to try out:&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/a0a0b967c480ab973d40aaf5e78fd47f&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/a0a0b967c480ab973d40aaf5e78fd47f.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;I love building 3D this way, it’s kind of like LEGO. I use basic geometric building blocks (cube, sphere, cylinder, polygon, etc) and some boolean operations (difference, intersection, union). There are some other cool things (hull, minkowski). I have the commands in &lt;a href=&quot;https://dash.app/&quot;&gt;Dash.app&lt;/a&gt;alongside the Playdate SDK docs.&lt;/p&gt;

&lt;p&gt;In the image 2 you can see what the model looks like with all the blocks I have used to make it visible at once.&lt;/p&gt;

&lt;p&gt;My particular approach is &lt;em&gt;subtractive&lt;/em&gt;— kind of like sculpting—I start with large blocks and cut away at them using other shapes and the &lt;em&gt;difference&lt;/em&gt; function. When finding the exact placement for a block I use the &lt;em&gt;#&lt;/em&gt; precedent which makes the blocks show up as semi-opaque red blocks. See main image.&lt;/p&gt;

&lt;p&gt;I colour each block in a one or two shades of grey, black and white. This is to help with the conversion to 1-bit later on. It’s not so obvious here as the lighting makes the colours look many different shades - for example the wheels are black with white centre but look grey here. See image 3.&lt;/p&gt;

&lt;p&gt;Using some simple programming constructs and variables I can add booleans to trigger different states, I use this for angled front wheels and tilted car body. See image 4.&lt;/p&gt;

&lt;p&gt;And also to set the distance and rotation of the camera relative to the model when in animation mode. In this mode I enter a speed (doesn’t matter but higher the better) number of frames = 32, and the tick the box to dump the images. The tick disappears when the images are all done. See image 5.&lt;/p&gt;

&lt;p&gt;I also rendered the skid marks, car shadow, and some other elements.&lt;/p&gt;

&lt;p&gt;There is a lot that is annoying about this app&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;not retina-optimised (so I run it in Low Resolution, set using Get Info on the app)&lt;/li&gt;
  &lt;li&gt;runs maxed out on a single core&lt;/li&gt;
  &lt;li&gt;doesn’t have configurable lighting (I’d prefer uniform or no lighting)&lt;/li&gt;
  &lt;li&gt;Qt Framework app, so not really macOS-native&lt;/li&gt;
  &lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…but I still use it! I am not aware of anything else quite like it.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 08 Aug 2020 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2020/08/08/openscad-workflow/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2020/08/08/openscad-workflow/</guid>
        </item>
      
    
      
        <item>
          <title>Services for Mac OS X</title>
          <description>&lt;p&gt;I created some Services for Mac OS X to do useful things on selected text.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Calculate &amp;amp; Append&lt;/li&gt;
  &lt;li&gt;Calculate &amp;amp; Replace&lt;/li&gt;
  &lt;li&gt;Calculate &amp;amp; Speak&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Edit, January 2026: over the following years I would create many more, most of which are in daily use.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The Services menu in Mac OS X (from 10.6 onwards) lets you use features of one application while working in another. The Services menu is contextual, so it shows just the services appropriate for the application you’re using or content you’re viewing rather than all available services. You can access services with a right click of your mouse or Control-click of your trackpad. You can configure the menu to show only the services you want, and you can even create your own services using Automator.” —Apple&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;screenshot&quot;&gt;Screenshot&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/services/calculate-services.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/services/&quot;&gt;www.gingerbeardman.com/services/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 28 Aug 2009 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2009/08/28/services-for-mac-os-x/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2009/08/28/services-for-mac-os-x/</guid>
        </item>
      
    
      
        <item>
          <title>PNGenie image optimiser</title>
          <description>&lt;p&gt;PNGenie is lossless PNG optimiser for Mac OS X.&lt;/p&gt;

&lt;p&gt;A PNG file contains image related data. This data can be stored in a multitude of different ways and the file size will vary for each storage method—even with the same raw image data. Image files often have an attached resource fork that also artificially increases the size of the file.&lt;/p&gt;

&lt;p&gt;PNGenie optimises any given PNG file—trying out different methods of compression—with a goal of achieving the smallest file size. Smaller files are good for a number of number practical reasons as well as, obviously, saving disk space. Let’s say you want to reduce the size of data files belonging to an iPhone application, or you want to speed up loading of a website that uses PNG images, for example. Resource forks, gamma values and software-specific tags are automatically stripped, too.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/pngenie/processing.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/pngenie/&quot;&gt;www.gingerbeardman.com/pngenie/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 03 Jul 2009 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2009/07/03/pngenie-image-optimiser/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2009/07/03/pngenie-image-optimiser/</guid>
        </item>
      
    
      
        <item>
          <title>Today screensaver</title>
          <description>&lt;p&gt;&lt;em&gt;Today&lt;/em&gt; is a Mac OS X screensaver inspired by &lt;a href=&quot;https://en.wikipedia.org/wiki/On_Kawara#Today_series&quot;&gt;On Kawara’s &lt;em&gt;Today Series&lt;/em&gt;&lt;/a&gt;—nearly 3,000 date paintings made over five decades, each a monochrome background with the current date inscribed on it in the language and calendrical conventions of wherever Kawara happened to be that day.&lt;/p&gt;

&lt;p&gt;The screensaver displays today’s date in the same spirit. It can show the date, time, or day of the week, on a background colour of your choosing—or a random one.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/today/today.png&quot; alt=&quot;Today screensaver&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/today/&quot;&gt;gingerbeardman.com/today/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 09 Apr 2009 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2009/04/09/today-screensaver/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2009/04/09/today-screensaver/</guid>
        </item>
      
    
      
        <item>
          <title>Radio Paradise on Chumby</title>
          <description>&lt;p&gt;I created the Official Radio Paradise widget for Chumby, the internet connected hacky sack! You can listen along, see the most recently played songs, and the current playing song with album artwork.&lt;/p&gt;

&lt;p&gt;Fun fact: this was my one and only contract involving Flash Lite.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;a-really-low-quality-screenshot-of-the-chumby-web-management-interface&quot;&gt;A Really Low Quality Screenshot of the Chumby Web Management Interface&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/radio-paradise-chumby.webp&quot; alt=&quot;WEBP&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.theregister.com/Print/2009/12/12/review_gadget_chumby/&quot;&gt;www.theregister.com/Print/2009/12/12/review_gadget_chumby/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 01 Jul 2008 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2008/07/01/radio-paradise-on-chumby/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2008/07/01/radio-paradise-on-chumby/</guid>
        </item>
      
    
      
        <item>
          <title>(iPhoto) Exif Cleaner</title>
          <description>&lt;p&gt;Digital cameras attach information to each photo you take. This information - called EXIF data - contains the time-stamp, aperture and shutter time amongst other things.&lt;/p&gt;

&lt;p&gt;However some brands of cameras also attach a lot of extraneous data. This is inconvenient for a number of reasons, but most of all the fact that iPhoto caches all this data in it’s Library file resulting in slower operation after each new import of photos. Affected camera brands include: Casio, Konica Minolta, Kyocera, Nikon and Pentax - amongst others.&lt;/p&gt;

&lt;h2 id=&quot;purpose&quot;&gt;Purpose&lt;/h2&gt;

&lt;p&gt;This application will seamlessly clean your photos of extraneous exif data prior to importing into iPhoto. This utility does not delete all exif data, just the extraneous MakerNote section and any blank sections. All remaining, standard exif data is left intact.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/exifcleaner/screenshot.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.gingerbeardman.com/exifcleaner/&quot;&gt;www.gingerbeardman.com/exifcleaner/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20061029083354/http://www.versiontracker.com/dyn/moreinfo/macosx/28734&quot;&gt;Version Tracker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 31 Dec 2005 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2005/12/31/exif-cleaner/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2005/12/31/exif-cleaner/</guid>
        </item>
      
    
      
        <item>
          <title>Upcoming badge</title>
          <description>&lt;p&gt;Upcoming Badge is a widget for Mac OS X Dashboard.&lt;/p&gt;

&lt;p&gt;Use it to view your Upcoming.org events watch list using Dashboard.&lt;/p&gt;

&lt;p&gt;Simply enter your username on the flip side of the widget.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/upcoming/upcoming.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/upcoming/&quot;&gt;www.gingerbeardman.com/upcoming/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 22 Nov 2005 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2005/11/22/upcoming-badge/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2005/11/22/upcoming-badge/</guid>
        </item>
      
    
      
        <item>
          <title>Ceefax Viewer</title>
          <description>&lt;p&gt;Ceefax Viewer is a widget for Mac OS X Dashboard.&lt;/p&gt;

&lt;p&gt;Ceefax (phonetic for “See Facts”) is the BBC’s teletext information service. Teletext is an information retrieval service provided by television broadcast companies. Teletext pages can be viewed on television sets with suitable decoders. They offer a range of text-based information, usually including national, international and sporting news, weather and TV schedules. Subtitle (or closed caption) information is also transmitted in the teletext signal.&lt;/p&gt;

&lt;p&gt;This widget will let you view Ceefax at any time using Dashboard. You do not need a TV but you will need Mac OS 10.4.3 or better and an internet connection.&lt;/p&gt;

&lt;p&gt;Click on the page numbers to navigate around the service. Use keyboard shortcuts for additional quick navigation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/ceefaxviewer/ceefaxviewer.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/ceefaxviewer/&quot;&gt;www.gingerbeardman.com/ceefaxviewer/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 22 May 2005 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2005/05/22/ceefax-viewer/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2005/05/22/ceefax-viewer/</guid>
        </item>
      
    

  </channel>
</rss>
