The Beacon is lit

Saturday, February 7, 2026

Comments: 2   (latest 7 days later)

Tagged: zarf, art, beacon, leds, pixelblaze

Here's the back wall of my home office -- a.k.a. my videochat background. It used to be boring. Now it's colorful!

An LED strip runs along the top of a wall, above some wire-rack shelves of CD-ROMs and assorted junk. The strip glows in irregular segments of deep blue and bright cyan. The colors are bright enough to blow out the image somewhat; they shine up the wall and reflect off the ceiling. Pattern: slowflies. The lights are bright enough to saturate my phone camera; that's why the blue looks weirdly dark.

That's a programmable LED strip running a pattern that I designed. In a pattern language that I came up with. The source code for that pattern is here: slowflies.pbb.

As you can see, it's a declarative syntax which describes waves. Roughly, this code says "Generate some sine-wave pulses that move slowly back and forth; add them; draw them in green ($0F0) with a blue ($02C) fade-down."

Here's another one, in pinks and oranges:

An LED strip runs along the top of a wall. The strip glows in irregular segments of yellow, orange, and magenta. Pattern: fireblobs. I turned off the room lights to bring out the colors.

And some video:

Pattern: wanderedges.

It's nicely flexible. That green-fireflies video is a sum-of-waves like all the others, but with a gradient that only shows the midpoint of the summed curves. This gives a sort of cheapass edge-detection over time, which is visually interesting.


This project has taken several detours. I originally wanted to run an LED strip around the bay window of my second-floor apartment. Thus the "Beacon" -- it would shine out over my neighborhood. Or at least amuse the neighbors.

I may yet do that, but weather-proofing and outdoor wiring is a lot. I figured it would be easier to start with an indoor installation. The neighbors would miss out, but it would amuse my compatriots at morning stand-up.

The pattern language is a syntax that I came up with a couple of years ago. My first implementation was in Rust. (I wanted some Rust practice.) That was intended to run on a Raspberry Pi. And it did! I bought a Pi and an LED strip. I managed to get the software installed, and it ran great.

However, connecting the Pi to the LED strip was, well, a soldering project. I've soldered wires, but my skill level is "blob it on and hope it works" and I just didn't feel comfortable anywhere near circuitboard pins. So that got shelved for a while.


But a few months ago, someone mentioned the word "Pixelblaze". The Pixelblaze is a user-friendly microcontroller for LED strips. It has a built-in web interface. You can get it with the connector already soldered on! Plug in, fire up a web page on your local network, select a pattern, and you're off. There's a pattern editor, too. Exactly what I wanted.

A small black case with a four-wire connector plugged into it. The connector is plugged into a different connector at the other end. One wire has a tag with "+5V" scribbled on it. A Pixelblaze controller plugged into its connectors. It's illuminated in green because where do you think the light is coming from?

Only one problem: it doesn't run my pattern language. It uses a Javascript-like language. Nothing wrong with that -- but I had this extremely cool (I thought) declarative syntax all worked out, and I wanted to use it.

Sadly, while the Pixelblaze hardware (and its software) are intended for tinkerers, they are not open-source. So I couldn't just replace their language with mine.

Instead, I had to write a translator tool. Behold: pbbeacon!

(Yes, the "Pixelblaze Beacon". I wasn't feeling groovy for names.)

pbbeacon is a translator which reads one of my pattern files (a .pbb file) and writes out a Pixelblaze language file. You can see examples of both in the scripts directory. The slowflies.pbb script up top gets translated into slowflies.pat. You shove that onto the Pixelblaze and it runs.

(The top of the .pat file is the original .pbb source, included as a comment. Provenance is all.)


More to be done

This project is, as they say, very much in progress.

  • The pbbeacon language is missing a few features compared to the Rust original. In particular, I need to implement the noise operator. The PB language has a noise() function; I just need to get it hooked up right. [EDIT: Done as of Feb 13th.]

  • My system has a global clock. It needs to fire waves at known intervals, so time-tracking is critical. But the underlying Pixelblaze language uses fixed-point 16.16 math for everything. (Really 15.16, because it's signed numbers.) That means that my clock will overflow after 32767 seconds -- nine hours. Probably the whole pattern will freeze up, or go blank, or something. So it's no good for all-day operation. I should write some code to time-shift down after eight hours.

  • You're supposed to be able to control the Pixelblaze by sending messages over a websocket connection. I have not yet gotten this to work. (I could have it flash red when someone messages me on Slack! Or when someone files a bug on one of my Github repos! Wait, no, that's a terrible idea.) [EDIT: Done as of Feb 13th.]

  • I did a fair amount of code optimization, but my patterns are inherently slow. Adding up several sine waves is just a lot of math. The Pixelblaze chugs a bit under the load. Some of my patterns run at 30 fps, which is slower than I prefer. If I try to get any more complex, it would get unacceptably blinky. Or if I got a longer strip, or a denser one (more dots-per-inch).

Pattern gallery

Here's all the patterns I've come up with so far. I'm sure I'll add some more as time goes on. Fiddling with pattern code is fun!

(I've uploaded most of these to the Pixelblaze pattern repository as well.)

These are animations rendered by the Pixelblaze editor. (Or, really, 2D images scrolling through a narrow rectangle. Slit-scan trippiness!)

Techy details

I bought a Pixelblaze V3 Standard, the "pre-soldered" version, along with the slim case. Actually I bought two, one pre-soldered and one not.

A small circuitboard in a plastic case. What it looks like with the case open. This is the non-pre-soldered Pixelblaze.

Then (or rather, two years earlier) I got an Adafruit DotStar LED strip, 4 meters at 60 LEDs per meter. Also a chunky 5V power supply.

You can power the Pixelblaze through a USB-mini interface, but it can't draw much power that way. So there's a limit to how many LEDs you can run off USB. I followed the recommended setup, which is to power the LED strip directly from the power supply. Then the Pixelblaze doesn't need its own power connection; it just runs off the power coming from the LED strip. It's clever.

I ran into one hitch. If you look at the Pixelblaze store page, you'll see it comes with a tiny little ribbon cable that connects the controller to the LED strip. (Also visible in the green closeup above.) The DotStar strip I bought has a four-pin connector on each end (they can daisy-chain) so it all just worked.

...eeexcept it didn't work. Upon careful inspection, the PB ribbon cable wanted to connect to the wrong end of the DotStar strip. (They daisy-chain, but one end is "input" and the other is "output".)

But I was smart this time. I asked my partner, who has been soldering LEDs for decades. She threw together a 4-pin gender-changer from parts lying around, and then it all just worked.

Yes, I will get her to deal with the Raspberry Pi as well. My original plan still awaits. The Pi will run much faster -- which means fancier patterns on longer strips. The Beacon will rise! Forth EorlinGaAs!


Comments from Andrew Plotkin


Comments from Mastodon