Skip to content

Ghost Frame: Display Images of People (and Cats) that Don’t Exist

  • by

For this project, I used a PyPortal device from Adafruit. I have been waiting for a CircuitPython device that could connect to the internet and the PyPortal not only does that with the use of the ESP32 as a WiFi coprocessor, but it also has a touch display, an ATMEL/Microchip SAMD51 M4 processor for tons of speed and memory, 8MB of Flash, plugin ports and so much more. I have so many ideas for using this device. As Humphrey Bogart once said in a movie,

“I think this is the beginning of a beautiful friendship.”

Source: Casablanca
Speaker: Rick Blaine (Humphrey Bogart)

For my first PyPortal project (and my first CircuitPlaygound project), I wanted to display images from the site, which displays random AI generated images of people who actually do not exist in real life, hence the name, “Ghost Frame”. The site generates a new JPEG image about every 2 seconds. So the idea was to download this JPEG file from the site and display it on the PyPortal. The first problem: the PyPortal does not natively support JPEG images. It does support BMP images but JPEG decompression is quite computationally intensive for CircuitPython. JPEG decompression may have native support in the future but not in its current release.

To work around this issue, an idea mentioned by Lady Ada herself on GitHub was to convert the JPEG file to BMP. With a bit of research, I wrote a JPEG to BMP converter in PHP. This is a web service written in less than 10 lines of PHP code. It not only converts JPEG images to BMP images, but it also scales the BMP image to fit the PyPortal’s screen dimensions of 320 x 240 pixels. You will need a website running PHP (or have a really good friend that has one) in order to run this web service.

The next hurdle was downloading the converted JPEG image to the PyPortal for display. This required storing the image file temporarily on the PyPortal before displaying it, since the image was too big to fit in memory. I first came up with using an SD card in the PyPortal’s SD card slot and storing the image there. The same image file is written over and over each time a new image is downloaded. This option works, but you need an SD card to use with the PyPortal. I also wanted to try downloading the image file directly to the CircuitPython’s 8MB filesystem. This is a bit tricky since the filesystem is also available to the host computer the CircuitPython device is plugged into, and having two different processors writing to the same filesystem is a disaster waiting to happen.

What is needed is a file, which tells the PyPortal if the filesystem is read-only or read-write. This check is done when the PyPortal boots up. The mode is toggled by a female/female jumper wire on the D4 connector. Connect the jumper wire and it is read-write for CircuitPython. Remove the jumper wire and it is read-write for the host operating system. So you need the operating system to have write access to install and modify the Ghost Frame code in Once you have that working, you can insert the wire to allow the PyPortal to download the image directly to the CircuitPython file system. An extra bonus is that you can view the most recent image directly from the host computer.

PyPortal showing jumper wire needed to enable CircuitPython programs to write to the filesystem.

Here is the code for

import board
import digitalio
import storage

# Either of these should work for the PyPortal
switch = digitalio.DigitalInOut(board.D4) # For PyPortal
#switch = digitalio.DigitalInOut(board.D3) # For PyPortal

switch.direction = digitalio.Direction.INPUT
switch.pull = digitalio.Pull.UP

# If the switch pin is connected to ground CircuitPython can write to the drive
# Use a female/female jumper wire to connect the outer pins in the D4 connecter

storage.remount("/", switch.value)

The filesystem method is quite a bit faster in loading the image to the PyPortal.

The “Ghost Frame” is not just limited to people. The site, displays AI generated images of cats. You can select in the code if you want people ghosts or cat ghosts to appear in the frame.

AI generated image from

The code for the “Ghost Frame” is open source and is available on GitHub. For future development, it would be nice to take advantage of the touch screen and be able to select on the screen to show people or cats.

What ghosts will appear in your “Ghost Frame”?