You can make a seemingly innocuous portrait come to life using an ornately framed flat panel TV powered by a Raspberry Pi. With proper lighting, it will look just like a painting, but every so often, the portrait subject's eyes will shift their gaze ever so slighting, sending chills up the spines of your unsuspecting victims, er, friends.

You'll use a hidden Raspberry Pi Zero as the image playback device to drive the TV over HDMI. Plus, you'll have a set of controls available using the buttons and thumbstick of the Joy Bonnet so you can pause and start the show, and more!

Parts and Materials

Most of the parts you'll need for the image player are provided in the Adabox 005 kit -- you'll just need to supply an OTG Ethernet hub or WiFi dongle, plus a keyboard and mouse. Here are the individual parts:

1 x Raspberry Pi Zero
Version 1.3 Pi Zero ( the W model will work, too)
1 x Joy Bonnet
Controls for your Pi Zero
1 x Adafruit Pi Zero Case
Lovely little case, with snap-off GPIO port cover
1 x Hammer Headers
solderless Raspberry Pi connections


1 x 8GB MicroSD Card with NOOBS 2.0
Micro SD card for your Raspbian instlallation
1 x USB OTG Host Cable
MicroB OTG male to A female
1 x Ethernet Hub and USB Hub
with Micro USB OTG connector

You can save some money and add some convenience by getting the Pi Zero budget pack instead of some of the individual parts above:

1 x Pi Zero Budget Pack
Pi Zero, case, power, SD card, header pins, mini HDMI to HDMI adapter, and OTG cable!

For the digital portrait you'll need:

  • Flat panel TV with HDMI input
  • Ornate frame, large enough to fit TV, or
  • Framing material or crown molding (a cheaper alternative) to make your own
  • VESA mounting hardware to hang the TV
  • Miscellaneous hardware to affix the frame to the TV, such as small wood screws and angle brackets 


If you're going to cut a matte for your TV to fit a frame, you'll need a long straight edge or framing square, and a utility knife.

To cut a frame to exact dimensions or make one from framing or molding, you'll need a tape measure, hand saw and miter box, or a miter saw, as well as fasteners and wood glue, and some corner clamps.

Let's get started setting up the Pi Zero.

Pi Prep

First, prep the Raspberry Pi Zero so that you can attach the Joy Bonnet to it. You'll do this by attaching male headers to the Raspberry Pi Zero's GPIO pins. You have a couple of options here: you can solder on a standard 2x20-pin strip dual male header, or you can use the provided GPIO Hammer Header and jig.

To solder on the the standard pins, follow this guide.

If you don't want to solder on the headers, here's a great alternative!: Hammer Headers.

Hammer headers are special friction fit headers that include a little jig to hold everything in place, while you smash away at it with a hammer! OK, you may want to be a bit gentle with it, but not too gentle. You know, "hammer gentle".


Once you've be-headered your Pi Zero, go ahead and fit it into its case.  Then, align the Joy Bonnet's female headers over the Pi Zero's male headers and squeeze them together snugly.

OS Installation

SD Card

The Pi Zero can't do much without an operating system, so let's give it one. Insert the 8GB NOOBS (New Out of Box Software) micro-SD card into the Pi Zero's micro-SD card slot. NOOBS is an interface that makes it easy to install your operating system, in this case we'll install Raspbian Linux, once we get everything else plugged in.


Plug the micro-HDMI to HDMI cable into the Pi Zero on one end, and your TV or monitor on the other.



While you won't need a keyboard and mouse connected when once your Pi Zero is configured as an image player for your haunted portrait, you'll need to connect those now on order to configure things. 


You can use a wireless keyboard/mouse combo, such as this one, or use wired input devices via a hub plugged into the Pi Zero using the OTG host cable. That particular hub is a great choice, because it allows you to plug in multiple USB devices as well as an Ethernet cable, which we'll need for some additional software installation later.


Note that the Pi Zero's data micro-USB port is the one near the center of the board, marked "USB", and this is the one into which your OTG cable must be plugged. The other micro-USB port, marked "PWR", is for power only.

I used a small monitor for setup, just to make documentation easier, but you can go ahead and use your full sized TV or monitor!


You can now power up the Pi. Make sure the TV is turned on, then plug the power supply into the wall, and the other end into the Pi's PWR micro-USB port. The green power indicator LED on the Pi Zero will light up, and then you'll see signs of life on the TV.

Install Raspbian

After your Pi Zero boots up, and you will use the NOOBS interface to install Raspbian. 

You'll want to choose the language and keyboard from the dropdown menus at the bottom (for example, English (US) and US), then check the Raspbian OS checkbox, then click "Install".

This is a great resource for more information on NOOBS and Raspbian installation. (Skip ahead to the section called "First Boot"), here are some highlights:


  1. Plug in your keyboard, mouse, and monitor cables.
  2. Now plug the USB power cable into your Pi.
  3. Your Raspberry Pi will boot, and a window will appear with a list of different operating systems that you can install. We recommend that you use Raspbian – tick the box next to Raspbian and click on Install.
  4. Raspbian will then run through its installation process. Note that this can take a while.
  5. When the install process has completed, the Raspberry Pi configuration menu (raspi-config) will load. Here you are able to set the time and date for your region, enable a Raspberry Pi camera board, or even create users. You can exit this menu by using Tab on your keyboard to move to Finish.

The default login for Raspbian is username pi with the password raspberryNote that you will not see any writing appear when you type the password. This is a security feature in Linux.

To load the graphical user interface, type startx and press Enter.

Congratulations, you've installed your operating system! 

Keyboard Layout

In case you didn't choose this during setup (I occasionally forget), now's a good time to adjust the internationalization of your system and keyboard to match your region. Here's a great guide on adjusting internationalization and keyboard layout, as well as some other useful configs.

Get Online

One nice feature of Linux is the ability to easily install software from online repositories by using simple commands. To access these, plug in an Ethernet cable from your local network router to the OTG hub on the Pi Zero. Alternately, you can use a USB WiFi dongle to get online. Here's a guide on setting up WiFi on the Pi.

Update Application List


Once online, you can update the "apt-get" software. This will update the list of available software. To do so, you'll open a command line window by clicking the "Terminal" icon at the top of the screen.


Then, on the Terminal window's command line, type 


sudo apt-get update


And then press return. This will run for a little bit as it reads the available package lists, and then finish, reporting that it is done.


Now you'll be able to install software in later steps, including the image player, called feh, and know that you're getting the latest version.  

sudo? What's sudo? It stands for "superuser do", and it's a program that lets you run powerful commands which are normally restricted for reasons of safety and security. Or just to prevent accidents!

Next, we'll set up the Pi Zero for image playback. This guide on building a Pi-based digital picture frame is an excellent resource, which I followed closely while building my Haunted Portrait.

I'll cover all of the crucial steps here, but you can head to the link for other details, such as setting up SSH, which we won't be doing here.

Once your Pi Zero is connected to your network, you can ssh (secure shell) into it from another computer if you like. Here's how:

Keep the Screen On


First thing to do is prevent the screen from turning "off" (actually just going black) after your Pi Zero has been idle for too long. This is normally a good thing, but not when you're making a Haunted Portrait! You can do this by editing the lightdm.conf file. In the Terminal, type:


sudo nano /etc/lightdm/lightdm.conf


This opens the lightdm.conf file in a text editor called nano.

Once this opens in nano, use the arrow keys (or mouse wheel for faster scrolling) to navigate down to the line that reads:




Add a new line below that one, and type this in:


xserver-command=X -s 0 -dpms


That line tells the screen to never "blank" or darken.


To save and exit from nano type ctrl+x on the keyboard, which will lead to the question to save the file (modified buffer) or not, to which you'll reply yes by typing a Y for "yes". Then press return to accept the existing file name, overwriting the old version.


This file's parameters are read at system startup, so in order to invoke them, you must now restart the Pi Zero by typing: 

sudo reboot

in the Terminal window and then pressing return.

Once your Pi Zero reboots, we can move on to installing the image player software.

Now, let's install the image player!

Test Images


Before we start installing the image software, grab a few images to use as a test. You can use the Pi Zero's web browser to save some test images. Click the browser icon (a little Earth with cursor) to view this page on your Pi Zero (in case you're following along and a different device) and then download the three images shown below.


Do so by first clicking an image, which brings up the image asset page.


Then, right-click on the link marked Original and save the full-sized image to the Pictures directory in your user directory (the user directory is /home/pi by default).


Repeat these steps for all three images.

These images are sized to 1920 x 1080 pixels, which is the default resolution of an HDMI device.

You can now close the web browser.

NOTE: Another great way to get images onto your Pi Zero is by setting up the Pi as a Samba server. This allows it to be discovered on your network, and then you can drag files onto it from another machine! Here's a great guide to setting up Samba.

Install and Configure feh

Feh is a very nice command line image viewer, that happens to be perfect for running a slide show with specific parameters. It also allows key commands to be used while running, which is how we'll be able to use the Joy Bonnet to trigger different events.


 Let's install feh! Type this into the Terminal window, and then press return:


sudo apt-get install feh


This will download the necessary files and then install the application. When prompted, type and press return to continue.

Let's try out feh as a slide show viewer. Type this in the Terminal window (it may be easier to copy and paste it) and then press return:

 DISPLAY=:0.0 XAUTHORITY=/home/pi/.Xauthority /usr/bin/feh --quiet --preload --randomize --full-screen --hide-pointer --slideshow-delay 5.0 /home/pi/Documents/testPics &


This will display the images in your /testPics directory, with the cursor hidden, fullscreen, and a with few other options. If you're curious about all of the options, you can press esc on the keyboard to stop the slideshow, and then type:


man feh


in your Terminal window and press return to read the manual page for feh. You can press when you're done to quit man.

One thing you can do to clean up your code is to use short versions of the option flag names, as seen here:

DISPLAY=:0.0 XAUTHORITY=/home/pi/.Xauthority /usr/bin/feh -q -p -F -Y -D 5.0 /home/pi/Pictures &

 This will do the same thing as the more verbose version above.

Script It


Instead of retyping that command every time you want to run a slideshow, you can create a special type of text file, known as a shell script, to store the command (or many commands). This shell script can then be invoked any time you want to run the commands contained within.


Create a new shell script with this command:


sudo nano /home/pi/


Once again, this will launch the nano text editor. Type this into the file:




DISPLAY=:0.0 XAUTHORITY=/home/pi/.Xauthority /usr/bin/feh --quiet --preload --full-screen ---hide-pointer --slideshow-delay 15.0 /home/pi/Pictures &


The first line tells the system to run the rest of it as a bash shell command, so it knows what to expect. 

We've increased the delay to 15 seconds here, you can adjust this to whatever value you like. For a properly creepy atmospheric effect you can let it go minutes between each image change.

Close and save the shell script by pressing ctrl-x and then and return.

Now, run the shell script -- which will in turn run all of the contents of the file -- by typing this:

bash /home/pi/

That just ran your slideshow with a much shorter command. Much easier!

Now, we'll set up the Joy Bonnet to use as a slideshow controller.

Install the Joy Bonnet

The Joy Bonnet is a terrific little board! Originally designed to play video games in emulation, it's actually incredibly useful for adding a small set of controls to embedded projects as well.

Let's install the Joy Bonnet software so that the system can use it. You should already have the Joy Bonnet attached to the Pi Zero -- if not, do so now.

Installation is as simple as entering two lines in the Terminal, but you can read more details here in the Joy Bonnet Learn Guide.

Type this in to your Terminal to load the install script on your system:

cd ~
sudo apt-get install python3-pip
sudo pip3 install --upgrade adafruit-python-shell

Then, type this in your Terminal to run the install script:

sudo python3

then press return, then: 

  • enter Y to agree to install
  • enter again to disable overscan, and then enter:
  • enter N to not install the GPIO-halt utility
  • finally enter one last time to continue

Once installation is complete (it takes a few minutes), you'll be asked if you want to reboot. You can go ahead and type and press return. Or, you can reboot the system later by entering this in the Terminal:

sudo reboot 

Joy Bonnet Button Configuration

Now, we can set up the buttons on the Joy Bonnet to run particular commands for our slideshow by sending keystrokes to feh.

First, let's open the script which contains the key bindings by typing:

sudo nano /boot/

Scroll down to the section that starts with:


Then change the key assignments to the following:

BUTTON_A: e.KEY_N,        # next image
BUTTON_B: e.KEY_P,        # previous image
BUTTON_X: e.KEY_Z,        # unassigned
BUTTON_Y: e.KEY_M,        # toggle menu
SELECT:   e.KEY_Q,        # quit feh
START:    e.KEY_H,        # pause/continue slideshow
PLAYER1:  e.KEY_ESC,      # escape key  to close menus       
PLAYER2:  e.KEY_ENTER,    # enter key for menus
1000:     e.KEY_UP,       # Analog up for menu nav
1001:     e.KEY_DOWN,     # Analog down for menu nav
1002:     e.KEY_LEFT,     # Analog left -- menu nav and next image
1003:     e.KEY_RIGHT,    # Analog right - menu nav and previous image
The rows in the first column correspond to the Joy Bonnet buttons and thumbstick (the four directions are the 1000-1003 entries), and the second column is the keystrokes that are sent

Once edited, save the file by pressing ctrl-x followed by and return.

You can choose other hotkeys, just look at the feh manual by typing 'man feh' in a Terminal to see what's available.

Reboot the Pi Zero so the changes take effect. Then, try out the Joy Bonnet buttons by opening a Terminal and pressing the buttons to "type" the assigned letters.

Now, let's use them as intended! Run the frame player script by typing this in your Terminal:


Once the slideshow has begun, try pausing with the Joy Bonnet Start button, and then advancing or going back through the images with the and buttons.

Next, let's change the system to portrait orientation.

Rotate the Display

A large part of the visual impact of this project comes from hanging the TV vertically in portrait orientation. We're so used to seeing TVs in their horizontal landscape position, that the portrait orientation is novel and unexpected. This, plus the frame that we'll use, and careful lighting really sell the gag.

To rotate the orientation of the data sent to the TV, you'll add a single line to the Pi Zero's configuration text file. Type this in a terminal to edit the file:


sudo nano /boot/config.txt


and then press return.


You can add the new line at the very bottom of the file. Type this in on a new line:


#rotate the display: 0=normal, 1=right vertical, 2=upside down, 3=left vertical


That line is a comment, which won't be read by the machine, just us humans. Comment lines are preceded by the # symbol.


On the next line, type this:




Now, you can save the file (ctrl-x, Yes, return) and then restart the Pi Zero by typing:

sudo reboot

Get ready to turn your neck sideways or rotate your monitor to continue! If you ever need to go back to regular orientation, change the config.txt file's display_rotate value to 0.

Autostart the feh Script

In the final Haunted Portrait, you won't have a keyboard or mouse connected (unless you use a wireless setup), so you'll want to set up the Pi Zero to automatically run your slideshow script on startup. This also means that if there is a loss of power, the system will not spend a long time stuck on a Linux screen!

To set the script to automatically run on startup, there are just two simple steps. First, we'll set the file permissions of the script to be "executable" by the system, not just the user who created it. Then, we'll add a line to the destktop environment configuration script that  automatically runs our picture frame shell script upon launch.

There are many different desktop environments available for Linux, the one used here in Raspbian is called LXDE for Lightweight X11 Desktop Environment


Let's have a look at the current file permissions. In your Terminal, type this in to list the directory contents with verbose info:

ls -l /home/pi

That list of letters on the left side in front of each file shows you what type of permissions exist for the file.

Possible permissions are read, write, and execute for three types of accounts: owner, group, and user. Here's a very good primer on the subject.

For the slideshow script we see: 


which means the file can be read and written by the owner ("root"), and only read (not executed or written) by groups and by users. This means the system (user "pi") doesn't have execution permission. So, we want to adjust that so the system can actually run the script without user intervention.

This simplest way to adjust this in our case is to grant execution permissions to all users. The chmod command is used to do so. Type this in your Terminal:

sudo chmod 755 /home/pi/


Now, list the file contents and you'll see the new permissions.


Next, you will add a line to the desktop environment autostart file so it run the script after booting. Open the autostart file for editing by typing:

sudo nano ~/.config/lxsession/LXDE-pi/autostart

Add a new line at the bottom and enter this text:


As is by now the familiar routine, save and exit the file with ctrl-x, Y, return.

Once again, you'll need to restart the Pi Zero to see the effects of your changes, so type: 

sudo reboot

This time, once the Pi Zero reboots, it will launch the LXDE desktop, and then start the slideshow! How automagical!

This time, once the Pi Zero reboots, it will launch the LXDE desktop, and then start the slideshow! How automagical!

When you tire of watching your test slideshow, you can press Esc on the keyboard, or the Select button on your Joy Bonnet to stop it. You can relaunch at any time by typing this into a Terminal window:

bash /home/pi/

Now, let's prepare some proper portrait images!

Now that your frame playback software is up and running, let's replace the test images with something more thematically appropriate.

How about Master Gracey, from the Haunted Mansion? This image was painted by character artist Corwin Webb, and is made available here with his permission. Thanks!

Check out  Corwin's illustration site for more excellent work! (I modified the painting in the second image to change Master G's gaze a bit, and to remove the pupils and irises in the third one.)

Make sure to crop or resize your images to the same resolution as the television you're using, in most cases, 1080 x 1920. 

You can make modifications to any portrait in an image editor such as Photoshop or GIMP. One simple trick is to paint out the irises and pupils, leaving behind only a haunting set of scleras (sclerae?). 

Or, you can paint them entirely black. Creepy.

How about red eyes!

A slightly more advanced technique is to use an image deformer lattice or "puppet warp" in Photoshop to make the eyes squint or look in a slightly different direction.

Or, turn your own photographs into portraits using filters.

I took photographs of a family portrait, that of my great-great-grandfather, the Rev. William Park. I'm sure he was a very nice guy, but for some reason I think he makes for an excellent subject of a Haunted Portrait.

In order to amp up the spooky factor, I made a couple of copies of the portrait and modified his eyes so that his expressions change just enough to make viewers question their own eyes.

Here's the original.

In this one I image warped his eyes to change his gaze and expression slightly.

Finally, I repainted this image so his eyes are closed.


Once you're happy with your images, save them as .jpg files and place them in your /home/pi/Pictures folder on the Pi Zero. Remove any images you don't want played, such as our original test images, and then restart the slideshow.

Now we're ready to frame the TV.

To finalize your Haunted Portrait, you'll want to place the TV inside an ornate picture frame, hang it from the wall, and adjust the lighting. Ideally, you'll also hide the wiring of the TV and Pi Zero power supplies by hanging the portrait over a wall outlet, or disguising the wires with a wall mount kit, color matched to the wall.


Here you can see one extreme -- where I removed the LCD panel and electronics from the plastic case and bezel in order to get the thinnest possible profile with a tight fit.

High voltage electricity is dangerous, be very careful around it. Don't remove the TV from the panel unless you are experienced and plan to protect guests and pets from the back of the TV by embedding it in a wall cutout.

Next, I resized and existing frame from a thrift store so that it is a nearly perfect, edge to edge fit. I did so by carefully measuring the LCD panel and then cutting the existing frame at 45 degree angels on a miter saw to size.

I then glued and fastened the corners back together, clamped them, and let it dry.

The fit was tight enough that I didn't need any fasteners to connect the frame to the LCD TV panel -- it simply stays on by friction, and the TV will bear the load when hung from the wall with it's built in VESA mounting points.

Attached the pre-existing power/input/volume button strip to the backside of the frame with double-stick foam tape.

I was also able to power the Pi Zero from the TV's built-in USB port, so one less power adapter to worry about hiding!

A less involved, but very attractive way to fit your TV to a frame is to buy a frame that is oversized and then cut (or have a framing store cut) a matte to match the TV screen dimensions. Or, how about an oval matte to further enhance the illusion?

A less involved, but very attractive way to fit your TV to a frame is to buy a frame that is oversized and then cut (or have a framing store cut) a matte to match the TV screen dimensions. Or, how about an oval matte to further enhance the illusion?

Of course, portraits come in all sizes, so there's probably an old, small monitor kicking around somewhere in the back of your tech graveyard closet -- if you've got one with an HDMI input, why not turn it into a Haunted Portrait?!

Mount the Haunted Portrait


Use a VESA mount to hang the portrait, it's much stronger and safer than a picture hook! Here, you can see the VESA mount in action on a wall where the portrait will cover the power outlet.

Final Touches

Here are some thing you may be able do in your TV's control menus to maximize the quality of the effect:

  • Turn off LCD backlight -- the less the image looks like it is transmissive light the better
  • Adjust brightness and contrast  until the dark and bright portions of the image look realistic for a painting
  • Turn off adaptive screen dimming
  • Turn off automatic power down or screen saver

Tuning the Image

Here are some thing you may be able do in your TV's control menus to maximize the quality of the effect:

  • Turn off LCD backlight -- the less the image looks like it is transmissive light the better
  • Adjust brightness and contrast  until the dark and bright portions of the image look realistic for a painting
  • Turn off adaptive screen dimming
  • Turn off automatic power down or screen saver

Tuning the Lighting

If you've seen digital portraits at theme parks, such as the living painting in the World of Harry Potter, there are a few things they've done that really make the effect work well. One of the most important is to control the lighting.

  • You want to try to light the frame at the same level as is apparent in your digital portrait, and use small side lights to lift the frame without washing out the display
  • At odds with this, you also want to avoid glare! Place the portrait in a place with limited viewing angles, such as the end of a short hallway
  • Another trick is to use a matte finish screen if possible, or for the hardcor,e even cover the screen in a thin level of thin, transparent gel medium or a matte film. Be careful, however: the matte effect work amazingly well when viewed straight on, but at oblique angles it can quickly reduce the amount of light visible from the screen and will go hazy gray
  • Most of all, have fun figuring out the best ways to delight and spook your guests with your Haunted Portrait!

Another great spooky frame project to check out is this one on making a reactive, face tracking CG picture frame. Just in time for Halloween!

This guide was first published on Sep 15, 2017. It was last updated on Sep 15, 2017.