A lightweight Python library for driving and simulating Neopixel / WS281x-style LED strips. The project provides device abstractions, color utilities, common effects, and demo/test scripts so you can run and prototype LED animations on real hardware or in a console simulator.
- Simple
Neopixeldevice abstraction and buffer helpers. - Multiple device backends and simulations (SPI, RPi SPI, console simulation).
- Color utilities and conversions for common color spaces (RGB, HSV, and HLS are currently implemented).
- Built-in effects (Fire, Meteor, etc.) and a test/demo harness.
- Supports multi-dimensional pixel arrangements like 2D display areas, 3D volumetric displays, or even higher dimensions.
- Python 3.8+
- See
requirements.txtfor optional dependencies.
Clone the repository and install dependencies (optional):
git clone <repo-url>
cd Neopixel
python -m pip install -r requirements.txtThis library is designed to be run on Linux/Raspberry Pi for SPI-backed devices, but includes simulation devices for development as well as an abstract device base class to implement custom hardware on other platforms.
- Run the demo and tests (console simulation or real device depending on configuration):
python demo_and_test.py- Use the library from your own script:
from neopixel_classes import Neopixel
from devices import ConsoleSimulationDevice
from colors import ColorMode
# Create a Neopixel instance (30 pixels). Use `auto_write=True` to update on each change.
np = Neopixel(30, color_mode=ColorMode.HSV, auto_write=False)
# Attach a device (console simulation for development):
dev = ConsoleSimulationDevice()
np.to(dev) # attach the device
# Write pixels (values are floats in range 0.0..1.0) and push to the device
np.fill((0.0, 1.0, 1.0)) # fill cyan in HSV color space
# or
np.RGB[:] = (0.0, 0.0, 1.0) # fill blue in RGB color space
np.show()Adjust the above to match your concrete device class from devices.py or rpi_devices.py (see RpiSpiDev for Raspberry Pi SPI usage).
- Console simulation (development, no hardware required):
from neopixel_classes import Neopixel
from devices import ConsoleSimulationDevice
from colors import ColorMode
np = Neopixel(30, color_mode=ColorMode.RGB, auto_write=False).to(ConsoleSimulationDevice())
np.fill((0.2, 0.0, 0.8)) # purple-ish (floats 0..1)
np.show()- Raspberry Pi SPI device (real hardware):
from neopixel_classes import Neopixel
from rpi_devices import RpiSpiDev
from colors import ColorMode
np = Neopixel(60, auto_write=False, color_mode=ColorMode.RGB)
dev = RpiSpiDev(device=0) # device 0 or 1
np.to(dev)
np.fill((0.0, 0.0, 1.0)) # blue
np.show()- Auto-write and batch updates:
# immediate updates on change
np.auto_write = True
np[0:5] = (1.0, 0.0, 0.0) # first five pixels become red immediately
# batched updates (useful when making many changes)
np.begin_update()
np[0] = (1.0, 0.0, 0.0)
np[1] = (0.0, 1.0, 0.0)
np.end_update() # triggers a single update/show when done- Multi-dimensional pixel arrays:
from neopixel_classes import Neopixel
from rpi_devices import RpiSpiDev
from colors import ColorMode, GAMMA
from PIL import Image
# create 4 quadratic areas, 8x8 each on a 256 pixel stripe
# stacked on top of each other will build a volumetric display
neo = Neopixel((4, 8, 8), color_mode=ColorMode.RGB)
dev = RpiSpiDev(device=0, gamma_function=GAMMA['srgb']) # device 0 or 1, use sRGB gamma correction
neo.to(dev)
neo[0] = (0.0, 0.0, 1.0) # 1st area blue
neo[1] = (1.0, 0.0, 0.0) # 2nd area red
neo[2] = (0.0, 1.0, 0.0) # 3rd area green
neo[3] = Image.open('image.png') # 4th area displays a PIL Image, it will be resized to 8x8 automatically
neo() # or neo.show()
# vertical rainbow on area 0
neo.HSV.F[0] = neo.create_gradient((0, 1, 1), (1, 1, 1), 64)
neo()
# horizontal rainbow on area 2
neo.HSV.T.F[2] = neo.create_gradient((0, 1, 1), (1, 1, 1), 64)
neo()- Using built-in effects:
from effects import Fire
from time import sleep
np = Neopixel(60, auto_write=False)
dev = ConsoleSimulationDevice()
np.to(dev)
fire = Fire(np)
while True:
fire.progress()
sleep(0.02)See devices.py, rpi_devices.py, and effects.py for more examples and options.
Key modules and where to look:
- Neopixel core: neopixel_classes.py —
Neopixel,NeopixelDevice, helpers and buffer utilities. - Devices / backends: devices.py — SPI base classes and console simulations.
- Raspberry Pi SPI driver: rpi_devices.py —
RpiSpiDevSPI-backed device. - Colors & enums: colors.py —
ColorMode,PixelOrder, and color helpers. - Color conversions: color_conversions.py — conversion utilities between color spaces.
- Effects: effects.py — built-in
NeopixelEffectsubclasses likeFire,Meteor. - Demo / tests: demo_and_test.py — example usage and quick manual tests.
- Use
ConsoleSimulationDevicefromdevices.pyto iterate effects without hardware. - Use
RpiSpiDevfromrpi_devices.pyon a Raspberry Pi with SPI enabled to drive real strips.
from neopixel_classes import NeopixelDevice
class CustomDevice(NeopixelDevice):
def __init__(self, *,
pixel_order: PixelOrder = PixelOrder.GRB,
gamma_function: Callable | None = None,
**kwargs
) -> None:
super().__init__(pixel_order=pixel_order, gamma_function=gamma_function, **kwargs)
# do your stuff here to initialize the device, allocating buffer etc
def open_(self, neopixel: Neopixel) -> Any:
"""Open the device and return device-specific data."""
super().open_(neopixel)
# store any desired data to be passed to write_to_device()
return device_data
def close_(self) -> Any:
"""Close the Neopixel device. Override this method in subclasses to implement device-specific closing logic."""
return super().close_()
def _write_buffer(self, buffer: NDArray[np.float32], device_data: Any) -> Any:
"""Modify the pixel buffer in its initial shape before it gets flattened and written to the device.
You do not need to override this method if this is not necessary."""
return super()._write_buffer(buffer, device_data)
def write_to_device(self, buffer: NDArray[np.float32], device_data: Any) -> Any:
"""Write the given buffer to the Neopixel device.
Override this method in subclasses to implement device-specific writing logic.
:param buffer: A 2D array of shape (num_pixels, num_channels=4) containing pixel values.
:type buffer: np.ndarray[float32]
:param device_data: Optional device-specific data from the Neopixel instance (returned from open_).
:type device_data: Any
"""
# implement your logic to write the data to your deviceContributions, bug reports and improvements are welcome. Please open issues or pull requests.
This project is provided under the terms of the included LICENSE file.