What's it like to get sucked into a black hole and travel at hypersonic speeds through a wormhole? Alan Watts built this Arduino-based psychedelic spacetime visualizer to find out.

[Video Link]

Since the dawn of man, we have looked to the stars in awe and wonderment. Gazing into the heavens has inspired age-old questions like "Why am I here?", "Are we alone?", and "What's it like to get sucked into a blackhole and travel at hypersonic speeds through a wormhole?" Well, with an Arduino, 120 LEDs, an infinity mirror, and some old-school NASA-inspired hardware, CLAW Amusement Technologies is at least pretending to have an answer to that nagging last one, if it were posed to a team of NASA engineers in 1960, with the Wormhole Actualization Machine (WAM).

The Infinity Mirror

Initial sketch of the wormhole core

The first step in building the Wormhole Actualization Machine was designing the infinity mirror assembly and sourcing the materials. After reading lots of Instructables and watching lots of YouTube videos of previous attempts at building an infinity mirror, I knew optic and reflective quality were the most important factors in getting a convincing tunneling effect. TappPlastics.com sells custom-cut sizes of both two-way mirrors (the front one you can partially see through) and regular mirrors (the back one). They were rather expensive (30 and 15 dollars), but worth the better quality achieved than by applying reflective window film to plexiglass, the most common DIY method. Since I'll be drilling holes in them, I bought them both in 1/8" acrylic. I wanted the ability to control the direction which the wormhole "fell" away – if the 2 mirrors are exactly parallel, the vanishing point will be in the center of the wormhole. Offset the angle slightly, and you'll get a nice 3d tunnel with the lights slowly bending into infinity. After doing some sketching, I came up with a design that incorporated 8 threaded rods and bolts that would allow the planes of the front and back mirrors to be precisely adjusted to the desired effect.

Both mirrors needed support to keep them true to flat (another important factor since every imperfection gets multiplied), so I needed to find something rigid to enclose the assembly. McMaster-Carr sells flanged angle rings made of 1/8" steel in different diameters that have a clean, simple industrial look. Since these would determine the size of my wormhole portal, I decided to go with two 12-inch rings.

Front and side technical rendering of the core

Now that I knew the size of the wormhole opening, I could design the tube that will hold the LEDs. I'm using strings of common WS2801-controlled RGB LEDs that I bought on AliExpress. I calculated the optimal grid pattern with the LEDs equally spaced to create the most seamless reflection and a more convincing tube of light. I settled on an 8-inch deep cylinder with 4 rows of 24 LEDs and another ring of 24 LEDs popping through the back mirror for a total of 120 lights. My string is 150 LEDs, so I'll snip 30 off – another great feature of the WS2801.

2 of the 3 rings installed

My initial thoughts involved using a concrete tube form or an acrylic drum shell for the 8-inch cylinder, but both were too thick for the flange on the LEDs. I did lots of wandering around in many different types of stores (and surely looked suspicious checking out at so many random things), but I couldn't find a suitable pre-formed round cylinder (the little plastic trashcan at Toys R Us was promising), so I bought an acrylic 12 inch x 6 foot wire shelf liner that was the perfect thickness and easily bent to form a circle. The acrylic needed to be an exact size, so I constructed a fairly elaborate clamping system to cut it. I used a long level to guide my jigsaw shoe along a straight line and then clamped the acrylic on either side, as close to the blade as possible to prevent it from shattering due to the rapid oscillation of the jigsaw.

The assembled wormhole core

The bolted core

Once cut with a fine-tooth metal-cutting blade, I built 3 sets of wooden ring clamps to form it in a round shape. All of the components would be held together with threaded rods, so I started by cutting my "master" guide holes in one of the steel rings. Making sure I kept everything in proper orientation, I clamped the three wood rings to the steel ring and drilled their holes. I then drilled each of my 2 pieces of acrylic mirror with a special drill bit for plastic, which I found out about after destroying a few pieces of acrylic. These bits have a less aggressive cutting edge and don't get jammed up in plastic.

Before drilling the holes for the LEDs, I printed out a template with my exact placements and marked the drill spots on the acrylic. Drilling all 120 holes was a slow and careful process. Afterwards, I sanded the acrylic to get rid of the gloss and then spray-painted the back black.

Once all the wormhole components were complete, I slipped them all onto the threaded rods in their proper order and orientation and bolted everything together.

First run – a success!

At this point, I still didn't know if the infinity mirror effect is even going to work, so I popped in all the LEDs and was excited and nervous to find out. My mom commented on its picture that it looked like a crockpot, so I was hoping it wasn't destined for a similar fate in a dark cabinet under the sink. The LED string hooks up easily to an Arduino via SPI and an external 5v supply, so I was ready to find out pretty quickly. Using the FastLED library, I ran through some basic animation functions I had already developed for a previous project and made 2 rows of red and blue lights spin around. Success! It looked as good as I had hoped and I felt my face already melting into the wormhole.

I/O Components

Now that I had a functional wormhole assembly, I needed to start building the physical interface to it. For inspiration, I browsed image archives of various NASA mission control rooms from the 50's and 60's and other space-related imagery of that era. Almost every console had clusters of rectangular buttons, a dial or two, some analog meters, and lots of toggle switches. I went to SparkFun and AdaFruit to peruse their selections of components and started mocking up the layout of potential parts in Adobe Illustrator. After designing a satisfactory panel configuration, I stocked up on potentiometers, rotary and toggle switches, colored buttons, indicator lights, a few 7-segment numeric displays, volt meters, and other parts, not yet really knowing what everything would actually do. And what potentially universe-altering machine is complete without one of those cold-war "missile launch" switch protection covers? SparkFun sells a great one complete with a red LED, so I knew the WAM would at least have a safety-first on/off switch.

I still needed to find rectangular buttons. Digikey, Mouser, and most of the other large component distributors carry some, but they must know NASA uses them because they start at $15 each. The current mockup of my control panel had 32 buttons, so I needed to find them cheaper. After much searching on AliExpress, I found a supplier that had both momentary and latching small rectangular buttons with configurable LED voltages for less than $2/piece so I ordered 20 of each. At the end of my button shopping binge, I had shipments from around the world heading my way and a crazy amount of soldering to do.

Panel Design and Fabrication

panel-layoutPanel layout

At this point, I had an almost complete panel design drawn in Adobe Illustrator including all the mount holes for my incoming buttons and switches. Originally, the panels were going to be rear-painted Lexan as my previous Arduino project had been, but I started to think that plastic would end up being too cheesy – especially compared to all the fancy buttons. Hand-cutting and airbrushing with this amount of detail would take forever. I looked into getting aluminum panels made but was originally turned off by the price quotes I was getting from places like Front Panel Express. Austin's TechShop has a waterjet, but there wasn't enough time for me to get certified and up to speed with it.

Panel cut with waterjet cutter

I sought the advice of my metal sculptor friend and he connected me with San Marcos Waterjet – they were out of a garage in the Hill Country south of Austin. I exported a DXF of just the cut lines and they said it would be $100, so only one day later I had my two panels. Considering I would have spent a week filing square corners in plastic, wrangling tiny vinyl stencils with an X-Acto knife, and cursing a clogged airbrush, the $100 was happily given to my waterjet saviors.

Beautifully printed panels

The main panel was too large for my friend's screen-printing shop, so he forwarded me on to another local shop that specialized in printing metal signs. This ended up being the most expensive step of building the WAM, but the end result of the buffing, printing, and then clear-coating of the aluminum panels was totally worth it.

Designing The Circuitry

Temporary panel

While my aluminum panels were getting finished, I created a temporary control panel from 1/8" plywood so I could get everything mounted and then delve into my cram session in circuitry. Not counting SPI pins for the wormhole LEDs, the I2C for the three 7-segment displays, and serial TX/RX for the sound module, I needed 54 additional I/O pins for all the switches, buttons, and knobs. Having only completed one other Arduino project, I was fairly new to electronics and had never heard of shift registers, pull-down resistors, sourcing versus sinking current, or decoupling capacitors.

The input shift registers with pull-down resistors

After an online crash-course in shift registers and many late-night Digikey orders and cancellations, I ended up with five 74HC165 chips to handle 40 of the buttons (each chip is 8-bit which translates to 8 buttons). I set the 40 buttons that control the main functions of the wormhole to the shift registers, and, for simplicity's sake, everything else was assigned to its own I/O pin on the Arduino Mega 2560, which has plenty. I also had all the individual LEDs in the buttons to control, so after more crash-coursing, I found the TPIC6B595N – another type of shift register that can control all my lights with just four Arduino pins. This shift register is special in that it is connected to a 5 volt source and is designed sink current. It's needed here because of the high amperage all the LEDs could potentially draw from the small number of Arduino pins. They are 8-bit as well, so I used four of them daisy-chained together.

Wiring the circuits

All of the shift-in registers for the buttons were connected to one breadboard and another was used for the LED shift-out registers and their other respective components. It took me a few nights of zen-like dedication to solder the wires to all my buttons and switches, but I soon had everything finally connected together after a careful weaving of over 200 wires.

Once everything was connected to the Arduino, I needed both 5 volts (for the wormhole LEDs, 7-segment displays, button LEDs, and circuits) and 12 volts (for the Arduino and the two panel indicator LEDs), so I used a JAMMA-compliant arcade power supply.

Software Development

Finally seeing the tangle of wires attached to the Arduino started to worry me. I still had no idea if it would be able to handle all the input and outputs while processing all the LED animation effects I was anticipating having. I figured the best thing to do was to code the basic functionality I needed, check the performance, and then slowly add in the "fluff". I began with the simple spinning LEDs from my previous test as my starting point.

Since WAM will do different things based on which of the dozens of buttons the user pushes, I designed the main program loop as a finite state machine – this allows me to have different "modes" that will need a different set of code executions. For instance, if the power is off, the LED light blinks and the entire system is in an idle mode and awaiting to be switched on. Once switched on, it enters its "testing" state and will go through a startup procedure and then idle again until the user pushes the "engage" button. A properly architected FSM allows new states to be easily added and provide an extendable base on which to build different functionality.

LED buttons

For the 40 buttons assigned to the five shift registers, I create five 8-bit byte variables – each bit would be 0 or 1 depending on its up or down state. In the main program loop, the shift register is continuously polled at a high rate and, using bit-shifting, compared to the previous state stored in the variable. If there's a change, I know the button has been pushed (for the momentary buttons) or its state changed (for the latching ones). I created another class for the button LEDs that lets me toggle the state of a specific button on or off when it's pushed. I connect the pushing and the lighting classes together, so when a button is pushed, the corresponding LED of that button turns on.

I wanted lots of the components to behave autonomously: buttons that would blink at a certain rate for a number of seconds, needle sweeps that would animate on the analog meter, and auto-incrementing values on the 7-segment displays. I created a multi-purpose time-based tween class that functions as a basic value generator whose output can be sent to its parent class. I assign it a start value, an end value, the current time in milliseconds and the total duration to run. When I need to find out a tween, I send it at which milliseconds, and it will return the time-derived value. I can also set it to loop a certain number of times or to run forever. I can use it on my 7-segment displays to animate from 0 to 100 over 5 seconds, for example, or on an LED to make it blink for 10 seconds at 2 blinks per second. I don't need these effects to necessarily run at the fastest speed possible — especially since button response and the wormhole LEDs were the performance priority — so I added a frequency property which restricted updates to only happen at certain millisecond intervals that I could tweak on the fly.

As I coded the various functions and classes for all my buttons, knobs, switches, and display components, I tracked performance and noted how different settings affected overall performance. I was pretty happy that everything played nicely together at this point. Since everything was based on time, I could tweak each component's update frequency on demand and throttle its cpu usage.

Once I had code for all my buttons, switches and display components, I wanted to explore different visual effects for the wormhole, so I started by hard-coding sample animations. Eventually I would create more interactive and generative routines, but I wanted to try different effects to see what looked good. Colored, spinning lights were the obvious winning visual effect, but I was also able to simulate a falling-into-the-wormhole effect by alternating all the lights in rows 1 and 4 with the lights in rows 2 and 3. Due to the mirrored reflection of each row on itself (1-2-3-4 | 4-3-2-1 | 1-2-3-4 | etc.), I couldn't consecutively toggle each row for a convincing effect. So even though I am only animating two steps, combined with the depth perception created by the infinity mirror, it appeared as if the lights were in motion. All the blinkiness was starting to get a little seizure-inducing, so I wanted to add decay and fade effects to the LEDs to give them some life. I've written animation software before so I was aware how computationally expensive these functions can be, but I wanted to see how much I could squeeze out of Arduino's limited horsepower.

Prototype software block diagram sketch showing effects and compositing technique

With that in mind, I started to finalize the most optimal architecture (for both performance and flexibility) of the animation routines. I configured each of the five 24-LED rings to be independently animated, but could also synchronize its effects to any other. The LEDRing class contains the pattern and color of the 24 LEDs it is assigned to. On top of the pattern sits a spin effector. By setting the "step" value of the spin, it will offset the pattern by that amount. Combined with my tweener class, I can instruct an LED to animate all the way around in half of a second. On top of that effector is a fade effector which has its own tweener so I can oscillate the brightness of the LED at a different rate as it spins around. Each ring of LEDs is assigned two of these classes, allowing me to create two separate layers of patterns and spin them in opposite directions for a ping-pong effect, for example. My LEDRender class handles the animation updating of all five rings and composites the colors from the two layers of a ring. I ended up with a total of 12 independent layers assigned to 3 different sets of LED rings.

Part of the render process is handling how to toggle the currently-on LEDs. Instead of abruptly switching off, each LED has a dynamic "decay" property which allows it to slowly fade away once turned off. For example, a decay setting of 0.5 on a LED will decrease its brightness by 50% each time the frame update is called. By faking this persistence of vision, this gives a spinning LED a much smoother effect with a tail of light that fades behind it.

After I coded all the different animation functions, I needed to assign all the inputs to the various parameters and settings of my animation routines. I defined the four outer rings as "coil 1" on the control panel and combined the one inner ring and the step effect of the outer rings as "coil 2" and made up scientific-sounding function names that they'd be labelled as. I used my laser printer and origami paper to make the button labels. After my earlier tests with cellophane failed (I should have known that lasers melt plastic), I discovered the paper was perfect – packs come in dozens of different colors, have good color density, and are white on the back to diffuse light.

I still have more controls though. When the RPM knob is turned, the value is used to set how fast the LEDs step to the next light. Normally, each LED is stepped one "pixel" at a time when animating the spin effect. In this configuration, the fastest I could animate a 360-degree loop would be slightly less than one second (24 LEDs around at 30 fps). Since I couldn't force any more frames per second, I modified the animation routine to advance the LED by 2 or 3 steps depending on the RPM value. The lights in between the step get "filled in" and are faded, so it looks like it's just spinning faster as the steps get larger.

The R, G, and B toggle switches are linked up to my LEDRender class and skips adding those color values in the final output to the LEDs.

The 3-way "phase" rotary switches shift the RGB values of the LEDs right or left, so red values get assigned to green, green to blue, and blue to red. Turning it again shifts the colors the other way.

My main program loop runs (or at least tries to) at 30 frames per second. The latest millisecond value is read at the beginning of the loop and stored into a variable that is then sent to all the classes that have tweens to update.
Surprisingly, everything ended up running quite smoothly, but something was missing… my hallucinations were all visual. It needed sound!

Sound

I'd had some previous experience using Arduino's tone() command for fairly basic audio output and I was able to squeeze out some pretty interesting sounds with rapid frequency sweeps and pulses to create good low-fi sci-fi effects, but WAM needed more.

While doing research into different Arduino sound generation techniques using R2R resistor ladders and DACs, I came across Mozzi. It sounded promising, but unfortunately it requires the bulk of the processing power of the Arduino. Realizing that any sound generation is going to be fairly CPU intensive, I started looking into standalone chips that could be controlled via the Arduino and generate sounds independently. I discoveredBabbleBot and the GinSing shield that incorporates it. The BabbleBot chip features 6 oscillators that gives it the ability to create polyphonic sound effects and generate WOPR-like speech. This added a whole new dimension of sci-fi nostalgia to WAM. Everyone loves computers that talk! GinSing uses a serial communication API to control sound synthesis and it was only now that I noticed overall performance issues when issuing commands. It wasn't horrible, but confusing that it was this finally slowing it down. I haven't had time to deeply investigate this yet, but I'm curious if it's something in Arduino's serial transmission or a slowdown in the GinSing API due to buffering or something. It was only a few select commands causing the hiccup, so I continued by extending the GinSing class and adding a tweener to the sound controls. Now I can create additional effects by assigning its output to the frequency of an oscillator or any number of other sound settings. After much tweaking, I settle upon a pretty good subdued frequency-shifted WOM-WOM-WOM sound. Intergalactic auditory pleasure ensues.

The Enclosure and Cart

Sketchup design of enclosure and cart.

I wanted a simple enclosure to mount the panels to and hold all the electronics. With SketchUp, I determined the best angle to mount the two panels and then designed the enclosure profile based off that. From there, I virtually constructed the rest of the enclosure, down to the location of each bolt that held everything together. I printed out templates for the shape of the side panels and location of drill holes, transferred them by carbon-copy to 3/4" MDF, and cut the pieces with a jigsaw.

By using block joints, the entire enclosure can be disassembled if ever needed to take it all apart.

After everything was bolted together and the panels were installed, it was ready to get its wheels.

I found some industrial casters at my local big box home improvement store and built a stand using 3/4" galvanized pipe. Due to thread differences, I drilled and tapped galvanized caps to attach the casters to the bottom of each pipe leg.

Final Assembly

Everything was finally built but I was still using my temporary wood control panel with all my buttons, so I had to disconnect all my wiring and install everything into the aluminum panels. I took a deep breath and pulled handfuls of wires out of breadboards. Visions of WAM never working again and me trying to find the one wire I misplaced flashed into my head. After reconnecting the spaghetti of wires with cautious optimism and a steady hand, I mounted the wormhole core by inserting the threaded rods into the eight holes in the main panel and securing it with acorn nuts. I double-checked all the wire connections – especially voltages and grounds. I haven't zapped anything yet and I surely didn't want something to explode the first time I turn this thing on.

I turned on the power supply. Nothing exploded, but I hear a faint hum coming out of the speaker. "Needs a decoupling capacitor", I think, but it's too late, I am ready for my first trip. I wheel it around and the slowly blinking power switch beckons me. I don't resist. I flip it on, hear the familiar WOM-WOM-WOM-WOM-WOM, and begin my maiden voyage.

See you in hyperspace.

The Wormhole Actualization Machine is currently on display at Northern-Southern in Austin.