Last episode I kept pointing at one thing and never quite naming it: that small, silent, hidden little computer that has to run your whole installation for weeks without complaining. I said "start looking at the tiny computers people leave running in corners" and left it there on purpose. Well, today we open the box. That little brain has a name, and if you've been anywhere near a maker space in the last decade you've probably already heard it: the Raspberry Pi.
Here's why I'm giving it a whole episode. Every single thing we built in this physical chapter - the LEDs from episode 109, the projection from 110 and 111, the wearables, the room-sensing installation from last time - all of it eventually needs a computer that isn't your laptop. You cannot leave your daily-driver machine bolted behind a gallery wall for three weeks. You need something cheap, tiny, boring, and utterly reliable that boots straight into your art and never asks for attention. That's the Pi. Allez, let's meet the workhorse :-).
Let me keep this grounded. A Raspberry Pi is a complete computer on a single board about the size of a credit card. It runs Linux, it has USB ports, HDMI out, WiFi, and a row of pins for talking to the physical world. Depending on the model it costs somewhere between 35 and 75 euros. That's it. That's the magic - a real, whole computer, cheap enough that you can dedicate one to a single artwork and forget it exists.
And crucially for us: it runs the exact tools we've used all series. Python, a full web browser, Node, p5.js in that browser. The sketch you wrote in episode 1 runs on a Pi with basically zero changes. You're not learning a new platform, you're learning a new place to put the platform you already know.
// the mental shift from last episode, made concrete.
//
// INSTALLATION (episode 114): "a small hidden computer runs the piece"
// |
// v
// THIS EPISODE: that computer = a Raspberry Pi
//
// your p5.js sketch -> Chromium browser -> HDMI -> projector/screen
// (unchanged since ep1) (on the Pi) (the artwork)
//
// the viewer never sees a computer. they see light. the Pi hides in a box.
The whole point of that diagram is the last line. In a good installation nobody ever knows there's a computer involved. It sits in a ventilated box behind the piece, humming quietly, doing its one job forever.
The first idea you have to get comfortable with is running the Pi headless. That means: no monitor plugged in for you, no keyboard, no mouse. The Pi boots, launches your sketch, sends the picture out over HDMI to whatever display or projector the artwork uses, and that's the only screen anyone sees. You manage the thing entirely from your laptop, over the network, using SSH.
If you've never used SSH, it's just a way to type commands on another computer from your own. You open a terminal, you connect, and now you're driving the Pi remotely as if you were sat in front of it.
# from YOUR laptop, connect to the Pi over the network.
# 'pi' is the username, and .local finds it by name on your wifi.
ssh pi@raspberrypi.local
# once you're in, it's just Linux. check what it is and how hot it's running:
cat /proc/device-tree/model # e.g. "Raspberry Pi 5 Model B Rev 1.0"
vcgencmd measure_temp # temperature - installations run for WEEKS, watch this
That temperature check matters more than beginners expect. A Pi driving graphics for three weeks straight in a warm gallery gets hot, and a hot Pi quietly slows itself down to protect itself, which shows up as your smooth 60fps sketch mysteriously stuttering on day four. So you check the temp early, and if it's high you add a little heatsink or a fan to the box. Boring, unsexy, saves the show.
Here is the single most important skill in this whole episode, and it's the thing that turns a Pi from "a computer" into "an appliance that IS your art". You need your sketch to launch automatically the instant the Pi gets power. No login, no clicking, no human. Someone flips the gallery breaker in the morning, and sixty seconds later the art is on the wall. A cleaner trips the plug at 3am, it plugs back in, the art returns on its own. Nobody phones you.
The clean, modern way to do this on Linux is a systemd service. Think of it as a little contract you hand the operating system: "here is a program, please start it every time you boot, and if it ever dies, start it again". You write a small text file describing your program and drop it in the right place.
# /etc/systemd/system/artwork.service
# tells the Pi: run my sketch at boot, and RESTART it if it ever crashes.
[Unit]
Description=Generative Artwork
After=graphical.target
[Service]
ExecStart=/home/pi/start-artwork.sh
Restart=always # <- the magic: crash? relaunch. forever.
RestartSec=3
User=pi
[Install]
WantedBy=graphical.target
Then you switch it on once, and from that moment the Pi obeys the contract on every single boot:
# enable it once - now it survives reboots AND power cuts, on its own.
sudo systemctl enable artwork.service
sudo systemctl start artwork.service
# check it's happy, or read why it's sulking:
systemctl status artwork.service
journalctl -u artwork.service -f # live log, super handy while building
That Restart=always line is doing exactly what our watchdog and PM2 did last episode, but at the level of the whole operating system. It's the same philosophy we keep circling back to: assume it will crash, and make crashing a non-event. If you'd rather not touch systemd on your first go, there's a quick-and-dirty alternative using cron that gets you 80% of the way:
# the beginner-friendly version: run a script once at every boot.
# edit your crontab with 'crontab -e' and add this single line:
@reboot /home/pi/start-artwork.sh
# less robust than systemd (no auto-restart on crash) but dead simple.
So what does that start-artwork.sh script actually launch? For a p5.js sketch, the loveliest trick is Chromium kiosk mode. Chromium is the open-source browser that ships with the Pi, and kiosk mode strips away everything - no address bar, no tabs, no cursor, no menus. Just your sketch, fullscreen, edge to edge. The browser vanishes and only the art remains.
#!/bin/bash
# start-artwork.sh - launch a p5.js sketch fullscreen with ZERO browser UI.
# the viewer sees ONLY the canvas. no url bar, no cursor, no distractions.
# hide the mouse pointer when it's not moving (needs 'unclutter' installed)
unclutter -idle 0 &
chromium-browser \
--kiosk \ # fullscreen, no browser chrome at all
--noerrdialogs \ # never pop an error box in front of the art
--disable-infobars \ # kill the "restore pages?" nag
--incognito \ # start clean every boot, no stale state
--autoplay-policy=no-user-gesture-required \ # let sound/video just play
http://localhost:8000/sketch.html
Notice I'm loading the sketch from localhost - the Pi runs a tiny local web server serving your sketch files to its own browser. No internet needed at all, which we'll come back to because offline is a feature. The sketch itself is ordinary p5.js, the same code you'd run on your laptop, with two small manners-for-a-gallery additions:
// a gallery-ready p5.js sketch. two tweaks make it installation-friendly.
function setup() {
createCanvas(windowWidth, windowHeight); // fill the WHOLE screen
pixelDensity(1); // don't 2x-render on hidpi - saves the Pi
noCursor(); // belt-and-braces: hide the pointer
}
function draw() {
background(10, 20); // faint trails, our old friend
// ... your generative art, exactly as always ...
}
// if anyone plugs in a different projector, adapt instead of breaking
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
The pixelDensity(1) line is a small kindness to the Pi. On a high-resolution display p5 would try to render at double resolution by default, quadrupling the work for pixels nobody can tell apart at gallery viewing distance. Force it to 1 and you hand the little computer a huge amount of breathing room for free.
Let me be honest with you, because this is where dreams meet silicon. A Pi is a real computer but it is not a powerful one. A Raspberry Pi 4 with 4GB of RAM will happily run a p5.js sketch at a smooth 30 to 60fps up to around 800x600, maybe a bit more if the sketch is gentle. Push it to full HD with heavy particle systems or complex shaders and it starts to gasp. A Pi 5 is meaningfully faster and handles more. But the rule is the same rule as the whole physical chapter: profile before you commit hardware.
// cheap on-screen fps meter - run this ON THE PI, not just your laptop.
// your fast dev machine LIES to you about how the sketch really performs.
let times = [];
function showFPS() {
const now = millis();
times = times.filter(t => now - t < 1000); // keep last second of frames
times.push(now);
fill(0, 255, 0);
noStroke();
textSize(16);
text(times.length + ' fps', 12, 24); // frames in the last second
}
The mistake I made on my first Pi piece: I built and tested the whole thing on my beefy laptop, it ran like silk, and I only tried it on the actual Pi the night before install, where it chugged along at a heartbreaking 12fps. Lesson learned forever - develop against the target. Get the sketch running on the Pi early and keep checking, so you feel the ceiling while you can still design around it. If your idea genuinly needs more muscle than a Pi can give, that's fine, you find out in week one and not opening night.
Now the part that makes a Pi more than a small media player. Along one edge is a double row of metal pins called the GPIO - general-purpose input/output. These pins let your code read physical sensors and switch physical things on and off. A button, a motion sensor, a light sensor, a relay driving a motor, the LED strips from episode 109 - all of it wires to these pins, and all of it becomes readable and controllable from your art.
Reading a simple button in Python is genuinly a joy on the Pi thanks to a library called gpiozero, which is about as friendly as hardware ever gets:
# read a physical button wired to a GPIO pin. press it to trigger the art.
from gpiozero import Button
button = Button(17) # button connected to pin 17
def on_press():
print("pressed! - tell the sketch to change scene")
# e.g. write to a file / socket the browser sketch is watching
button.when_pressed = on_press
And driving output is just as clean. Here we pulse an LED, but the same pattern switches a relay, a solenoid, a fan - anything the artwork needs to physically do:
# fade an LED with the sketch's mood. output is as easy as input.
from gpiozero import PWMLED
from time import sleep
led = PWMLED(18) # an LED (through a resistor!) on pin 18
while True:
for b in range(0, 101):
led.value = b / 100 # 0.0 dark .. 1.0 full - a slow breath
sleep(0.01)
for b in range(100, -1, -1):
led.value = b / 100
sleep(0.01)
The design pattern that ties this to everything we've built: the Python GPIO script and the browser sketch are two separate little programs, so they need to talk. The simplest bridge is a tiny local WebSocket - the Python side reads the sensors and shouts the values, the p5.js side listens and drives the visuals. Sensor in one language, art in another, one thin wire between them.
// in the p5.js sketch: listen to the Pi's GPIO over a local socket.
// python reads the button/sensor, the browser draws the response.
let socket = new WebSocket('ws://localhost:8080');
let sensorValue = 0;
socket.onmessage = (event) => {
sensorValue = parseFloat(event.data); // fresh reading from the real world
};
function draw() {
background(0);
// the physical sensor now drives a visual parameter. room -> art.
const r = map(sensorValue, 0, 1, 20, 300);
circle(width / 2, height / 2, r);
}
See the shape? It's exactly the interaction loop from last episode - sense the room, map it to a parameter, render - only now the "sense" half is real hardware on real pins instead of a simulation. The Pi is what lets our installation ideas actually touch the physical world.
There's a little camera module that plugs straight into the Pi, and it opens a gorgeous door: computer vision, running entirely on the board, with no internet. Remember the pose detection and hand tracking we did back in episodes 93 and 94? A Pi with a camera can run lightweight versions of those models locally. Motion detection, presence, rough body tracking, all happening inside the box in the gallery, no cloud, no data leaving the room.
# grab frames from the Pi camera - the raw material for on-device vision.
from picamera2 import Picamera2
import time
cam = Picamera2()
cam.configure(cam.create_preview_configuration())
cam.start()
time.sleep(1) # let it warm up and set exposure
while True:
frame = cam.capture_array() # a numpy image, ready for processing
# feed to a small motion / pose model, or just diff frames for movement
# ... movement here becomes an input to the sketch, same as a sensor ...
time.sleep(0.05)
I want to stress the local part, because it's both an ethical and a practical win. An installation that watches people is a little uncomfortable if it's phoning frames off to some server. A Pi doing all its seeing on-device, keeping nothing, sending nothing, is honest with its audience and also can't be broken by a flaky gallery WiFi. Privacy and reliability, same decision. That's a nice place to be.
Speaking of WiFi - you have three choices for how connected your Pi is, and they rank very clearly for installations. WiFi is lovely for developing (you SSH in, tweak, restart) but flaky for running. Ethernet, an actual cable, is rock solid where you can run one. And the best of all, when your piece allows it, is fully offline: the sketch and all its files live on the Pi's SD card, the local server serves them to the local browser, and the Pi never touches a network at all once it's installed.
# serve the sketch to the Pi's OWN browser - no internet involved.
# a one-line web server, started by that boot script from earlier.
cd /home/pi/artwork
python3 -m http.server 8000 # localhost:8000 -> your sketch. offline forever.
Offline is maximum reliability. Every failure mode that involves "the network went down" simply cannot happen to a piece that has no network. For a huge number of generative installations - anything that doesn't pull live data - this is the right answer, and it lets you sleep at night while your art runs in a building you're not in.
One genuine gotcha specific to the Pi. It boots and runs from a microSD card, the same little card that goes in a camera. And SD cards have a weakness: they wear out if you write to them constantly. An installation running for weeks, scribbling logs and temp files every second, can actually kill a cheap SD card mid-show. I've had it happen, and a dead card on day nineteen is a miserable phone call.
Two good defences. The robust one is to make the whole system read-only using an overlay filesystem - the Pi runs from a frozen snapshot and writes go to disposable RAM, so nothing ever wears the card:
# turn on the read-only overlay filesystem via the Pi's config tool.
# after this, the SD card is never written to - it CANNOT wear out.
sudo raspi-config
# -> Performance Options -> Overlay File System -> Enable
# bonus: you can now yank the power with no risk of corrupting the card.
# no logs to disk either - keep them in RAM so nothing writes:
# add to /etc/fstab: tmpfs /var/log tmpfs defaults,noatime 0 0
That "yank the power safely" bonus is huge for installations, by the way. Normally pulling the plug on a running Linux box risks corrupting the card; with a read-only overlay you can cut power however you like and it boots back clean every time. Gallery staff flipping a master switch at closing time becomes completely safe. The other option, if your budget stretches, is to boot the Pi from a proper USB SSD instead of an SD card - faster and far more durable, a habbit worth adopting for pieces that must run for months.
Last idea, and it's the one that unlocks room-scale ambition. When one Pi and one display isn't enough - say you want five synchronised screens wrapping a corner, or a wall of panels all breathing together - you use several Pis, networked, one nominated as the master. The master decides the overall state and broadcasts it; the others listen and render their slice of the whole. A short message on the local network keeps them all in step.
// MASTER pi: broadcast one shared 'beat' so every screen stays in sync.
// a tiny number over the network is enough to lock them together.
const clients = []; // the other pis, connected in
setInterval(() => {
const state = { t: Date.now(), hue: (frameCount * 0.5) % 360 };
const msg = JSON.stringify(state);
clients.forEach(c => c.send(msg)); // "everyone, render THIS moment"
}, 33); // ~30 times a second
// CLIENT pi: render MY portion of the giant canvas from the master's state.
// each screen knows its own offset, so together they form one big image.
const MY_OFFSET_X = 1920; // this screen is the 2nd panel
socket.onmessage = (event) => {
const state = JSON.parse(event.data);
// draw the same world, just shifted - the seams line up into one artwork
drawWorld(state, MY_OFFSET_X);
};
For tight timing-sensitive work people often reach for a protocol called OSC instead of raw WebSockets, but the idea is identical: one brain, many bodies, a thin stream of numbers holding them together. It's the same master-and-couriers split we first met with the LEDs in episode 109, scaled up from a light strip to a whole wall of screens.
Here's your mission, and it's the real one, the foundation under every reliable installation you'll ever make. Take a Raspberry Pi, put one of your p5.js sketches on it, and set it up so it launches fullscreen in Chromium kiosk mode automatically on boot. Wire up the systemd service. Connect a display. Then do the test that matters:
# THE TEST that separates a demo from an installation.
# 1. set up the systemd service + kiosk script from this episode
# 2. connect the Pi to a screen or projector
# 3. now the brutal bit:
# -> pull the power cord clean out of the wall
# -> wait a few seconds
# -> plug it back in
# 4. START A TIMER. do NOTHING else. don't touch a keyboard.
#
# PASS = your art is back on the screen within ~60 seconds, all by itself.
# FAIL = you had to log in, click, or fix anything at all.
Pull the plug and walk away. If your sketch reappears on its own inside a minute, with you touching nothing, you have built something that can survive a gallery. If you had to intervene - if it dropped to a desktop, or asked for a login, or just sat black - then it isn't ready, and you go fix the auto-start until the plug-pull test passes clean. This one exercise, unglamorous as it looks, is the difference between art you have to babysit and art that lives on its own. Master it and every installation from here on rests on solid ground.
Restart=always launches your sketch every boot and relaunches it if it crashes - the same "make crashing a non-event" idea as last episode's PM2 and watchdog. A @reboot cron line is the simpler beginner versionpixelDensity(1) to hand the little Pi a lot of free performancegpiozero in Python makes it a joy, and a tiny local WebSocket bridges the Python sensor-reader to the p5.js sketch. It's last episode's sense-map-render loop, now touching actual hardwareSo that's the workhorse of physical creative coding laid bare. The Pi is the quiet, cheap, tireless little brain that takes everything from this whole chapter - the lights, the projection, the sensors, the room - and turns it into something that runs itself, out in the world, with you nowhere near. It is genuinly the piece of kit that made me stop thinking of my art as "a thing on my screen" and start thinking of it as "a thing that lives somewhere".
And now, look at what we're holding. We've plotted and cut and printed and lit and projected and worn and sensed a room, and today we got the brain that runs it all unattended. Every physical ingredient is finally on the table at once. You can probably feel where this is going - it's time we stopped learning the pieces one at a time and actually built something whole with them, end to end. Get that Pi flashed and passing the plug-pull test, because next time we're rolling everything together :-).
Sallukes! Thanks for reading.
X