Light Painter

Long exposure photography with Arduino and LEDs

This is a write up of how I built my light painter, going through the software, the materials, the design of the custom parts and putting everything together. Below are links to all the different sections, so you can skip to the bits you’re really interested in or you can continue for a more or less linear process.

  1. Introduction and goals
  2. Hardware
  3. Arduino sketch
  4. Enclosure and assembly
  5. Conclusions
  6. Links and references


1. Introduction

The main concept behind light painting (wikipedia article for more detailed info) is using a light source that is moved in front of a camera during a long exposure shot. The light source can be as simple as a hand held torch or a LED, but with a microcontroller, a LED strip and precise timing, we can create complex images and scenes.

Besides a LED strip and a micro controller, we need a portable power source and an easy way to load images. We will have to program our microcontroller to load an image, convert it into information that we can send to our LED strip and then drive the succession of lines to create a full image.

This has been done before, many, many times; and even commercially. But I wanted to build my own, both to learn in the process and because commercial options were out of my budget. I chose adafruit’s implementation as a starting point, because it worked on the hardware I already had and they have provided detailed instructions. I also wanted some additional features: I wanted to add a display and a menu system to be able to choose the image to display and adjust the settings (like brightness or speed). I also wanted to be able to turn off the brightness balancing that adafruit’s sketch did, because frame or animation painting wasn’t something I really needed. And most importantly, I adjusted the project to the materials I had at hand.

2. Hardware

This is an outline of the materials I used (note: the following are affiliate links; they would help me out at no cost to you, but I understand if you’re not into the idea):

  • Arduino UNO.
  • LED strip, any flavor of RGB addressable will work; I used a WS2812 strip like this one.
  • 8 AA batteries. I use eneloop rechargeable ones.
  • 8xAA battery holder.
  • Alternatively, you could use a LiPo battery, but then you would need protection and charge circuitry.
  • UBEC or buck converter to get a stable 5V from our power source
  • Micro SD card reader module. I used these.
  • 1602 LCD + I2c LCD module, like this one.
  • 24awg cable for Arduino and logic conecctions
  • 20awg cable for UBEC to LED strip
  • Momentary push buttons
  • Slide switch

This is a pretty simple schematic of how everything goes together.

The battery pack feeds into the UBEC with a switch that’s our master power on/off. The UBEC feeds 5V to the LED strip, the Arduino and all the other components. The mini-SD reader connects to pins 10 through 13, and the LCD-I2C adapter connects to SDA and SCL. Apart from that, the buttons are wired to A1, A2 and A3, and the digital in for the LED strip is connected to pin 6.

3. Arduino sketch

This sketch is based on the the one written by Phil Burgess/Paint Your Dragon for Adafruit, that can be found here. A complete write-up with instructions and hardware design can be found on Adafruit’s website (where there’s also more in-depth info on some of the components and design choices).

What I did is add an LCD to the sketch, compartmentalize the functions so that the loop was mostly waiting and acting upon user input and implement a (probably quite crude) menu system. The output and menu system was constrained by the limitations of a 16×2 display, which was also fun to get around.

I wanted the sketch to be able to present a menu with the available files in the SD, but reading all the files and populating an array with the names would’ve meant using a prohibitive amount of memory. The workaround is to scan the files that we’re interested in and store their file index (a much smaller memory footprint). Then we pull up the names on the fly. But even with this we are limited to 30ish files.

The images we use are 24 bit bitmaps, because their file format is pretty straightforward to decode, unlike more CPU intensive formats like png or jpeg (we’re talking at the 8-bit microcontroller level here).

The Arduino isn’t powerful enough to decode the files on the go, so on init or brightness recalculation, the files are scanned and converted to a raw format that later can be dumped directly to the lcd strip without any conversion. All the file loading, conversion and dumping was already taken care of in Phil’s sketch, so all credit to him.

Memory was the main limitation throughout, and I constantly kept getting stack collisions while I was developing the sketch. I peeled away variables and code (like the whole Serial interface) until I got it to run smoothly. I’m not great at C, so I’m sure that there are multiple optimizations that could give a bit more breathing room or things that could be more efficient in their use of variables (and memory). If you spot something I’d love to hear your thoughts.

The menu is quite simple. We can select the image we want to trigger, the brightness (which triggers a file rescan), the speed at which the image is displayed and the delay before the display starts.

Additionally, we have the option to save the settings to EEPROM, so that our current settings are restored the next time we power on.

I’ve also changed some routines to make boot quicker by prompting the user if a complete rescan should take place and rationalizing the generation of the raw files. Though Phil had ingeniously managed to get 3 different settings with just a potentiometer and push button, all that is now handled by the menu.

Please check out the code on Github (Light-painter), and feel free to let me know if there’s anything that can be improved.

4. Enclosure and assembly

This is the part that will depend the most on the materials you have easier access to or the ones you feel more comfortable working with. I tried to keep things simple, so I used a beam of wood to support the LED strip and I decided to design a custom enclosure that I would 3D print.

I used FreeCAD for the design. There was some amount of swearing and starting over, but in the end I managed to get something pretty close to what I had imagined.

I made custom adapters for the battery holder as well, that leave spaces for cable routing. Admittedly, this could’ve been done differently, but this was the simplest option

You can download the STL files from Thingiverse: Light painter. However, after using the light painter, I have found a few issues with my designs. I might tackle these in future revisions:

  • The screw tabs are not strong enough. They would need a thicker material or to be printed on the XY plane to have more tensile strength.
  • The pins that hold the Arduino and the SD module in place are way too thin and long to be practical. They break. Easily. This could be fixed with thicker pins that narrow out at the point of contact.
  • There is barely enough space inside the enclosure for all the cables. Make sure your cables are flexible. If I had to design the box again, I’d make it more roomy and leave enough space for the UBEC.
  • I would add some kind of tab or latch to keep the bottom and top halves together.  The two point fixture of the top half to the beam is not enough to close down (specially due to the previous point).
  • The button system I designed is horrible. It’s impossible to keep it in place. Once installed it works, but this would be something to definitely improve.

That said, everything fits in snugly and closes down with a few coercive drops of superglue. The main enclosure goes onto the beam, as well as the battery pack. Everything is connected as in the schematic.

The UBEC lives on the outside. Not as elegant, but it probably gets much more adequate cooling there and it’s not like it takes up a lot of space.

The placing of the battery pack and the main enclosure could be optimized. I just placed it based on the beam where it looked right and made sense from a cable routing point of view, but I didn’t actually test to get the best balance. The light painter feels ok to handle; a bit top heavy but not unwieldy.

5. Conclusions

Overall this was a fun project. Most of the effort went into writing and testing the script while most of the components were plugged into a breadboard. Designing the enclosure and battery holder blocks was probably a bit overkill, specially since they were my first time ever in FreeCAD, which sometimes just doesn’t like your geometry but it doesn’t tell you why, so you have to keep iterating different ways to create the same geometries until it suddenly works.

But it was great to design from the ground up, integrating software and hardware until reaching a functional device.

I’d love to hear your thoughts: drop a comment if you have any questions or if you would’ve done something differently and why.

6. Links



A non-blocking Arduino library for controlling 28BYJ-48 stepper motors.


28BYJ-48’s are little inexpensive 5v geared stepper motors that are particularly pesky in terms of what’s needed to drive them. They are halfstep steppers and require an 8 step control signal to drive them efficiently. They usually come with (equally inexpensive) ULN2003 driver boards that make connecting them to the Arduino much easier.

I had no success driving them with the standard stepper libraries (Stepper or AccelStepper). There was always resistance and stutter, resulting in increased heat and noise. I finally came across two libraries that worked: Tyler Henry’s CheapStepper, which I couldn’t get to drive two steppers simultaneously without issues; and Matthew Jones’s Unistep, which drove two or more steppers perfectly but in a blocking fashion.

So I decided to create a non-blocking version based on Unistep and added some extra functionality, but I wanted to recognize the contributions made by others.


  • Non-blocking
  • Can manage 2 or more steppers
  • Extra functions to control state of steppers
  • More energy efficient
  • Precise timing avoids noise, heat, stutter, stalling, etc.


Download the zip in Releases on github and use the library manager to add it to your libraries. Alternatively you can install manually into your Arduino library folder.


Just call

#include <Unistep2.h>

at the begining of your sketch and construct the stepper objects with the following call in your setup() function:

Unistep2 stepper(p1, p2, p3, p4, stepsPerRev, stepDelay);

where p1 to p4 are the pins you’ve connected your ULN2003 to, stepsPerRev are the steps that your stepper takes to do one revolution (4096-ish in the 28BYJ-48’s) and stepDelay is the delay between each step in microseconds (900 is the fastest that seems to work without issues).
Then you must call

in your loop. Delaying functions will interfere with the stepper movement.

Function descriptions

  • move(int steps): Moves the stepper the indicated amount of steps. If steps is negative movement will be counter-clockwise.
  • moveTo(unsigned int pos): Moves the stepper to an absolute position between 0 and stepsPerRev.
  • currentPosition(): Returns the current absolute position of the stepper.
  • stepsToGo(): Returns the amount of steps remaining for the stepper to complete the assigned movement. Negative will mean counter-clockwise.
  • stop(): Stops the current movement and powers down the pins to save energy and avoid heat generation. Is called automatically after each movement, but is available to be called by the user.


  • Create examples
  • Add metadata and library info
  • Repackage as plugin zip

Tip jar

If you find this useful in any way, feel free to leave a tip in my bitcoin address if you feel so inclined:



This library is released into the public domain.



Pannel is a compact and minimal writeboard to track projects featuring a very lightweight implementation of markdown and in-place AJAX editing. Obviously, heavily inspired by 37signalsWriteboard.

I wouldn’t consider pannel a finished and polished web app. For starters it’s development has spanned 7 years… Yeah, a lot of things have happened in between. 🙂 But I wanted to finish the basics and get it working, to finally push it out the door and move onto something else. It’s development has been more of an exercise and than an objective based project.

The main idea was to use in place editing and to be somewhat RESTful in the calls and the way the app flows. Because it was meant for collaboration, no ownership is enforced or tracked (thus, no privacy either), but I wanted to track versions. There are a lot of ajax calls everywhere, which were mostly done with jQuery; it was fun porting and rewriting the modules I made 7 years ago in scriptaculous and prototype. And revisiting my php code from long ago was tough, but it was a great refresher.

My mini-markdown implementation seems a bit too minimal now, but back then it was actually pretty nifty to have it run in 70 lines of code instead of using some of the behemoth classes or packages available at the time.

I haven’t bothered updating the style or layout; though CSS has come a very long way in these 7 years, it was a little bit beyond my scope for this project.

If you want to have a look at it, pannel’s repo is on github. Play with it if you want and have fun.