Categories
Apple Gadgets

Juicy Crumb DockLite G4

Last year, I purchased a DockLite G4 board from Juicy Crumb Systems. It’s a drop-in solution that repurposes the iconic iMac G4 into an HDMI display.

Installation

There are more affordable ways to achieve the same goal if you’ve got the time, but no one else makes it this simple.

In the box I got:

  • the main board
  • an installation manual
  • an HDMI cable
  • a minijack stereo cable
  • a USB-C cable
  • a sticker
  • a legit Australian Tim Tam

Overall, the instructions make sense – pull out the old PPC logic board, pop in the DockLite G4 board, connect power and the display, and you’re kinda done.

The board also features a USB hub, a speaker amplifier, and brightness-adjustment buttons. They all line up nicely with the back of the chassis, even if it’s not airtight, Apple-level industrial design.

Software control

If you’re running a Mac you can install Juicy-Link and connect to the USB hub to set the display brightness and upgrade firmware. It’s simple to install and seems to work well (as long as it’s a Mac).

Things to consider

Limitations

Their software is currently only available on macOS although their website claims Windows support is coming soon. Software control is better than nothing, but still not as nice as DDC/CI. If you find yourself on something other than macOS you’ll need to reach around the back and press the buttons to adjust the brightness.

More disappointing is that the display doesn’t support sleep mode, so turning off the backlight requires pressing the physical power button in the back. This makes the challenge of preserving an old backlight even harder.

The good news is that I think much of this can be fixed with software and firmware updates, so I’d love to see that happen.

It’s still an old LCD

Modern macOS looks great on this design (round all the borders!), but your eyes won’t be fooled coming from HiDPI Retina displays. LCDs have come a long way with higher resolutions and greater pixel densities, uniform and bright LED backlights, wider viewing angles, and higher refresh rates to name a few. This display has none of that. It’s not a suitable daily driver for my eyes, but it works great as a toy.

The drives can go

Juicy Crumb recommends that you leave the CD-ROM and HDD installed so the base retains a proper weight for stability. Interestingly their instructions don’t have you unplug the power from these drives even though they aren’t in use.

I understand keeping things simple, but the type of user who is disassembling an iMac G4 can surely unplug a couple cables to reduce unnecessary power consumption and strain on an old PSU.

I removed both from my 17″ system and I have no concerns about its stability. I recommend doing the same. Plus, you’ll get better airflow with the system fan (although I question if that’s really necessary, too).

Old PSU is old

Although Apple is known for high quality hardware, installing this board doesn’t really lower the risk that the 20+ year old power supply will fail and burn something up. It’d be nice to have an alternative DC input like a barrel pin connector.

The original fan is audible

Do yourself a favor and replace the factory fan with something modern and quiet. Without a hard drive and CD-ROM spinning away, the fan becomes more noticeable. The replacement should be a standard size, though you may want to match the color—anything non-white might be distracting.

Final thoughts

I’m really happy with the purchase and have had a blast playing around with different systems. If you’ve got a compatible iMac G4 and want to do more than Mac OS 9, this is a great solution. Just promise that if you order one and don’t want your Tim Tam biscuit, you’ll send it my way.

Categories
News

It’s time to retire Herp Derp

Thirteen years ago 🤯 I made a silly little browser plugin that got some Internet attention. It was featured on websites and a few big podcasts, and even served as inspiration for a WordPress plugin written by Jamie Zawinski. 😎 I’d occasionally see it pop up as a suggestion by Redditors and even learned of use cases I hadn’t anticipated – like preventing movie spoilers or making the site more suitable for classroom environments.

Reviews on the plugin page were overwhelmingly positive (many of them hilarious – one was even framed in my home) and I was delighted to see that the Internet could take a joke. On a serious note I also want to make it very clear that the term “herp derp” here meant “saying something meaningless”. I later learned there can be different interpretations of the term that don’t carry the same spirit – to be whimsical, silly, and most of all harmless. (this is why we can’t have nice things)

On the technical side the biggest surprise was that Google’s DOM structure stayed extremely stable throughout the whole run and only needed one update. I’m amazed it ran as long as it did with so little effort from my side.

Now that we’re in 2025 I think YouTube has improved comment moderation. Features like “Top comments” seem to help, and in this era of LLMs the accuracy of “quality scoring” should increase as well.

So with that, it’s time to say farewell to Herp Derp for YouTube! I mean, herp derp. 👋

Categories
Arduino Projects

FlipDotMatrix Library

In the past I’ve blogged about my Alfa-Zeta flip dots (one, two) and how I got them working with the Arduino platform. I’ve taken some time to turn the work into a consumable library that should make it much easier for others who want a turn-key way to drive the display.

The library is available in the Arduino IDE as well as the PlatformIO registry and comes with examples to get started. When added to a project it’ll grab all its dependencies so you can get going as quickly as possible.

Most of the heavy lifting is done by the excellent Adafruit GFX Library and it’s been updated to support 7×7 and 14×7 panels in addition to 28×7. Flip on! 🔴

Categories
DOS Fun Projects Retro Computing Video

Merry DOS-mas!

Now I can control my LED Christmas Tree from DOS! 🎄💾 I hope to share more details on the code + dev stack, but basically I used:

GitHub commit.

Categories
AI SwiftUI

ChatGPT SwiftUI Christmas Tree 2024

Over the past couple of years (2022, 2023) I’ve compared the output of the following prompt to ChatGPT:

Draw a Christmas tree using SwiftUI

For 2024 I chose the GPT-4o model, and this is what it output:

import SwiftUI

struct ChristmasTreeView: View {
    var body: some View {
        ZStack {
            // Background color
            Color.blue
                .edgesIgnoringSafeArea(.all)
            
            // Tree
            VStack(spacing: -20) {
                Triangle()
                    .fill(Color.green)
                    .frame(width: 150, height: 100)
                Triangle()
                    .fill(Color.green)
                    .frame(width: 180, height: 120)
                Triangle()
                    .fill(Color.green)
                    .frame(width: 210, height: 140)
                
                // Tree trunk
                Rectangle()
                    .fill(Color.brown)
                    .frame(width: 40, height: 60)
            }
            .offset(y: 50)
            
            // Star on top
            StarShape()
                .fill(Color.yellow)
                .frame(width: 40, height: 40)
                .offset(y: -170)
            
            // Decorations (Ornaments)
            Circle()
                .fill(Color.red)
                .frame(width: 15, height: 15)
                .offset(x: -40, y: -60)
            Circle()
                .fill(Color.yellow)
                .frame(width: 15, height: 15)
                .offset(x: 50, y: -30)
            Circle()
                .fill(Color.white)
                .frame(width: 15, height: 15)
                .offset(x: -30, y: 20)
            Circle()
                .fill(Color.blue)
                .frame(width: 15, height: 15)
                .offset(x: 40, y: 60)
        }
    }
}

// Triangle shape for the tree
struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        Path { path in
            path.move(to: CGPoint(x: rect.midX, y: rect.minY))
            path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
            path.closeSubpath()
        }
    }
}

// Star shape for the top of the tree
struct StarShape: Shape {
    func path(in rect: CGRect) -> Path {
        let points = [
            CGPoint(x: 0.5, y: 0),
            CGPoint(x: 0.61, y: 0.35),
            CGPoint(x: 1, y: 0.35),
            CGPoint(x: 0.68, y: 0.57),
            CGPoint(x: 0.79, y: 0.91),
            CGPoint(x: 0.5, y: 0.7),
            CGPoint(x: 0.21, y: 0.91),
            CGPoint(x: 0.32, y: 0.57),
            CGPoint(x: 0, y: 0.35),
            CGPoint(x: 0.39, y: 0.35)
        ].map { CGPoint(x: $0.x * rect.width, y: $0.y * rect.height) }
        
        return Path { path in
            path.move(to: points[0])
            for point in points.dropFirst() {
                path.addLine(to: point)
            }
            path.closeSubpath()
        }
    }
}

struct ChristmasTreeView_Previews: PreviewProvider {
    static var previews: some View {
        ChristmasTreeView()
    }
}

Which generated:

This is the best one yet! I didn’t have to add or change any code to make it work, it added ornaments and even a (floating 😀) star! 🎄

Categories
Linux Tutorials

LVM to the rescue

Root problem

I’ve been running Debian 12 for a while on my Linux desktop and recently ran into a big issue: My root volume ran out of space. Never good.

In the past I would’ve taken the path of least discomfort (for me): Clean install, repartition differently, start fresh. Because I’d used LVM when installing, however, I had another option this time:

On small systems (like a desktop), instead of having to estimate at installation time how big a partition might need to be, LVM allows filesystems to be easily resized as needed.

Wikipedia

Goal

I want to expand the root volume by shrinking the home volume a little bit. Let’s see how easy it really is with LVM!

Prerequisites

My environment was an x64 box running Debian 12 containing a single 1TB SSD. I didn’t add any physical drives, I only adjusted the space allotted to the LVM volumes.

Live image

I used a live install image to boot Linux without installing anything. After downloading the image I used dd to copy it to a USB flash drive. When booting on Debian, you’ll need to select the “Rescue mode” and follow the menus to get to a shell. When asked about using a root filesystem, tell it not to mount anything.

Figure out paths and numbers

To make this tutorial generic, we’re going to define four variables. Two for the volume paths, one for the amount we’re going to grow the smaller volume, and one for the amount we’re going to shrink the bigger volume.

Paths

There are plenty of ways to figure out the paths to the volumes you’ll be using, but lvdisplay works well.

shrinkPath = path of volume to want to shrink
growPath = path of volume to want to grow

Numbers

We’ll also need to define the growth amount of the growing volume and the new size of the shrinking volume.

growthAmount = how much we want the volume at growPath to grow
newShrinkSize = current size of the volume at shrinkPathgrowthAmount

Example

We have a root volume we want to grow by 30GB as well as a home volume (currently 900GB) that we need to shrink.

shrinkPath = /dev/debian-vg/home
growPath = /dev/debian-vg/root
growthAmount = 30G
newShrinkSize = 870G (which is 900-30)

Note: Gigabyte units are assumed in this example, hence the G after the 30 and 870.

Steps

⚠️ Complete these at your own risk! Any time you change filesystems you run the risk of data loss, so backup your stuff!

  1. Boot into a recovery mode without mounting any filesystems.
  2. e2fsck -f {shrinkPath} to check for errors.
    • Example: e2fsck -f /dev/debian-vg/home
  3. resize2fs {shrinkPath} {newShrinkSize} to shrink the filesystem.
    • Example: resize2fs /dev/debian-vg/home 870G
    • Important: Be sure to include the appropriate size unit after the new shrink size, like G for gigabytes. See the resize2fs man page.
  4. lvreduce -L -{growthAmount} {shrinkPath} to shrink the LVM volume.
    • Example: lvreduce -L -30G /dev/debian-vg/home
    • Note: Some tutorials accomplish steps 4 and 5 in one go via lvreduce --resizefs but this did not work for me, so I had to break it out into two steps.
    • Important: Be sure to include the appropriate size unit after the growth amount, like G for gigabytes. See the lvreduce man page.
  5. resize2fs {shrinkPath} to extend the partition to fit the volume.
    • Example: resize2fs /dev/debian-vg/home
  6. vgdisplay -C should show {growthAmount} free.
  7. e2fsck -f {growPath} to check for errors.
    • Example: e2fsck -f /dev/debian-vg/root
  8. lvextend -l +100%FREE {growPath} to grow the volume to 100% of the available Volume Group size.
    • Example: lvextend -l +100%FREE /dev/debian-vg/root
  9. resize2fs {growPath} to grow the filesystem to the available space.
    • Example: resize2fs /dev/debian-vg/root
  10. vgdisplay -C should show no free space
  11. You should be all done and free to reboot!
Categories
Thoughts

Turbo Typer

You do not need an alternate keyboard layout like Colemak or Dvorak to type fast. Nor do you need to learn to use the QWERTY “home row keys.” All it takes is a misspent youth and muscle memory.

My typing teacher in high school wasn’t my biggest fan. I won the class typing competitions without using the “proper” techniques she taught. I could tell it pained her to give me the prizes.

Now if I could only think as fast as I can type…

Give it a go!

Categories
Coding Projects WASM

Converting an old ray tracer to Wasm

Back around the time the first iPhone was released (feeling old! 😬) I was taking a course where we built a CPU-based ray tracer in C.

If you’re not familiar with the ray tracing technique, check it out here.

What I mostly remember from the course was math, pointers, and segfaults. Oh the segfaults. By the end of the course, however, I had a decent grasp on C and that’s been a valuable skill so many times in my career and hobbies.

How the original project functioned

  1. You wrote an input file that describes various shapes, light sources, and attributes of the environment.
  2. You fed the file into the C program.
  3. You waited a bit (remember, we’re doing everything on the CPU in ~2007).
  4. You got a fancy PPM file. (PPM was a great image format due to its simplicity – we were dealing with enough!)

Wasm motivation

Recently I decided that I wanted to learn more about the inner workings of WebAssembly (Wasm) and figured this would be a great candidate project. It’s fully contained without any external dependencies, *I wrote all the code so it shouldn’t be too mysterious, and if I got it to work there would be a visual payoff.

*Feel free to judge some of the rough spots in the code – it was a long time ago!

Process

The first thing I made sure of was that I could compile the project locally the non-Wasm way. There were no hiccups there – it worked on the first try using the Makefile. ✅

I then started reading this tutorial on converting a C project to Wasm. After installing emscripten on macOS (I used Homebrew) I decided to add a new C source file to the project and added a function that looked something like:

#include "emscripten.h"

EMSCRIPTEN_KEEPALIVE
int version() {
  return 42;
}

If I could get this to work I could at least get information from C -> JS for starters. All that it took to make this work was:

  1. Substituting gcc with emcc in the Makefile
  2. Making sure I added the EXPORTED_RUNTIME_METHODS='["cwrap"]' compiler flag
  3. Calling Module.cwrap from JS to use the function

That was pretty much it. I’m not going to go super in-depth with this blog post because I think most of it can be figured out from the source.

Next challenges

I had a bit more to go but was surprised at how easy it was to send a value from C to JS. The next items to figure out were:

  1. The ray tracer expected an input file and an output file, how would this work with a browser?
  2. We can pass integers easily, but what about the big array of pixel data when we ultimately generate an image?
  3. Where would our C calls to fprintf and its siblings go when trying to debug to stdout and stderr?
  4. What about main – does it run?

I’ll go ahead and spoil these really quickly in their respective order:

  1. fmemopen saved the day by taking the input string (which is a char *) and providing a FILE type in return which is an in-memory buffer stream. In other words, no massive overhauling needed although we aren’t using “real” files anymore. In addition a slight refactoring was done to the project to return an array of pixels rather than write out an image file.
  2. From what I understand, Wasm and C can share a heap and both just need to know where to find the data via pointers. Here’s an example of sending a pointer to C, and here’s an example of how JS grabs a pointer from C. In the latter, C sets a global (gross, I know, but they used it in their examples as well) and JS calls a function to get that int. It then is able to initialize an array of UInt8s.
  3. They automagically show up in the browser console! This is a really nice feature, and stderr calls are even properly displayed as errors.
  4. Yes! In my case I got rid of it because it was prompting for the CLI input, but it was interesting to see that it automatically ran. There may be a compiler setting to disable this.

A summary of what it took to convert the ray tracer to Wasm

  1. Tweaked the Makefile to use emcc.
  2. Removed main() because I didn’t need it.
  3. Used fmemopen to substitute a real file with a char array.
  4. Refactored the project to not try to write to a file, but instead return a big array of pixels that ultimately get passed to JS to write to a Canvas.
  5. Expanded the pixel struct to include an alpha channel for the expected RGBA format. Yay for properly using sizeof throughout the code.
  6. Wrote a C source file with everything we needed to interface with JS.
  7. Created an HTML page that calls our compiled JS and gives us access to the exposed functions.
  8. Created a big string using a JS template literal for our input.

That’s mostly it! Check out the GitHub repo here. 🚀

Categories
Fun Retro Computing

Pinball Arcade Trial Achievement

I really hate to brag, but I wanted to share this life achievement. The Pinball Arcade Trial running on Windows 98 informed me that I got the highest score possible for the trial version.

Categories
Nature

Geomagnetic storm

Some front yard shots from the geomagnetic storm on May 10th, 2024. I never thought we’d see one of these in South Carolina!

The sky wasn’t as vibrant to the naked eye due to all the ambient light in my neighborhood, but the iPhone 15 Pro’s sensor picked it up great with a three second exposure.