phosphor#
GPU-accelerated real-time sweep renderer for multichannel timeseries data. Built on WebGPU and Qt, phosphor renders thousands of channels at high sample rates with minimal CPU overhead.
Designed for neuroscience and real-time signal monitoring – push (n_samples, n_channels) numpy arrays and phosphor handles downsampling, autoscaling, and rendering.
Installation#
pip install phosphor
Quick Start#
import numpy as np
from PySide6.QtWidgets import QApplication
from phosphor import SweepConfig, SweepWidget
app = QApplication([])
widget = SweepWidget(SweepConfig(
n_channels=128,
srate=30000.0,
display_dur=2.0,
n_visible=64,
))
widget.show()
# Push data from any source -- shape: (n_samples, n_channels), float32
widget.push_data(np.random.randn(500, 128).astype(np.float32))
app.exec()
Embedding in an Existing Qt Application#
SweepWidget is a standard QWidget that can be added to any layout:
from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget
from phosphor import SweepConfig, SweepWidget
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.sweep = SweepWidget(SweepConfig(n_channels=64, srate=1000.0))
self.setCentralWidget(self.sweep)
def on_new_data(self, data):
self.sweep.push_data(data)
Runtime Configuration#
Update parameters without recreating the widget:
from phosphor import SweepConfig
widget.update_config(SweepConfig(
n_channels=256,
srate=30000.0,
display_dur=4.0,
n_visible=128,
))
Built-in Demo#
python -m phosphor
python -m phosphor --channels 256 --srate 30000 --visible 64 --dur 2.0
Keyboard Controls#
Key |
Action |
|---|---|
|
Scroll channels by 1 |
|
Scroll channels by one page |
|
Halve / double visible channel count |
|
Y-axis zoom out / in (disables autoscale) |
|
Toggle autoscale |
|
Halve / double display duration |
Future: Migration to fastplotlib#
Phosphor currently uses custom WGSL shaders and raw wgpu-py for rendering. The plan is to migrate to fastplotlib (built on pygfx) once both libraries reach 1.0 (targeting mid-2026), which would eliminate the custom shader maintenance burden. The migration can happen in stages:
Keep
SweepBuffer– the CPU-side circular buffer with incremental min/max downsampling is the core of phosphor’s performance story. Neither pygfx nor fastplotlib provide built-in min/max downsampling for line data, so this logic stays.Replace
GPURenderer+ WGSL shaders with fastplotlib’sLineStack, feeding it the already-downsampled min/max columns fromSweepBuffer. This drops the custom pipeline/shader code and gains fastplotlib’s built-in axes, colormapping, and interaction tools.Evaluate whether
ChannelPlotWidgetcan be simplified or replaced by fastplotlib’sFigure/Subplotlayout, retaining the keyboard controls and channel pagination UX.
Development#
We use uv for development.
Fork and clone the repository
uv syncto create a virtual environment and install dependenciesuv run pre-commit installto set up linting and formatting hooksuv run pytest teststo run the test suiteSubmit a PR against the
devbranch
Contents: