Skip to the text
waywardcelt.com · writing · talking-to-a-haunted-crystal-ball · proof no. 5 · October 17, 2026 · author: J. O’Donnell · editor: also J. O’Donnell
The Wayward Celt
Galley · entry · October 16, 2026

Talking with bits and bytes to a haunted crystal ball

Filed under The Workbench · 6 minute read

Crystal Ball Remote is a small iPhone app I wrote for myself to put Madame Leota in a crystal ball, because the official iPhone app was stuck in App Store review and I wasn’t going to wait. It took one August afternoon and evening.

A crystal ball, a phone, and one evening. Madame Leota as the ball draws her, beside the app's tabs.

Halloween is two weeks out, so let’s talk about the one build on my workbench that belongs on a haunted mantelpiece.

It started in the Halloween aisle at Lowe’s, with a holographic crystal ball: a fan of LEDs spinning fast enough that your eye sees a floating, glowing image in the glass. Most people would see a decoration. I saw Madame Leota.

If you’ve ridden the Haunted Mansion, you’ve met her: the disembodied head in the séance room, floating inside a crystal ball, calling the spirits to materialize. A crystal ball that plays video was an invitation I couldn’t refuse. I bought it for exactly one purpose, to put her in it.

Then came the snag. Loading your own video takes the company’s proprietary app. The iPhone version was stuck in Apple’s App Store review, and had been for weeks. There was an Android version. My phone is an iPhone, and Halloween does not wait for App Review.

Neither, it turns out, do I.

So I built my own.

Editor: Starting with ‘So’?

Author: And why not.

01 · The intent

The goal was narrow and a little stubborn: an independent iPhone app that speaks the same language the ball already speaks, so it connects to the same hardware in the same way. Interoperability with a thing I own, nothing more. That set three rules before I started:

  • Interface facts only. Addresses, ports, and the text commands the ball understands. No vendor code or artwork in the app, and no vendor name in its interface.
  • Everything the original app could do, and then some. Browse and play the built-in clips, manage what’s on the ball’s memory card, and drive the controls (power, brightness, rotation, speed). Then the whole point: send it my own video.
  • Testable without the ball on the desk. If I could only test against the real hardware, I’d spend the evening walking back and forth to the shelf.

02 · The process

The first Swift file is stamped 4:56p on a Thursday afternoon in late August. The installable package was built a little after nine that night.

Step one was reading the Android app closely enough to learn how it talks to the ball, and writing down only the interface: what the phone connects to, and what it says. The protocol turned out to be charmingly simple. The ball runs its own Wi-Fi network, and the phone joins it. Commands go over a plain socket as short text tokens. There’s no length framing at all, so the app recognizes replies by what they contain.

How the phone talks to the ball: join its Wi-Fi, send plain-text commands, upload media in framed batches.

My favourite discovery was a function in the original app named for a hex-encoding scheme it never actually uses. Its body just sends plain text. Every codebase has one of those: a name that made sense once, outlived its reason, and now lies quietly to the next person who reads it. If you’ve ever maintained a knowledge base, you’ve met its cousin, the article whose title no longer matches its contents.

Editor: Serial comma: present.

Author: Always.

editor: Is this really a knowledge-management story?

author: Everything is… and if it isn’t, it’s a distilling one. Stet.

Editor: That’s one.

Before touching the real ball, I built a tiny Python stand-in that pretends to be one, so the app could connect, list files, and send commands end to end on my desk. It still missed one thing. The ball’s file list starts with a marker that ends in two digits, and a naive parser happily glued that onto the first file’s number. The first real connection caught it. Mocks are wonderful; hardware is the truth.

Editor: Semicolon. Bold.

Author: Earned.

Then came the part I’d bought the ball for: Madame Leota herself. The fan doesn’t have pixels. It has two arms of LEDs sweeping a circle, so every frame of her video has to be resampled from a square grid into rings and angles, cut down to six bits per colour, and packed bit-plane by bit-plane into exactly 126,000 bytes. This is where my use of Claude Code really shined. No way I would be doing THAT by hand.

Editor: 126,000: source?

Author: Linked. Mostly.

Madame Leota, as the ball sees her: a square frame, shrunk to 160 by 160, unwrapped into 80 radii and 600 angles, and swept by two arms of light.

That’s her on the right, run through the same steps the app uses: a crystal-ball head inside a crystal ball, which I like to think the Imagineers would appreciate. By the end of the evening the app had Upload, Video, and Text tabs feeding the pipeline, with a diagnostics log of every command it sent.

And once a pipeline works, scope creep becomes a moral obligation. Little Leota, the tiny bride who bids riders farewell at the end of the Mansion, went on next. Then Princess Leia’s message to Obi-Wan, the most famous hologram in the movies. Followed by other frivolities. No reason at all, except the holographic fun of it.

The guest list: Madame Leota, Little Leota, and Princess Leia, each as the ball draws her.

03 · The outcome

The control side is done and verified against the real ball: connect over its Wi-Fi, browse and play the built-in clips, play, pause, skip and loop, power, brightness, rotation alignment, mute, speed, list and delete what’s on its card, and even change the ball’s Wi-Fi password. More to the point, it does the one thing I bought it for. Madame Leota floats in the glass, with Little Leota and Princess Leia waiting their turn.

Note the video looks a bit off due to the misaligned frame rate capturing between the camera and the LEDs. It is a bit more seamless in person, and even better in lower light.
The ledger: about six hours, about 2,400 lines of Swift, 7 tabs, 126,000 bytes per frame, 1 mock device, 0 vendor code.

The app isn’t on the App Store. It’s packaged as an unsigned app for sideloading. I scrubbed so it carries no developer identity, signing certificate, or saved passwords (checked with a raw byte scan before it could ever leave my machine). Anyone with the same ball and a bit of technical know-how could install it with their own Apple ID, but there’s never been a distribution plan.

The lesson is a small one, and I like it better for that. Not every build needs a roadmap, a market, or a launch. Some of the best ones scratch a single itch in a single evening, teach you something about bit-planes you didn’t know you wanted to know, and end with Madame Leota floating in a glass ball while the company’s official app was still waiting its turn in review.

What’s the gadget in your house that almost works the way you want?
You might be one evening away from a personal success.


Transparency note: drafted with Claude from the Crystal Ball Remote project, its protocol notes, and its sideloading guide, then read, cut, edited heavily, rewritten, and finally approved by me. As always: AI drafts, humans ship.

Read by a human
OK to ship
October 16, 2026 · J. O’D.

AI drafts, humans ship. This entry has been through the human.

§ · More filed under The Workbench

2entries
Send me the corrected proofs

New entries by email. Join other subscribers to stay in the know!

Thirty minutes, no agenda required.
Book a call
© 2026 The Wayward Celt · set in Cormorant Garamond, Spectral, Barlow, and IBM Plex Mono · serial commas throughout