Ben Nuttall — gpiozero flow
Published 25 July 2026
gpiozero flow
Back in 2015, I had the idea for gpiozero — a Python<br>library for Raspberry Pi, making programming GPIO devices simpler by abstraction: instead of<br>thinking about pin channels, voltages, pull-up resistors and rising or falling edges, we think about<br>buttons being pressed and released, sensors detecting light or motion, and motors driving forwards<br>and backwards. gpiozero let you write code in these terms.
We developed a device-focused API, and patterns emerged. We ended up supporting three types of<br>programming:
Procedural:
while True:<br>if button.is_pressed:<br>led.on()<br>else:<br>led.off()
Event-based:
button.when_pressed = led.on<br>button.when_released = led.off
Declarative:
led.source = button
These three snippets achieve the same thing in different ways. One repeatedly asks the button if<br>it is pressed; one tells the button to control the LED; and one tells the LED to follow the<br>button's state.
It's hard to say which one is easier to read, write or understand. Maybe the last one, since it's a<br>single line. But if you wanted to reverse the logic, and make the button turn the LED off instead of<br>on, it would be trivial to work out how to change the first two, but not the third.
But that's where it gets interesting! We provide<br>functions for manipulating the stream of<br>values as it passes from one device to another:
led.source = negated(button)
and even for providing artificial value streams like a sine wave:
servo.source = sin_values()
This would drive the servo steadily between its minimum and maximum points.
The way all this works is by gpiozero assigning standard value ranges to devices. Some are simple<br>booleans, like the button and LED. The button is 1 when pressed, otherwise 0. For the LED, 0 is off<br>and 1 is on. We can use PWM to control the LED, allowing it to have its brightness set on a scale<br>from 0 to 1. And if we take an analogue input that reads 0 to 1, we can directly control the LED's<br>brightness from that device:
pot = MCP3008()<br>led = PWMLED(pin)
led.source = pot
Some devices go from -1 to 1, like a motor, where the 0 to 1 part is speed forwards, and the 0 to -1<br>part is speed backwards. So the sine wave would make it ramp up to full speed, slow down, reverse<br>direction, and ramp up to full speed the other way.
You might want to manipulate or combine value streams — so we provide a collection of functions for<br>common use cases, like negated, inverted, clamped, scaled, absoluted, booleanized, zipped, and<br>smoothed. And you can write your own generator functions too.
I used to regularly include this concept in my many conference talks while working at Raspberry Pi,<br>and I wrote a docs page explaining<br>it. This page showed how the relationship between devices could be expressed using simple<br>flow diagrams:
gpiozero flow
This led to an idea — a web-based UI where you could drag and drop GPIO devices from a side panel<br>into a freeform canvas, and draw lines between them to connect them. If you connected an LED to a<br>button, the button would control the LED, purely based on source/values.
There's a project called Node-RED that works this way, but I found it convoluted, and its GPIO<br>functionality was pin-based rather than device-focused. What I had in mind was based purely on how<br>gpiozero's source/values worked.
I knew what I wanted but there were too many complexities to manage, and it required a lot of<br>knowledge of modern JavaScript. So here we are in 2026 and I have Claude to make light work of such<br>things.
I wrote a description of the idea and a spec for the MVP — purely a simulator — and quickly had a<br>working prototype after agreeing on some tech and design choices. I spent some time tweaking the UI,<br>adding configuration, icons and animations. It soon became a fun tool to play around with:
We added a Python code generator so you could see the equivalent Python code for your project. We<br>then looked into methods of remotely controlling a Pi's GPIO pins, and wrote an agent to run on the<br>Pi. The web app communicated with the Pi using websockets, and it just worked.
We added and refined more features and functionality — supporting pretty much every device in<br>gpiozero, including internal devices like TimeOfDay, the range of source tools for manipulating<br>value streams, and artificial sources such as sin_values:
I wanted to support add-on boards, but that didn't lend itself well to source/values because the<br>shape of a composite device made up of input and output components makes it hard to use. However,<br>it's useful to be able to drag in a board you have and not have to assign all the specific pin<br>numbers it uses — so I included the boards but made them appear on the canvas as separate<br>components, pre-configured on the correct pins.
I thought it would be useful to be able to break up composite devices like traffic lights into their<br>component parts, and convert LEDs into PWMLEDs and back — so we added that. We added a limited<br>set of...