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.
pip install phosphorimport 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()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)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,
))python -m phosphor
python -m phosphor --channels 256 --srate 30000 --visible 64 --dur 2.0| Key | Action |
|---|---|
Up / Down |
Scroll channels by 1 |
Page Up / Page Down |
Scroll channels by one page |
[ / ] |
Halve / double visible channel count |
- / = |
Y-axis zoom out / in (disables autoscale) |
A |
Toggle autoscale |
, / . |
Halve / double display duration |
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.
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 suite- Submit a PR against the
devbranch