@@ -70,90 +70,103 @@ const examples = [
7070 section : "Performance" ,
7171 title : "Million Point Line + Custom X + LOD" ,
7272 text : "Explicit x/y buffers, WASM min/max LOD, and callback-driven hover/click/selection inspection." ,
73+ pythonCode : 'p = ip.Plot(width=1000, height=420, title="Line + LOD")\nh = p.line("signal", y, x=x)\np.on_hover(lambda plot, e: print(e["index"], e["x"], e["y"]))\np.on_select(lambda plot, e: plot.indices_for_selection(e, h))\np' ,
7374 code : 'const h = plot.line("signal", y, { x });\nplot.onHover(console.log);\nplot.onSelection((e) => plot.indicesForSelection(e, h));' ,
7475 } ,
7576 {
7677 id : "streaming-plot" ,
7778 section : "Performance" ,
7879 title : "Realtime Streaming Ring Buffer" ,
7980 text : "Append explicit x/y chunks into a fixed-capacity line without recreating the plot object." ,
81+ pythonCode : 'p = ip.Plot(width=1000, height=360, title="Realtime")\nh = p.stream_line("ticks", capacity=12000, initial=y0, initial_x=x0, auto_render=True)\nh.append(chunk, x=chunk_x)\nh.pause(); h.resume()\np' ,
8082 code : 'const h = plot.streamLine("ticks", { capacity: 12000, x: initialX });\nh.append(chunk, { x: chunkX });\nh.pause(); h.resume();' ,
8183 } ,
8284 {
8385 id : "scatter-plot" ,
8486 section : "Points" ,
8587 title : "Scatter + Bubble Encodings" ,
8688 text : "Point-cloud rendering with explicit x/y data and bubble-size encodings for dense browser workflows." ,
89+ pythonCode : 'p = ip.Plot(width=1000, height=420, title="Scatter + Bubbles")\np.scatter("samples", y, x=x, size=2.5)\np.bubbles("volume", y, sizes, x=x)\np' ,
8790 code : 'plot.scatter("samples", y, { x });\nplot.bubbles("volume", y, sizes, { x });' ,
8891 } ,
8992 {
9093 id : "curve-plot" ,
9194 section : "Curves" ,
9295 title : "Stairs, Stems, Digital, Shaded, Error Bars" ,
9396 text : "Signal-analysis overlays in one canvas: stepped series, impulses, bands, digital states, and uncertainty." ,
97+ pythonCode : 'p = ip.Plot(width=1100, height=420, title="Signal Overlays")\np.stairs("step", y, x=x)\np.stems("stem", impulses, x=x)\np.digital("state", states, x=x)\np.shaded("band", lower, upper, x=x)\np.error_bars("fit", fit, err=err, x=x)\np' ,
9498 code : 'plot.stairs("step", y, { x });\nplot.shaded("band", lower, upper, { x });\nplot.errorBars("fit", y, { x, err });' ,
9599 } ,
96100 {
97101 id : "bars-plot" ,
98102 section : "Categorical" ,
99103 title : "Bars, Grouped Bars, Horizontal Bars" ,
100104 text : "Vertical bars, grouped categories, and horizontal rankings across ImPlot subplots." ,
105+ pythonCode : 'sp = ip.Subplots(1, 3, width=1100, height=360, title="Bars")\nsp.subplot(0, 0).bars("sales", values)\nsp.subplot(0, 1).bar_groups(["A", "B", "C"], matrix)\nsp.subplot(0, 2).bars_h("rank", values)\nsp' ,
101106 code : 'plot.setSubplots(1, 3);\nplot.bars("sales", values);\nplot.barGroups(labels, matrix);\nplot.barsH("rank", values);' ,
102107 } ,
103108 {
104109 id : "distribution-plot" ,
105110 section : "Statistics" ,
106111 title : "Histogram + 2D Histogram" ,
107112 text : "1D and 2D distributions with colorbar-backed density inspection." ,
113+ pythonCode : 'p = ip.Plot(width=1100, height=420, title="Distributions")\np.histogram("returns", values, bins=80)\np.histogram2d("density", x, y, x_bins=80, y_bins=60, show_colorbar=True)\np' ,
108114 code : 'plot.histogram("returns", values, { bins: 80 });\nplot.histogram2d("density", x, y, { xBins: 80, yBins: 60 });' ,
109115 } ,
110116 {
111117 id : "heatmap-image-plot" ,
112118 section : "Matrices" ,
113119 title : "Heatmap + Image" ,
114120 text : "Matrix and image plotting with empty heatmap labels, colorbar formatting, and float RGB buffers." ,
121+ pythonCode : 'p = ip.Plot(width=1100, height=420, title="Heatmap + Image")\np.set_colormap("Viridis")\np.heatmap("z", matrix, label_fmt="", show_colorbar=True, colorbar_format="%.2f")\np.image("rgb", image, bounds=((0, 0), (cols, rows)))\np' ,
115122 code : 'plot.setColormap("Viridis");\nplot.heatmap("z", matrix, { rows, cols, labelFmt: "" });\nplot.image("rgb", image, { rows, cols, channels: 3 });' ,
116123 } ,
117124 {
118125 id : "overlays-plot" ,
119126 section : "Overlays" ,
120127 title : "Annotations, Tags, Text, Infinite Lines, Pie" ,
121128 text : "Thresholds, callouts, labels, tags, and pie-chart composition using ImPlot primitives." ,
129+ pythonCode : 'p = ip.Plot(width=1100, height=420, title="Overlays")\np.vlines("events", xs)\np.hlines("limits", ys)\np.tag_y(0.0, label_fmt="zero")\np.annotation("peak", x0, y0)\np.pie_chart("mix", values, labels=labels, x=8, y=0, radius=1)\np' ,
122130 code : 'plot.vlines("events", xs);\nplot.tagY(0, { labelFmt: "zero" });\nplot.annotation("peak", x, y);\nplot.pieChart("mix", values, { labels });' ,
123131 } ,
124132 {
125133 id : "axes-plot" ,
126134 section : "Axes" ,
127135 title : "Axis Labels, Formats, Ticks, Log Scale, Secondary Axis" ,
128136 text : "Secondary axes, custom ticks, numeric formats, linked axes, and log/time scale controls." ,
137+ pythonCode : 'p = ip.Plot(width=1100, height=420, title="Axes")\np.set_secondary_axes(y2=True)\np.set_axis_scale(x="linear", y="log")\np.set_axis_label("x1", "time")\np.set_axis_format("y1", "%.2e")\np.set_axis_ticks("x1", ticks, labels=labels)\np.line("primary", y, x=x)\np.line("secondary", y2, x=x, y_axis="y2")\np' ,
129138 code : 'plot.setSecondaryAxes({ y2: true });\nplot.setAxisScale({ x: "linear", y: "log" });\nplot.setAxisTicks("x1", ticks, { labels });' ,
130139 } ,
131140 {
132141 id : "subplots-plot" ,
133142 section : "Layout" ,
134143 title : "Linked Subplots + Crosshair" ,
135144 text : "A 2x2 ImPlot subplot grid with linked x-axis behavior and crosshair synchronization." ,
145+ pythonCode : 'sp = ip.Subplots(2, 2, link_all_x=True, width=1100, height=650, title="Linked")\nsp.set_linked_crosshair("desk", axis="x")\nsp.subplot(0, 0).line("sin", y0, x=x)\nsp.subplot(0, 1).line("cos", y1, x=x)\nsp.subplot(1, 0).scatter("noise", y2, x=x)\nsp' ,
136146 code : 'plot.setSubplots(2, 2, { linkAllX: true });\nplot.setLinkedCrosshair("desk", { axis: "x" });\nplot.line("a", y, { x, subplotIndex: 0 });' ,
137147 } ,
138148 {
139149 id : "drag-plot" ,
140150 section : "Interaction" ,
141151 title : "Drag Lines, Drag Point, Drag Rect, Drag/Drop Targets" ,
142152 text : "Interactive ImPlot primitives for draggable guides, anchors, rectangles, and drop targets." ,
153+ pythonCode : 'p = ip.Plot(width=1100, height=420, title="Drag Tools")\np.drag_line_x("cursor", 40)\np.drag_line_y("threshold", 0.5)\np.drag_point("anchor", 25, 0.5)\np.drag_rect("roi", 10, -1, 20, 1)\np.on_tool_change(lambda plot, event: print(event))\np' ,
143154 code : 'plot.dragLineX("cursor", 40);\nplot.dragPoint("anchor", 25, 0.5);\nplot.onInteraction(events => ...);' ,
144155 } ,
145156 {
146157 id : "colormap-plot" ,
147158 section : "Colormaps" ,
148159 title : "Colormap Widgets + Runtime Switching" ,
149160 text : "Selector, slider, and color button widgets that update heatmaps and colorbar primitives at runtime." ,
161+ pythonCode : 'p = ip.Plot(width=1000, height=420, title="Colormaps")\np.set_colormap("Plasma")\np.heatmap("z", matrix, label_fmt="", show_colorbar=True)\np.colormap_selector(label="Choose map")\np.colormap_slider(label="Sample")\np.colormap_button(label="Active")\np' ,
150162 code : 'plot.setColormap("Plasma");\nplot.colormapSelector({ label: "Choose map" });\nplot.colormapSlider({ label: "Sample" });' ,
151163 } ,
152164 {
153165 id : "advanced-api-plot" ,
154166 section : "Advanced API" ,
155167 title : "State, Selection, Export" ,
156168 text : "View callbacks, state snapshots, PNG export, selection CSV, highlighting, constraints, links, and direct primitive access." ,
169+ pythonCode : 'p = ip.Plot(width=1100, height=420, title="Advanced")\np.set_theme("nbimplot")\nh = p.line("signal", y, x=x)\nstate = p.get_state(include_data=True)\np.highlight_selection(selection, h)\ncsv = p.export_csv_selection(selection, h)\np.export_png("advanced.png")\np' ,
157170 code : 'plot.setTheme("nbimplot");\nconst state = plot.getState({ includeData: true });\nplot.highlightSelection(selection, h);\nconst csv = plot.exportCSVSelection(selection, h);\nawait plot.downloadPNG("advanced.png");' ,
158171 } ,
159172] ;
@@ -170,6 +183,7 @@ export default function Page() {
170183 </ a >
171184 < div className = "topbar-links" >
172185 < a href = "#why" > Architecture</ a >
186+ < a href = "#developer-api" > Python API</ a >
173187 < a href = "#command-center" > Demo Controls</ a >
174188 < a href = "#examples" > Examples</ a >
175189 < a href = "https://pypi.org/project/nbimplot/" > PyPI</ a >
@@ -374,6 +388,7 @@ export default function Page() {
374388 < p >
375389 Each canvas is an independent WASM session. Scroll to lazy-load examples and
376390 verify lifecycle cleanup, interactions, subplots, colormaps, and exports.
391+ Every card shows the equivalent Python notebook API and direct web API.
377392 </ p >
378393 </ div >
379394 < div className = "example-tabs" aria-label = "Example categories" >
@@ -393,7 +408,22 @@ export default function Page() {
393408 </ div >
394409 < h2 > { example . title } </ h2 >
395410 < p > { example . text } </ p >
396- < pre > < code > { example . code } </ code > </ pre >
411+ < div className = "code-duo" aria-label = { `${ example . title } Python and web examples` } >
412+ < section className = "code-pane" >
413+ < div className = "code-pane-top" >
414+ < span > Python notebook</ span >
415+ < strong > nbimplot</ strong >
416+ </ div >
417+ < pre > < code > { example . pythonCode } </ code > </ pre >
418+ </ section >
419+ < section className = "code-pane code-pane-web" >
420+ < div className = "code-pane-top" >
421+ < span > Web app</ span >
422+ < strong > @nbimplot/web</ strong >
423+ </ div >
424+ < pre > < code > { example . code } </ code > </ pre >
425+ </ section >
426+ </ div >
397427 </ div >
398428 < div className = "plot-frame" >
399429 < div className = "plot-frame-top" >
0 commit comments