# GlyphX **A next-generation Python visualization library — SVG-first, interactive, and built to replace Matplotlib, Seaborn, and Plotly.** [](https://github.com/kjkoeller/glyphx/actions/workflows/ci_tests.yml) [](https://glyphx.readthedocs.io/en/latest/?badge=latest) [](https://badge.fury.io/py/glyphx) [](https://github.com/kjkoeller/glyphx/releases/) [](LICENSE) [](https://www.python.org/downloads/) GlyphX renders crisp, interactive SVG charts that work everywhere — Jupyter notebooks, CLI pipelines, FastAPI servers, and static HTML files — with zero configuration and no `plt.show()` required. View the documentation page [here](https://glyphx.readthedocs.io/en/latest/index.html) for more detailed explanations of everything. --- ## Why GlyphX? | Feature | GlyphX | Matplotlib | Seaborn | Plotly | |---|:---:|:---:|:---:|:---:| | Auto-display (no `show()`) | ✅ | ❌ | ❌ | ❌ | | Method chaining API | ✅ | ❌ | ❌ | Partial | | DataFrame accessor (`df.glyphx.*`) | ✅ | ❌ | Partial | ❌ | | Natural language chart generation | ✅ | ❌ | ❌ | ❌ | | Linked interactive brushing | ✅ | ❌ | ❌ | ✅ (needs server) | | Self-contained shareable HTML | ✅ | ❌ | ❌ | ❌ | | Statistical significance brackets | ✅ | ❌ | ❌ | ❌ | | ECDF plot | ✅ | ❌ | ✅ | ❌ | | Raincloud plot | ✅ | ❌ | ❌ | ❌ | | 3-D scatter / surface / line / bar | ✅ (WebGL + SVG) | ❌ | ❌ | ✅ (WebGL) | | Bubble chart | ✅ | ✅ | ❌ | ✅ | | Sunburst chart | ✅ | ❌ | ❌ | ✅ | | Parallel coordinates | ✅ | ❌ | ✅ | ✅ | | Diverging bar | ✅ | ❌ | ❌ | ✅ | | Auto large-data downsampling (SVG) | ✅ M4+LTTB+voxel | Rasterises | ❌ | ❌ | | Perceptually-uniform colormaps | ✅ (9 built-in) | ✅ | ✅ | ✅ | | Continuous color encoding (scatter) | ✅ | ✅ | ✅ | ✅ | | Candlestick / OHLC | ✅ | ❌ | ❌ | ✅ | | Waterfall / bridge chart | ✅ | ❌ | ❌ | ✅ | | Treemap (squarified) | ✅ | ❌ | ❌ | ✅ | | Streaming / real-time series | ✅ (no server) | ❌ | ❌ | ✅ (needs server) | | Synchronized crosshair | ✅ | ❌ | ❌ | ✅ (needs server) | | PPTX export | ✅ | ❌ | ❌ | ❌ | | CLI tool (`glyphx plot data.csv`) | ✅ | ❌ | ❌ | ❌ | | Full ARIA / WCAG 2.1 AA accessibility | ✅ | ❌ | ❌ | Partial | | Full type annotations (`py.typed`) | ✅ | ❌ | ❌ | Partial | | `tight_layout()` | ✅ auto | Manual | Auto | Auto | | Log-scale axes | ✅ | ✅ | ✅ | ✅ | | Dual Y-axis | ✅ | ✅ | ❌ | ✅ | | Error bars (X and Y) | ✅ | ✅ | ✅ | ✅ | --- ## Installation ```bash pip install glyphx # Optional extras pip install "glyphx[export]" # PNG/JPG raster export (cairosvg) pip install "glyphx[pptx]" # PowerPoint export (python-pptx + cairosvg) pip install "glyphx[nlp]" # Natural language charts (anthropic) pip install "glyphx[all]" # Everything ``` **Requirements:** Python 3.12+ · NumPy ≥ 1.26 · pandas ≥ 2.1 --- ## Quick Start ```python from glyphx import plot # One-liner — auto-displays in Jupyter, opens browser in CLI plot([1, 2, 3], [4, 5, 6], kind="line", title="My First Chart") ``` ```python from glyphx import Figure from glyphx.series import LineSeries, BarSeries months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"] revenue = [120, 145, 132, 178, 159, 203] costs = [ 80, 90, 85, 105, 98, 115] fig = ( Figure(width=800, height=500) .set_title("Revenue vs Costs — H1") .set_theme("dark") .set_xlabel("Month") .set_ylabel("USD (thousands)") .add(LineSeries(months, revenue, color="#60a5fa", label="Revenue")) .add(LineSeries(months, costs, color="#f87171", label="Costs", linestyle="dashed")) .add_stat_annotation("Jan", "Jun", p_value=0.004) .set_legend("top-left") .tight_layout() ) fig.show() # Jupyter inline or browser tab fig.save("chart.svg") # SVG vector fig.save("chart.html") # Interactive HTML fig.save("chart.png") # Raster PNG (requires cairosvg) fig.save("chart.pptx") # PowerPoint (requires glyphx[pptx]) fig.share("report.html") # Zero-CDN self-contained HTML ``` --- ## Core APIs ### `plot()` — One-liner charts The fastest path to any chart type. Mirrors pandas' `df.plot()`: ```python from glyphx import plot plot([1,2,3], [4,5,6], kind="line", title="Line") plot(["A","B","C"], [10,20,15], kind="bar", title="Bar") plot([1,2,3], [4,5,6], kind="scatter", title="Scatter") plot(data=[30, 40, 30], kind="pie", labels=["A","B","C"]) plot(data=[30, 40, 30], kind="donut", labels=["A","B","C"]) plot(data=raw_values, kind="hist", bins=20) plot(data=raw_values, kind="box") plot(data=matrix, kind="heatmap") ``` ### Method-Chaining API Every method returns `self`. Build the entire chart in one expression: ```python fig = ( Figure(width=900, height=520, theme="warm") .set_title("Q3 Performance") .set_xlabel("Month").set_ylabel("Revenue ($M)") .set_legend("bottom-right") .add(LineSeries(x, revenue, label="Revenue")) .add(BarSeries(x, costs, label="Costs"), use_y2=True) .annotate("Record High", x=10, y=5.4, arrow=True, color="#dc2626") .add_stat_annotation("Jan", "Jun", p_value=0.001) .vline(x=6, color="#888", linestyle="dashed") .hline(y=5.0, color="#888", linestyle="dotted") .tight_layout() .share("dashboard.html") ) ``` ### DataFrame Accessor Import `glyphx` once — every `pd.DataFrame` gains `.glyphx`: ```python import pandas as pd import glyphx # registers accessor automatically df = pd.read_csv("sales.csv") # One-liner charts from column names df.glyphx.line(x="date", y="revenue", title="Daily Revenue") df.glyphx.bar( x="product", y="sales", title="Sales by Product") df.glyphx.scatter(x="spend", y="revenue") df.glyphx.hist(col="response_time", bins=20) df.glyphx.box(col="score", groupby="region") df.glyphx.pie(labels="category", values="share") df.glyphx.donut(labels="segment", values="revenue") df.glyphx.heatmap(title="Correlation Matrix") # Groupby aggregation df.glyphx.bar(groupby="region", y="revenue", agg="sum", title="Revenue by Region") # Hue splitting — one BarSeries per unique region value, auto-colored df.glyphx.bar(x="month", y="revenue", hue="region", title="Revenue by Month and Region") # Full chain from the accessor (df.glyphx .bar(x="month", y="revenue", auto_display=False) .set_theme("dark") .add_stat_annotation("Jan", "Jun", p_value=0.002) .share("report.html")) ``` ### Natural Language Charts Describe a chart in plain English; GlyphX builds it. Requires `pip install "glyphx[nlp]"` and `ANTHROPIC_API_KEY`. ```python from glyphx import from_prompt import pandas as pd df = pd.read_csv("sales.csv") # GlyphX infers chart type, axis mapping, grouping, theme, and title fig = from_prompt("bar chart of total revenue by region, dark theme", df=df) # Without a DataFrame — generates illustrative sample data fig = from_prompt("scatter plot showing a strong positive correlation") # Complex intent fig = from_prompt( "top 10 products by revenue this quarter, sorted descending", df=df, ) ``` --- ## Chart Types ### Core charts ```python from glyphx.series import ( LineSeries, BarSeries, ScatterSeries, PieSeries, DonutSeries, HistogramSeries, BoxPlotSeries, HeatmapSeries, ) # Line — multiple linestyles, error bars LineSeries(x, y, color="#2563eb", label="Revenue", linestyle="dashed", # solid | dashed | dotted | longdash | step width=2, yerr=error_values, # symmetric Y error bars with caps xerr=x_error_values, # symmetric X error bars ) # Bar — error bars, per-bar color list BarSeries(x, y, color="#7c3aed", label="Units", bar_width=0.7, yerr=std_errors, ) # Scatter — continuous color encoding ScatterSeries(x, y, c=z_values, # per-point values → colormap cmap="viridis", # any of 9 built-in colormaps size=6, marker="circle", # circle | square ) # Histogram HistogramSeries(data, bins=20, color="#0891b2") # Box plot — single or multi-group BoxPlotSeries([group_a, group_b, group_c], categories=["Control", "Drug A", "Drug B"], box_width=24, ) # Heatmap — colorbar, row/col labels, value overlay HeatmapSeries(matrix, row_labels=row_names, col_labels=col_names, show_values=True, cmap=["#1e40af", "#f0f0f0", "#b91c1c"], # custom diverging ) ``` ### Statistical ```python # ECDF — no bin-width choice needed, shows full distribution from glyphx.ecdf import ECDFSeries fig.add(ECDFSeries(data, label="Control", complementary=False)) fig.add(ECDFSeries(data2, label="Treatment")) # KDE — smooth density curve (no scipy required) from glyphx.kde import KDESeries fig.add(KDESeries(data, filled=True, alpha=0.20, label="Density")) # Area / fill-between from glyphx.fill_between import FillBetweenSeries fig.add(FillBetweenSeries(x, y_lower, y_upper, color="#2563eb", alpha=0.25, label="95% CI")) # Raincloud — jitter + half-violin + box in one plot from glyphx.raincloud import RaincloudSeries fig.add(RaincloudSeries( data=[control, drug_a, drug_b], categories=["Control", "Drug A", "Drug B"], violin_width=35, )) # Violin plot from glyphx.violin_plot import ViolinPlotSeries fig.add(ViolinPlotSeries([grp_a, grp_b], show_median=True, show_box=True)) # Statistical significance brackets (built-in, no extra package) fig.add_stat_annotation("Control", "Drug A", p_value=0.001) # → *** fig.add_stat_annotation("Control", "Drug B", p_value=0.031, y_offset=30) # stack fig.add_stat_annotation("Drug A", "Drug B", p_value=0.18, style="numeric") ``` ### Financial ```python # Candlestick / OHLC from glyphx.candlestick import CandlestickSeries fig.add(CandlestickSeries( dates=["Mon", "Tue", "Wed", "Thu", "Fri"], open= [150, 153, 149, 155, 158], high= [155, 157, 153, 160, 162], low= [148, 151, 146, 154, 156], close=[153, 149, 155, 158, 160], )) # Waterfall / bridge chart from glyphx.waterfall import WaterfallSeries fig.add(WaterfallSeries( labels=["Q2 Revenue", "New Sales", "Upsell", "Churn", "Q3 Revenue"], values=[8_200_000, 2_100_000, 650_000, -420_000, None], # None = auto-total show_values=True, )) ``` ### Hierarchical ```python # Treemap — squarified layout, area-proportional rectangles from glyphx.treemap import TreemapSeries fig.add(TreemapSeries( labels=["Cloud", "AI", "Mobile", "Security", "Data"], values=[4200, 3100, 2800, 2100, 1900], cmap="viridis", show_values=True, )) ``` ### 3-D Charts Use ``Figure3D`` for interactive Three.js WebGL output with an SVG fallback. All 3-D series support per-series ``threshold=`` and expose ``last_downsample_info`` after rendering. ```python from glyphx import Figure3D, plot3d from glyphx.scatter3d import Scatter3DSeries from glyphx.surface3d import Surface3DSeries from glyphx.line3d import Line3DSeries from glyphx.bar3d import Bar3DSeries from glyphx.contour import ContourSeries import numpy as np # 3-D scatter with colormap fig = Figure3D(title="Clusters", theme="dark", azimuth=45, elevation=30) fig.add(Scatter3DSeries(xs, ys, zs, c=zs, cmap="plasma", size=4)) fig.show() # WebGL interactive; .save("chart.html") for sharing # 3-D surface — auto-decimated for large grids x = np.linspace(-3, 3, 200) y = np.linspace(-3, 3, 200) Z = np.sin(np.sqrt(x[None,:]**2 + y[:,None]**2)) Figure3D().add(Surface3DSeries(x, y, Z, cmap="viridis", wireframe=True)).show() # Helix polyline t = np.linspace(0, 4*np.pi, 2000) Figure3D().add(Line3DSeries(np.cos(t), np.sin(t), t/(4*np.pi))).show() # One-liner plot3d(xs, ys, zs, kind="scatter", title="Quick 3D") plot3d(x, y, Z, kind="surface", title="Quick Surface") ``` ### New Chart Types (v1.5+) ```python # Bubble — scatter with size encoding from glyphx.bubble import BubbleSeries fig.add(BubbleSeries(x, y, sizes=market_cap, c=growth, cmap="plasma")) # Sunburst — multi-ring hierarchy from glyphx.sunburst import SunburstSeries fig.add(SunburstSeries(labels=[...], parents=[...], values=[...])) # Parallel coordinates from glyphx.parallel_coords import ParallelCoordinatesSeries fig.add(ParallelCoordinatesSeries(data=df[cols], labels=df["species"])) # Diverging bar from glyphx.diverging_bar import DivergingBarSeries fig.add(DivergingBarSeries(categories=[...], values=[12,-8,21,-3])) ``` ### Streaming / Real-Time ```python from glyphx.streaming import StreamingSeries fig = Figure(title="Live Sensor Feed") stream = StreamingSeries(max_points=100, color="#7c3aed", label="Sensor") fig.add(stream) # Manual push stream.push(42.0) stream.push_many([41.5, 42.3, 43.1]) # Jupyter live mode — re-renders at target FPS, no server needed with stream.live(fig, fps=10) as s: for reading in sensor_generator(): s.push(reading) ``` --- ## Large-Data Downsampling GlyphX automatically keeps SVG files fast on large datasets. All algorithms are fully vectorised with NumPy. | Series type | Algorithm | Threshold | |---|---|---| | `LineSeries` | Two-stage M4 → LTTB | M4 at 50k pts; LTTB at 5k pts | | `ScatterSeries` | 2-D voxel grid thinning | 5k pts | | `Line3DSeries` | LTTB in screen space (camera-aware) | 5k pts | | `Scatter3DSeries` | 3-D voxel grid thinning | 5k pts | | `Surface3DSeries` | Grid decimation + face culling | 5k faces | ```python # Per-series threshold override from glyphx.series import LineSeries ls = LineSeries(x, y, threshold=1_000) # keep at most 1 000 points # Inspect what happened after render ls_info = ls.last_downsample_info # {'algorithm': 'M4+LTTB', 'original_n': 200000, 'thinned_n': 1000} # Global kill-switch (thread-local — safe for multi-threaded renderers) import glyphx.downsample as ds ds.disable() # no downsampling on this thread fig.render_svg() ds.enable() # Manual use of any algorithm from glyphx.downsample import lttb, m4, voxel_thin_2d, lttb_3d, decimate_grid x_down, y_down = lttb(x, y, threshold=2_000) x_m4, y_m4 = m4(x, y, pixel_width=800) xt, yt, ct = voxel_thin_2d(xs, ys, c=labels, max_points=5_000) ``` See the [Downsampling docs](https://glyphx.readthedocs.io/en/latest/downsampling.html) for the full API, benchmark results, and the test suite. --- ## Interactivity All charts rendered to HTML include: | Interaction | How | |---|---| | **Tooltips** | Hover any data point | | **Zoom** | Mouse wheel | | **Pan** | Click and drag | | **Reset zoom** | Double-click | | **Linked brushing** | `Shift` + drag — filters all charts on the page | | **Keyboard navigation** | `Tab` / `Arrow` keys between data points | | **Legend toggle** | Click a legend item to show/hide its series | | **Export** | SVG / PNG buttons in the toolbar | | **Synchronized crosshair** | `fig.enable_crosshair()` | ### Linked Brushing Hold `Shift` and drag a selection rectangle on any chart. All charts on the page with matching X values highlight together and dim non-matching points. Press `Escape` to clear. ```python from glyphx.layout import grid f1 = Figure(auto_display=False).add(ScatterSeries(x, y1, label="Sales")) f2 = Figure(auto_display=False).add(LineSeries(x, y2, label="Revenue")) html = grid([f1, f2], rows=1, cols=2) open("dashboard.html", "w").write(html) ``` --- ## Advanced Layout ```python # Dual Y-axis fig.add(LineSeries(x, prices, label="Price (left)")) fig.add(BarSeries(x, volume, label="Volume (right)"), use_y2=True) # Log-scale axes fig = Figure(yscale="log") fig = Figure(xscale="log", yscale="log") # Subplot grid fig = Figure(rows=2, cols=2, width=1000, height=700) ax0 = fig.add_axes(0, 0); ax0.add_series(LineSeries(x, y)) ax1 = fig.add_axes(0, 1); ax1.add_series(BarSeries(cats, vals)) ax2 = fig.add_axes(1, 0); ax2.add_series(ScatterSeries(x, y2)) ax3 = fig.add_axes(1, 1); ax3.add_series(HistogramSeries(data)) # Reference lines fig.vline(x=50, color="#e11d48", linestyle="dashed") fig.hline(y=3.5, color="#0284c7", linestyle="dotted") # Text annotations with optional arrows fig.annotate("Peak", x=10, y=5.4, arrow=True, color="#dc2626", font_size=12) fig.annotate("Baseline", x=0, y=2.0, anchor="start") # Auto tight layout (adjusts padding, rotates crowded X labels) fig.tight_layout() ``` --- ## Colormaps Nine perceptually-uniform colormaps: | Name | Type | Best for | |---|---|---| | `viridis` | Sequential | Default continuous encoding | | `plasma` | Sequential | High-contrast continuous | | `inferno` | Sequential | Print-safe dark backgrounds | | `magma` | Sequential | Heatmaps and density | | `cividis` | Sequential | Deuteranopia-safe | | `coolwarm` | Diverging | Correlation matrices | | `rdbu` | Diverging | Positive / negative values | | `spectral` | Multi-hue | Categorical ranges | | `greys` | Sequential | Monochrome / print export | ```python from glyphx.colormaps import apply_colormap, colormap_colors, list_colormaps apply_colormap(0.75, "plasma") # → "#eb5f34" colormap_colors("viridis", 6) # → list of 6 hex colors list_colormaps() # → ["cividis", "coolwarm", ...] # Color-encode scatter by a third variable ScatterSeries(x, y, c=z_values, cmap="inferno") ``` --- ## Themes Seven built-in themes: ```python Figure(theme="default") # clean white background Figure(theme="dark") # charcoal background Figure(theme="colorblind") # Okabe-Ito palette — safe for all color vision types Figure(theme="pastel") # soft, presentation-friendly Figure(theme="warm") # earthy tones, Georgia serif font Figure(theme="ocean") # blue palette, light blue background Figure(theme="monochrome") # grayscale, print-safe # Custom theme dict Figure(theme={ "colors": ["#ff6b6b", "#4ecdc4", "#45b7d1"], "background": "#1a1a2e", "text_color": "#eeeeee", "axis_color": "#555555", "grid_color": "#333333", "font": "Roboto, sans-serif", }) # Mid-chain theme swap fig.set_theme("dark") ``` > **Accessibility note:** The `colorblind` theme uses the [Okabe-Ito palette](https://jfly.uni-koeln.de/color/) — the scientific standard for color-vision-deficiency-safe visualization. It is safe for deuteranopia, protanopia, and tritanopia. --- ## Export Options ```python fig.save("chart.svg") # SVG vector — scales to any size fig.save("chart.html") # interactive HTML with tooltips, zoom, export buttons fig.save("chart.png") # raster PNG (requires: pip install "glyphx[export]") fig.save("chart.jpg") # raster JPG (requires: pip install "glyphx[export]") fig.save("chart.pptx") # PowerPoint slide (requires: pip install "glyphx[pptx]") # Self-contained HTML — all JS inlined, works fully offline html_str = fig.share() # returns string html_str = fig.share("report.html") # also writes to disk html_str = fig.share(title="Q3 Report") # custom