Releases: coredumpdev/photon
Release list
v0.7.2
Waterfall
addWaterfall(plot, opts) — a spectrogram that streams. Each pushed column becomes the newest row at the top and the history slides one row down, while the y axis reads as elapsed time.
const wf = addWaterfall(plot, {
extent: [0, 160_000], // the band one row spans
cols: 512, rows: 400, // cells across × rows of history
rowSeconds: 0.08, // seconds per row → 32s on screen
domain: [-68, -6], // fix it, or the colours breathe every push
colormap: "plasma", name: "power (dB)",
timeFormat: "hh:mm:ss", // or "mm:ss.mmm", or (s) => your own label
timeTitle: "time",
});
wf.push(psdInDb); // one column per step
wf.setTimeAxis({ format: "mm:ss.mmm" }); // relabel liveTwo things a hand-rolled version keeps getting wrong are handled inside: ticks are handed over as a fresh array on every push (an axis caches its resolved ticks while the domain and config hold still, and a waterfall's domain never moves — so a generator gets called once and the labels sit there while the image scrolls under them), and the clock starts one row negative so the first push lands on startTime exactly instead of leaving the top edge of the empty history a row above its own tick.
A column longer than cols is reduced by block maximum, so a peak two bins wide survives fitting a 200k-bin spectrum into a few hundred cells. history opens on a pre-computed grid instead of an empty one. Clock labels are wider than plain numbers and the left margin is not measured from them — pass margin: { left: 72 }.
Pure and unit tested: waterfallTimeTicks, formatDuration, niceTimeStep, blockMax.
Wrappers: a Waterfall component (react/vue/solid) and a waterfall series (svelte/gea/wc). Live streaming still goes through the handle on a core Plot, as with every other streaming layer.
Gallery
examples/vanilla gains a Signal tab — a 409.6 kHz receiver as a 204 800-point live spectrum over the waterfall, both full width on a shared frequency axis, with start/stop frequency boxes and a time-label picker. Its streaming updaters are now grouped per tab, so switching away from Dynamic no longer leaves those 50 panels running behind it.
v0.7.1
A bug-fix release. Every item here was a chart that drew nothing at all, with no error — the failure mode that is hardest to notice and hardest to report.
Python bridge
attention_mapworks with a 2-D matrix again. A numpy array was ravelled into one buffer so its shape never reached the browser, and a list of lists arrived as anArrayofFloat64Array, which the shape inference (Array.isArray(weights[0])) rejected. Both forms now render identically to the flat form.annotate("band", …)is reachable from Python.fromis a Python keyword, so the field is spelledfrom_and translated on the way out — previously there was no way to pass it.- Wrong field names raise instead of drawing nothing. A 3D box is sized with
w/h/d(notwidth/height/depth); aline/rayannotation runsx0,y0 → x1,y1(notx1,y1 → x2,y2);fibtakesx0,x1,high,low. Each now reports which fields the type takes, and the Python docstrings list them.
Core
- A polar series added after the first frame is drawn.
PolarPlot.addLine/addScatterrefit but never scheduled a render, so the series stayed invisible until something resized the container. toDataURL()on a hidden or detached container returns the chart, not a 1×1 blank PNG.- A chart that scrolls back into view is current, not stale. With
offscreenCullingon, a redraw is now unconditional on re-entry: an app that also skips its own data generation never calledrender()while away, so the catch-up never triggered.
Examples
- The svelte gallery's two linked-finance panels build from a
use:action rather thanonMount— Rollup was deleting the wholeonMount(...)call from the production bundle, leaving both panels empty with no error.
Housekeeping
plot.tsno longer contains literal NUL bytes, which madefile(1)classify it as binary andgrepsilently report no matches on the largest source file in the repo.
v0.7.0
Closes the two gaps left open at 0.6.0.
Triangulations — matplotlib's tri* family
For samples that are scattered rather than gridded.
| Photon | matplotlib |
|---|---|
addTriplot / pv.triplot |
triplot |
addTripcolor / pv.tripcolor |
tripcolor |
addTricontour / pv.tricontour |
tricontour |
addTricontourf / pv.tricontourf |
tricontourf |
pv.tripcolor(x, y, z, edges=True) # flat-shaded triangles + the mesh
pv.tricontourf(x, y, z, levels=12) # filled bands over the triangulationBacking them is a new delaunay(x, y): incremental insertion with Lawson edge flipping over a half-edge mesh. Points go in along a spatial order and each one is located by walking from the last triangle touched, so location stays near O(1) amortised instead of the O(n) scan a naive Bowyer–Watson does — 4000 points triangulate in tens of milliseconds. Zero-area slivers and exact duplicates are dropped rather than emitted, since a degenerate triangle divides by zero in every interpolation downstream.
Pass your own triangles when the connectivity is part of the data — a finite-element result, say — and no triangulation runs.
Binding parity — 32 builders, all six wrappers
The statistics pack (Regression, Ecdf, CorrMatrix, Psd), the ML pack (16, from ConfusionMatrix to LearningCurve), the diagram pack (Treemap through ParallelCoordinates), Drawdown, and the new tri* four existed in core but in no wrapper. All are now:
- components in React, Vue and Solid
type:entries in the Svelte, Gea and Web Component series specs- methods and one-liners in Python
Core exports collectLayers(handle) to make that safe: it finds the layers in whatever shape a builder returns — a bare layer, { upper, middle, lower }, { lines: Layer[], arrows? } — mixed in with computed data like auc or layout. Naming each handle's fields by hand across 96 call sites is how one gets forgotten.
Fixes
Six Python methods were written against guessed option names and rendered an error box rather than a chart. The browser pass caught all six; there is now a test pinning the key names the layers actually read (yTrue/yPred, pd, weights).
Verification
68 gallery panels and 32 Python chart types all draw with non-zero ink under headless Chromium. 217 TS tests — 13 new for the triangulator, including that no vertex falls inside any circumcircle and that the triangle count matches Euler's formula — and 43 Python tests.
Install
npm i @photonviz/core # or @photonviz/react, vue, svelte, solid, gea, wc
pip install photonvizFull changelog: v0.6.0...v0.7.0
v0.6.0
matplotlib ergonomics for the Python API
figsize is matplotlib's (width, height) in inches at dpi (100 by default), and works on any chart — not just a figure.
fig, axes = pv.subplots(2, 2, figsize=(12, 7), sharex=True, theme="dark")
axes[0, 0].line(t, loss).title("Loss")
axes[0, 1].roc_curve(scores, labels)
axes[1, 0].confusion_matrix(y_true, y_pred)
axes[1, 1].histogram(residuals)
figsubplots returns (figure, axes) with matplotlib's squeeze rules, so axes[i, j], axes[i] and axes.flat all behave. sharex / sharey link the panels' views. For layouts that aren't a uniform grid, pv.figure() + add_subplot(row, col, rowspan, colspan, kind) covers spans and mixed 2D / polar / 3D panels.
The grid is one widget, not one per panel: the drawing API moved onto plain Axes / Axes3D / PolarAxes spec objects, so a 2×2 figure ships a single copy of the ~280 KB bundled engine instead of four.
New chart types — matplotlib's field and raster gallery
| Photon | matplotlib |
|---|---|
addContourFilled / pv.contourf |
contourf |
addPcolormesh / pv.pcolormesh |
pcolormesh |
addStreamplot / pv.streamplot |
streamplot |
addBarbs / pv.barbs |
barbs |
addHist2d / pv.hist2d |
hist2d |
addEventPlot / pv.eventplot |
eventplot |
Filled bands are real polygons, not a quantised image: every cell is split into four triangles around its centre before clipping, which removes the saddle ambiguity plain marching squares has. Streamlines are RK4-traced with matplotlib's occupancy trick, so they stay evenly spaced instead of bunching on attractors. The pure halves — isobands, streamlines, hist2d — are exported too.
PlotGrid for JavaScript
const grid = new PlotGrid(el, { rows: 2, cols: 2, gap: 14, title: "Sensors", linkX: true });
grid.addPlot().addLine({ x, y: a });
grid.addPlot({ colSpan: 2 }).addLine({ x, y: total });
grid.addPolar({ row: 1, col: 1 }).addLine({ theta, r });Cells all draw through the one shared WebGL context, so a 4×4 figure costs no more GPU contexts than a single chart. linkY joins linkX as the y-view counterpart.
Fixes
pv.boxdrew nothing. It documented groups as{"x": …, "values": …}but the layer readsposition, so every vertex wasNaN— the gallery notebook's "Violin + Tukey box" cell rendered empty axes. Python accepts either key now, andBoxLayerthrows on a non-finite position instead of drawing nothing.addRenkowithoutbrickSizeproduced zero bricks; it throws now.- Series-spec wrappers leaked layers.
addSeriesreturned a single layer, so a multi-layer builder left the rest behind on rebuild (a model graph leaked its connectors). It returnsLayer[]now. addContourFilledreturns{ bands, lines? }so its optional stroke layer is removable.
Python API gaps closed
Nine chart types reachable from JS had no Python method: grouped_bars, stacked_bars, stacked_area, patches, graph, renko, depth, shap_beeswarm, and contour3d. training_curves also accepts "values" alongside the layer's "y".
Wrappers
<ContourFilled>, <Pcolormesh>, <Hist2d>, <EventPlot>, <Streamplot> and <Barbs> in React / Vue / Solid; matching type: entries in the Svelte / Gea / Web Component series specs. PlotGrid and linkY re-exported from all six.
Install
npm i @photonviz/core # or @photonviz/react, vue, svelte, solid, gea, wc
pip install photonvizFull changelog: v0.5.0...v0.6.0
v0.5.0
GPU charts that can now draw the model that produced the data — plus a Python
bridge, readable colour, and a documentation site.
Model architecture graphs
Draw a real model's layers, straight from PyTorch, Keras, scikit-learn or ONNX.
addModelGraph(plot, { graph })— a flat Netron-style DAG. Boxes are coloured
by layer family, and a residual edge that skips ranks routes around the
trunk instead of cutting through it.addModelGraph3D(plot3d, { graph })— one cuboid per layer, sized from its
output tensor: the visible face is H×W, the thickness is the channel count, so
a CNN reads as feature maps shrinking while depth grows.
Six pure adapters (modelGraphFromTorchFx, modelGraphFromKeras,
modelGraphFromSklearn, modelGraphFromOnnx, sequentialModel, mlpModel)
feed one shared layout — modelLayout is exported if you would rather draw it
yourself.
New: Boxes3DLayer (instanced lit cuboids), and on Plot3D — aspectMode: "data", projection: "orthographic", showAxes, and addLabel3D for outlined
text pinned in data space.
Python — pip install photonviz
An anywidget bridge for Jupyter Notebook, JupyterLab,
VS Code and Google Colab. NumPy arrays and torch tensors cross to the browser
as binary buffers, so a million points stay interactive in a cell.
```python
import numpy as np, photonviz as pv
pv.line(x, np.sin(x), name="signal", plot={"theme": "dark", "legend": True})
pv.model_graph_3d(torch_model, example_input=torch.randn(1, 3, 224, 224))
```
Two runnable notebooks under `examples/notebooks/`.
Colorbars, and colour worth trusting
- Colorbars are on by default. Any layer that maps values to colours —
heatmap, hexbin, contour, choropleth patches, `colorBy` scatter/quiver —
reports a scale, and the plot draws a bar for it. - 4 colormaps become 12 across sequential / diverging / cyclic, plus 4
categorical palettes (including the colour-vision-safe `okabe-ito`). - Bring your own: `registerColormap` / `registerPalette`, or pass inline
colours anywhere a name is accepted. - `symmetricDomain` centres a diverging scale so its neutral colour lands on
zero instead of drifting with the data.
Marks and interaction
- Bubble charts — per-point `sizes` and `colors` on scatter.
- Dashed lines — `dash: [6, 4]` for guides and forecasts.
- Interactive legend — click an entry to hide a series; the auto axes re-fit
to what is left. Keyboard-accessible, with `onVisibilityChange`.
Finance, ML, signal and statistics
- Finance: `cci`, `mfi`, `williamsR`, `aroon`, `donchian`, `parabolicSar`,
`pivotPoints`, `resampleOhlc`, `drawdown` + `addDrawdown`. - ML: `r2`, `rmse`, `mae`, `logLoss`, `brierScore`, `classificationReport`,
`liftCurve`, `rocCurveOvR` + `addPredVsActual`, `addResiduals`,
`addLiftCurve`, `addLearningCurve`. - Signal + statistics (new modules): window functions, Welch PSD,
Savitzky-Golay, cross-correlation, OLS/LOESS fits, ECDF, z-score,
correlation matrix + `addRegression`, `addEcdf`, `addCorrMatrix`, `addPsd`.
Documentation
A full site at https://coredumpdev.github.io/photon/docs/ — guides, the
chart catalog with live demos, and a TypeDoc API reference. Every demo is a
real module loaded twice, imported to run and read raw to display, so the code
on the page is what produced the chart above it.
Also
All six framework wrappers expose the new charts, dash and sizes/colors.
190 TypeScript tests and 22 Python tests.
v0.4.1
Docs: add a Docs for AI agents link (→ llms-full.txt) to every package README so it shows on the npm package pages, alongside the live-demo link. No code changes.
v0.4.0
✨ ML / deep-learning chart pack (@photonviz/core)
Classification metrics (confusion matrix, ROC + AUC, precision–recall + AP,
calibration + ECE), a PCA reducer + embedding projector, explainability
(feature importance, SHAP beeswarm, partial dependence, attention maps), and
training-monitoring charts (EMA-smoothed training curves, ridgeline). All
composed addX(plot, opts) builders; the pure metrics are unit-tested and the
whole pack is re-exported from every framework wrapper. New examples/ml app +
an ML tab in every gallery + playground presets.
⚠️ Breaking — maps decoupled, @photonviz/map removed
The @photonviz/map package has been removed. The framework wrappers no longer
depend on it and no longer ship <Map> / <GeoJson> components or map /
geojson series. If you need a basemap, add it imperatively on the core
Plot (addMap / addGeoJson) via onReady / usePlot / the Web
Components .plot getter.
🩹 Fixes
- Legend now shows only explicitly
named series — auto-id helper layers
(fills, ICE curves, raw pre-smoothing lines) no longer clutter it.
📚 Docs & discoverability
- Live demo + playground: https://coredumpdev.github.io/photon/
llms.txt/llms-full.txt+AGENTS.md— docs for AI coding agents.- npm / downloads / size badges,
CODE_OF_CONDUCT,SECURITY,FUNDING.
v0.3.2
Since v0.3.1:
✨ Features
- Image export — every plot (
Plot/Plot3D/PolarPlot) hastoDataURL()/toBlob()/downloadImage()/copyToClipboard(), plus a one-click download-PNG button on the toolbar (and next to reset-view on 3D). - Interactive drawing tools —
new Plot(el, { drawingTools: true })adds trendline / horizontal / ray / Fibonacci / rectangle tools. Drawings are editable: drag endpoint handles to reshape, drag the body to move, double-click to label, right-click for a context menu (rename / recolor / delete), Delete to remove. - 7 diagram chart types —
addTreemap·addFunnel·addSunburst·addGauge·addSankey·addChord·addParallelCoordinates(pure*Layoutfns exported too). - 7 more finance indicators — Stochastic, Keltner, OBV, Ichimoku, ADX, SuperTrend, Fibonacci retracements.
@photonviz/wc— new framework-agnostic Web Components package:<photon-plot>/<photon-plot3d>/<photon-polar>.- Data adapters —
parseCSV(text)→ a typedTable, andlttb(x, y, threshold)downsampling for long line series. - Accessibility — plots render as
role="img"with an auto-summarizedaria-label(ariaLabel/setAriaLabel()/describe()). - Examples — per-chart fullscreen button; a new interactive playground (CodeMirror editor) and a Web Components gallery.
🐛 Fixes
- Ordinal-time axis gridlines now snap to real calendar dates (month/week/day) and no longer drift when panning.
- Hover x-readout uses the axis scale's formatter (dates on time/ordinal-time instead of a raw index).
All 8 packages published to npm at 0.3.2.
v0.3.0
GPU-accelerated scientific plotting — a big feature release: a full styling/config system, a much larger 2D + 3D chart catalog, two new framework wrappers, and performance work. All 7 packages published to npm with provenance.
Highlights
Styling & config
background/borderfills, plottitle, DOMlegend- Per-axis line/tick/label/grid color, font & label rotation
- Categorical (factor) scale
New 2D charts
- Scatter marker glyphs (circle/square/triangle/diamond/cross/plus)
- Grouped / stacked / horizontal bars, stacked area
- Pie / donut, patches/polygons (earcut, choropleth), graph/network (force layout)
- Image (RGBA/URL), annotations (span/band/box/label)
Full 3D suite (Plot3D)
- New layers: line3d, bar3d, quiver3d, contour3d, isosurface (marching cubes), volume (GPU raymarch) + surface wireframe
- 3D chrome: legend, colorbar, title, hover tooltip + highlight ring, back-wall grid planes, reset-view, auto-rotate; per-point size/label + streaming
setData
Wrappers — now five frameworks
- Added @photonviz/solid and @photonviz/gea; every chart wrapped across React, Vue, Svelte, Solid & Gea
Performance
- LUT-backed colormaps (~14× faster hot path) + a
pnpm benchbenchmark suite
Housekeeping
- LICENSE + author/bugs in every package; new unit tests (earcut, marching cubes, force layout, categorical scale) — 98 total
Packages (all @0.3.0)
@photonviz/core · map · react · vue · svelte · solid · gea
v0.2.1
Per-package READMEs for npm, each with the Photon banner + gallery images (and the vector-map image for @photonviz/map). No API changes since v0.2.0.